diff --git a/.eslintrc.js b/.eslintrc.js index 62743f2c..5c9295fb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,86 +1,16 @@ module.exports = { - 'extends': [ - 'airbnb', - 'prettier' - ], - 'parser': '@typescript-eslint/parser', - 'parserOptions': { - 'ecmaVersion': 2018, - 'sourceType': 'module', - 'modules': true - }, - 'plugins': [ - '@typescript-eslint' - ], - 'settings': { - 'import/resolver': { - 'typescript': { - } - } - }, - 'rules': { - 'quotes': [ - 2, - 'single', - { - 'allowTemplateLiterals': true - } - ], - 'class-methods-use-this': 0, - 'consistent-return': 0, - 'func-names': 0, - 'global-require': 0, - 'guard-for-in': 0, - 'import/no-duplicates': 0, - 'import/no-dynamic-require': 0, - 'import/no-extraneous-dependencies': 0, - 'import/prefer-default-export': 0, - 'lines-between-class-members': 0, - 'no-await-in-loop': 0, - 'no-bitwise': 0, - 'no-console': 0, - 'no-continue': 0, - 'no-control-regex': 0, - 'no-empty': 0, - 'no-loop-func': 0, - 'no-nested-ternary': 0, - 'no-param-reassign': 0, - 'no-plusplus': 0, - 'no-restricted-globals': 0, - 'no-restricted-syntax': 0, - 'no-shadow': 0, - 'no-underscore-dangle': 0, - 'no-use-before-define': 0, - 'prefer-const': 0, - 'prefer-destructuring': 0, - 'camelcase': 0, - 'no-unused-vars': 0, // in favor of '@typescript-eslint/no-unused-vars' - // 'indent': 0 // in favor of '@typescript-eslint/indent' - '@typescript-eslint/no-unused-vars': 'warn', - // '@typescript-eslint/indent': ['error', 2] // this might conflict with a lot ongoing changes - '@typescript-eslint/no-array-constructor': 'error', - '@typescript-eslint/adjacent-overload-signatures': 'error', - '@typescript-eslint/class-name-casing': 'error', - '@typescript-eslint/interface-name-prefix': 'error', - '@typescript-eslint/no-empty-interface': 'error', - '@typescript-eslint/no-inferrable-types': 'error', - '@typescript-eslint/no-misused-new': 'error', - '@typescript-eslint/no-namespace': 'error', - '@typescript-eslint/no-non-null-assertion': 'error', - '@typescript-eslint/no-parameter-properties': 'error', - '@typescript-eslint/no-triple-slash-reference': 'error', - '@typescript-eslint/prefer-namespace-keyword': 'error', - '@typescript-eslint/type-annotation-spacing': 'error', - // '@typescript-eslint/array-type': 'error', - // '@typescript-eslint/ban-types': 'error', - // '@typescript-eslint/explicit-function-return-type': 'warn', - // '@typescript-eslint/explicit-member-accessibility': 'error', - // '@typescript-eslint/member-delimiter-style': 'error', - // '@typescript-eslint/no-angle-bracket-type-assertion': 'error', - // '@typescript-eslint/no-explicit-any': 'warn', - // '@typescript-eslint/no-object-literal-type-assertion': 'error', - // '@typescript-eslint/no-use-before-define': 'error', - // '@typescript-eslint/no-var-requires': 'error', - // '@typescript-eslint/prefer-interface': 'error' - } -} + root: true, + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint'], + env: { + node: true, + }, + // This tells ESLint to load the config from the package `eslint-config-custom` + extends: [ + 'turbo', + 'prettier', + 'eslint:recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + ], +}; diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4130b969..548da44d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,12 +1,6 @@ name: Node CI -on: - push: - branches: - - master - tags: - - '!*' - pull_request: +on: [push] jobs: build: @@ -15,32 +9,39 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - node-version: [6.x, 8.x, 10.x, 12.x, 14.x] + node-version: [14.x, 16.x, 18.x, 20.x] + fail-fast: false runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v1 - + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + with: + version: 7.32.2 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + cache: 'pnpm' + - run: pnpm install --frozen-lockfile + - run: pnpm build + - run: pnpm test - - name: Print Node.js Version - run: node --version - - - name: Install Dependencies - run: npm install - env: - CI: true + lint: + name: Lint - - name: Run "build" step - run: npm run build --if-present - env: - CI: true + runs-on: ubuntu-latest - - name: Run tests - run: npm test - env: - CI: true + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + with: + version: 7.32.2 + - name: Use Node.js 20.x + uses: actions/setup-node@v3 + with: + node-version: 20.x + cache: 'pnpm' + - run: pnpm install --frozen-lockfile + - run: pnpm lint diff --git a/.gitignore b/.gitignore index e1d74da5..831fe876 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,26 @@ -/node_modules -/yarn.lock -/?.?s -/dist +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +?.?s +dist + +# dependencies +node_modules +.pnp +.pnp.js + +# testing +coverage + +# misc +.DS_Store + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# turbo +.turbo + +# vercel +.vercel diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 4d6db5e6..00000000 --- a/.npmignore +++ /dev/null @@ -1,3 +0,0 @@ -History.md -test -.travis.yml diff --git a/package.json b/package.json index fb2aba1b..757dfca5 100644 --- a/package.json +++ b/package.json @@ -1,56 +1,19 @@ { - "name": "https-proxy-agent", - "version": "5.0.1", - "description": "An HTTP(s) proxy `http.Agent` implementation for HTTPS", - "main": "dist/index", - "types": "dist/index", - "files": [ - "dist" - ], + "private": true, + "name": "proxy-agent-monorepo", "scripts": { - "prebuild": "rimraf dist", - "build": "tsc", - "test": "mocha --reporter spec", - "test-lint": "eslint src --ext .js,.ts", - "prepublishOnly": "npm run build" - }, - "repository": { - "type": "git", - "url": "git://github.com/TooTallNate/node-https-proxy-agent.git" - }, - "keywords": [ - "https", - "proxy", - "endpoint", - "agent" - ], - "author": "Nathan Rajlich (http://n8.io/)", - "license": "MIT", - "bugs": { - "url": "https://github.com/TooTallNate/node-https-proxy-agent/issues" - }, - "dependencies": { - "agent-base": "6", - "debug": "4" + "build": "turbo run build", + "lint": "turbo run lint", + "test": "turbo run test", + "format": "prettier --write \"**/*.{ts,tsx,md}\"" }, "devDependencies": { - "@types/debug": "4", - "@types/node": "^12.12.11", - "@typescript-eslint/eslint-plugin": "1.6.0", - "@typescript-eslint/parser": "1.1.0", - "eslint": "5.16.0", - "eslint-config-airbnb": "17.1.0", - "eslint-config-prettier": "4.1.0", - "eslint-import-resolver-typescript": "1.1.1", - "eslint-plugin-import": "2.16.0", - "eslint-plugin-jsx-a11y": "6.2.1", - "eslint-plugin-react": "7.12.4", - "mocha": "^6.2.2", - "proxy": "1", - "rimraf": "^3.0.0", - "typescript": "^3.5.3" - }, - "engines": { - "node": ">= 6" + "@typescript-eslint/eslint-plugin": "^5.59.1", + "@typescript-eslint/parser": "^5.59.1", + "eslint": "^7.32.0", + "eslint-config-prettier": "^8.8.0", + "eslint-config-turbo": "^1.9.3", + "prettier": "^2.5.1", + "turbo": "latest" } } diff --git a/packages/agent-base/README.md b/packages/agent-base/README.md new file mode 100644 index 00000000..35907269 --- /dev/null +++ b/packages/agent-base/README.md @@ -0,0 +1,140 @@ +agent-base +========== +### Turn a function into an [`http.Agent`][http.Agent] instance +[![Build Status](https://github.com/TooTallNate/node-agent-base/workflows/Node%20CI/badge.svg)](https://github.com/TooTallNate/node-agent-base/actions?workflow=Node+CI) + +This module provides an `http.Agent` generator. That is, you pass it an async +callback function, and it returns a new `http.Agent` instance that will invoke the +given callback function when sending outbound HTTP requests. + +#### Some subclasses: + +Here's some more interesting uses of `agent-base`. +Send a pull request to list yours! + + * [`http-proxy-agent`][http-proxy-agent]: An HTTP(s) proxy `http.Agent` implementation for HTTP endpoints + * [`https-proxy-agent`][https-proxy-agent]: An HTTP(s) proxy `http.Agent` implementation for HTTPS endpoints + * [`pac-proxy-agent`][pac-proxy-agent]: A PAC file proxy `http.Agent` implementation for HTTP and HTTPS + * [`socks-proxy-agent`][socks-proxy-agent]: A SOCKS proxy `http.Agent` implementation for HTTP and HTTPS + + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install agent-base +``` + + +Example +------- + +Here's a minimal example that creates a new `net.Socket` connection to the server +for every HTTP request (i.e. the equivalent of `agent: false` option): + +```ts +import * as net from 'net'; +import * as tls from 'tls'; +import * as http from 'http'; +import { Agent } from 'agent-base'; + +const agent = new Agent(function (req, opts) { + var socket; + // `secureEndpoint` is true when using the "https" module + if (opts.secureEndpoint) { + socket = tls.connect(opts); + } else { + socket = net.connect(opts); + } + return socket; +}); + +// Pass the `agent` option when creating the HTTP request +http.get('http://nodejs.org/api/', { agent }, (res) => { + console.log('"response" event!', res.headers); + res.pipe(process.stdout); +}); +``` + +Returning a Promise or using an `async` function is also supported: + +```ts +new Agent(async (req, opts) => { + await sleep(1000); + // etc… +}); +``` + +Return another `http.Agent` instance to "pass through" the responsibility +for that HTTP request to that agent: + +```ts +new Agent((req, opts) => { + return opts.secureEndpoint ? https.globalAgent : http.globalAgent; +}); +``` + + +API +--- + +## Agent(Function callback[, Object options]) → [http.Agent][] + +Creates a base `http.Agent` that will execute the callback function `callback` +for every HTTP request that it is used as the `agent` for. The callback function +is responsible for creating a `stream.Duplex` instance of some kind that will be +used as the underlying socket in the HTTP request. + +The `options` object accepts the following properties: + + * `timeout` - Number - Timeout for the `callback()` function in milliseconds. Defaults to Infinity (optional). + +The callback function should have the following signature: + +### callback(http.ClientRequest req, Object options, Function cb) → undefined + +The ClientRequest `req` can be accessed to read request headers and +and the path, etc. The `options` object contains the options passed +to the `http.request()`/`https.request()` function call, and is formatted +to be directly passed to `net.connect()`/`tls.connect()`, or however +else you want a Socket to be created. Pass the created socket to +the callback function `cb` once created, and the HTTP request will +continue to proceed. + +If the `https` module is used to invoke the HTTP request, then the +`secureEndpoint` property on `options` _will be set to `true`_. + + +License +------- + +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +[http-proxy-agent]: https://github.com/TooTallNate/node-http-proxy-agent +[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent +[pac-proxy-agent]: https://github.com/TooTallNate/node-pac-proxy-agent +[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent +[http.Agent]: https://nodejs.org/api/http.html#http_class_http_agent diff --git a/packages/agent-base/jest.config.js b/packages/agent-base/jest.config.js new file mode 100644 index 00000000..ee66e76e --- /dev/null +++ b/packages/agent-base/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/packages/agent-base/package.json b/packages/agent-base/package.json new file mode 100644 index 00000000..67d77202 --- /dev/null +++ b/packages/agent-base/package.json @@ -0,0 +1,51 @@ +{ + "name": "agent-base", + "version": "6.0.2", + "description": "Turn a function into an `http.Agent` instance", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "test": "jest --env node --verbose --bail", + "lint": "eslint . --ext .ts", + "prepublishOnly": "npm run build" + }, + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/node-agent-base.git" + }, + "keywords": [ + "http", + "agent", + "base", + "barebones", + "https" + ], + "author": "Nathan Rajlich (http://n8.io/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/TooTallNate/node-agent-base/issues" + }, + "dependencies": { + "debug": "^4.3.4" + }, + "devDependencies": { + "@types/debug": "^4.1.7", + "@types/jest": "^29.5.1", + "@types/node": "^14.18.43", + "@types/semver": "^7.3.13", + "@types/ws": "^6.0.4", + "async-listen": "^2.1.0", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "tsconfig": "workspace:*", + "typescript": "^5.0.4", + "ws": "^3.3.3" + }, + "engines": { + "node": ">= 14" + } +} diff --git a/packages/agent-base/src/helpers.ts b/packages/agent-base/src/helpers.ts new file mode 100644 index 00000000..897371d4 --- /dev/null +++ b/packages/agent-base/src/helpers.ts @@ -0,0 +1,31 @@ +import * as http from "http"; +import * as https from "https"; +import type { Readable } from "stream"; + +export async function toBuffer(stream: Readable): Promise { + let length = 0; + const chunks: Buffer[] = []; + for await (const chunk of stream) { + length += chunk.length; + chunks.push(chunk); + } + return Buffer.concat(chunks, length); +} + +export async function json(stream: Readable): Promise> { + const buf = await toBuffer(stream); + return JSON.parse(buf.toString('utf8')); +} + +export function req( + url: string | URL, + opts: https.RequestOptions +): Promise { + return new Promise((resolve, reject) => { + const href = typeof url === 'string' ? url : url.href; + (href.startsWith("https:") ? https : http) + .request(url, opts, resolve) + .once("error", reject) + .end(); + }); +} \ No newline at end of file diff --git a/packages/agent-base/src/index.ts b/packages/agent-base/src/index.ts new file mode 100644 index 00000000..ec80223f --- /dev/null +++ b/packages/agent-base/src/index.ts @@ -0,0 +1,99 @@ +import * as net from 'net'; +import * as tls from 'tls'; +import * as http from 'http'; +import { Duplex } from 'stream'; + +export * from './helpers'; + +function isSecureEndpoint(): boolean { + const { stack } = new Error(); + if (typeof stack !== 'string') return false; + return stack + .split('\n') + .some( + (l) => + l.indexOf('(https.js:') !== -1 || + l.indexOf('node:https:') !== -1 + ); +} + +interface HttpConnectOpts extends net.TcpNetConnectOpts { + secureEndpoint: false; +} + +interface HttpsConnectOpts extends tls.ConnectionOptions { + port: number; + secureEndpoint: true; +} + +export type AgentConnectOpts = HttpConnectOpts | HttpsConnectOpts; + +export abstract class Agent extends http.Agent { + _defaultPort?: number; + _protocol?: string; + _currentSocket?: Duplex; + + constructor() { + super(); + this._defaultPort = undefined; + this._protocol = undefined; + } + + abstract connect( + req: http.ClientRequest, + options: AgentConnectOpts + ): Promise | Duplex | http.Agent; + + createSocket( + req: http.ClientRequest, + options: AgentConnectOpts, + cb: (err: Error | null, s?: Duplex) => void + ) { + const o = { + ...options, + secureEndpoint: options.secureEndpoint ?? isSecureEndpoint(), + }; + Promise.resolve() + .then(() => this.connect(req, o)) + .then((socket) => { + if (socket instanceof http.Agent) { + // @ts-expect-error `createSocket()` isn't defined in `@types/node` + return socket.createSocket(req, o, cb); + } + this._currentSocket = socket; + // @ts-expect-error `createSocket()` isn't defined in `@types/node` + super.createSocket(req, options, cb); + }, cb); + } + + createConnection(): Duplex { + if (!this._currentSocket) { + throw new Error('no socket'); + } + return this._currentSocket; + } + + get defaultPort(): number { + if (typeof this._defaultPort === 'number') { + return this._defaultPort; + } + const port = isSecureEndpoint() ? 443 : 80; + return port; + } + + set defaultPort(v: number) { + this._defaultPort = v; + } + + get protocol(): string { + if (typeof this._protocol === 'string') { + return this._protocol; + } + const p = isSecureEndpoint() ? 'https:' : 'http:'; + return p; + } + + set protocol(v: string) { + this._protocol = v; + } +} diff --git a/test/ssl-cert-snakeoil.key b/packages/agent-base/test/ssl-cert-snakeoil.key similarity index 100% rename from test/ssl-cert-snakeoil.key rename to packages/agent-base/test/ssl-cert-snakeoil.key diff --git a/test/ssl-cert-snakeoil.pem b/packages/agent-base/test/ssl-cert-snakeoil.pem similarity index 100% rename from test/ssl-cert-snakeoil.pem rename to packages/agent-base/test/ssl-cert-snakeoil.pem diff --git a/packages/agent-base/test/test-legacy.js b/packages/agent-base/test/test-legacy.js new file mode 100644 index 00000000..d5f8f856 --- /dev/null +++ b/packages/agent-base/test/test-legacy.js @@ -0,0 +1,689 @@ +/** + * Module dependencies. + */ + +var fs = require('fs'); +var url = require('url'); +var net = require('net'); +var tls = require('tls'); +var http = require('http'); +var https = require('https'); +var WebSocket = require('ws'); +var assert = require('assert'); +var events = require('events'); +var inherits = require('util').inherits; +var Agent = require('../src'); + +var PassthroughAgent = Agent(function (req, opts) { + return opts.secureEndpoint ? https.globalAgent : http.globalAgent; +}); + +describe('Agent (JavaScript)', function () { + describe('subclass', function () { + it('should be subclassable', function (done) { + function MyAgent() { + Agent.call(this); + } + inherits(MyAgent, Agent); + + MyAgent.prototype.callback = function (req, opts, fn) { + assert.equal(req.path, '/foo'); + assert.equal(req.getHeader('host'), '127.0.0.1:1234'); + assert.equal(opts.secureEndpoint, true); + done(); + }; + + var info = url.parse('https://127.0.0.1:1234/foo'); + info.agent = new MyAgent(); + assert(info.agent instanceof Agent); + assert(info.agent instanceof MyAgent); + https.get(info); + }); + }); + describe('options', function () { + it('should support an options Object as first argument', function () { + var agent = new Agent({ timeout: 1000 }); + assert.equal(1000, agent.timeout); + }); + it('should support an options Object as second argument', function () { + var agent = new Agent(function () {}, { timeout: 1000 }); + assert.equal(1000, agent.timeout); + }); + }); + describe('`this` context', function () { + it('should be the Agent instance', function (done) { + var called = false; + var agent = new Agent(); + agent.callback = function () { + called = true; + assert.equal(this, agent); + }; + var info = url.parse('http://127.0.0.1/foo'); + info.agent = agent; + var req = http.get(info); + req.on('error', function (err) { + assert(/no Duplex stream was returned/.test(err.message)); + done(); + }); + }); + it('should be the Agent instance with callback signature', function (done) { + var called = false; + var agent = new Agent(); + agent.callback = function (req, opts, fn) { + called = true; + assert.equal(this, agent); + fn(); + }; + var info = url.parse('http://127.0.0.1/foo'); + info.agent = agent; + var req = http.get(info); + req.on('error', function (err) { + assert(/no Duplex stream was returned/.test(err.message)); + done(); + }); + }); + }); + describe('"error" event', function () { + it('should be invoked on `http.ClientRequest` instance if `callback()` has not been defined', function (done) { + var agent = new Agent(); + var info = url.parse('http://127.0.0.1/foo'); + info.agent = agent; + var req = http.get(info); + req.on('error', function (err) { + assert.equal( + '"agent-base" has no default implementation, you must subclass and override `callback()`', + err.message + ); + done(); + }); + }); + it('should be invoked on `http.ClientRequest` instance if Error passed to callback function on the first tick', function (done) { + var agent = new Agent(function (req, opts, fn) { + fn(new Error('is this caught?')); + }); + var info = url.parse('http://127.0.0.1/foo'); + info.agent = agent; + var req = http.get(info); + req.on('error', function (err) { + assert.equal('is this caught?', err.message); + done(); + }); + }); + it('should be invoked on `http.ClientRequest` instance if Error passed to callback function after the first tick', function (done) { + var agent = new Agent(function (req, opts, fn) { + setTimeout(function () { + fn(new Error('is this caught?')); + }, 10); + }); + var info = url.parse('http://127.0.0.1/foo'); + info.agent = agent; + var req = http.get(info); + req.on('error', function (err) { + assert.equal('is this caught?', err.message); + done(); + }); + }); + }); + describe('artificial "streams"', function () { + it('should send a GET request', function (done) { + var stream = new events.EventEmitter(); + + // needed for the `http` module to call .write() on the stream + stream.writable = true; + + stream.write = function (str) { + assert(0 == str.indexOf('GET / HTTP/1.1')); + done(); + }; + + // needed for `http` module in Node.js 4 + stream.cork = function () {}; + stream.uncork = function () {}; + + var opts = { + method: 'GET', + host: '127.0.0.1', + path: '/', + port: 80, + agent: new Agent(function (req, opts, fn) { + fn(null, stream); + }), + }; + var req = http.request(opts); + req.end(); + }); + it('should receive a GET response', function (done) { + var stream = new events.EventEmitter(); + var opts = { + method: 'GET', + host: '127.0.0.1', + path: '/', + port: 80, + agent: new Agent(function (req, opts, fn) { + fn(null, stream); + }), + }; + var req = http.request(opts, function (res) { + assert.equal('1.0', res.httpVersion); + assert.equal(200, res.statusCode); + assert.equal('bar', res.headers.foo); + assert.deepEqual(['1', '2'], res.headers['set-cookie']); + done(); + }); + + // have to wait for the "socket" event since `http.ClientRequest` + // doesn't *actually* attach the listeners to the "stream" until + // this happens + req.once('socket', function () { + var buf = Buffer.from( + 'HTTP/1.0 200\r\n' + + 'Foo: bar\r\n' + + 'Set-Cookie: 1\r\n' + + 'Set-Cookie: 2\r\n\r\n' + ); + stream.emit('data', buf); + }); + + req.end(); + }); + }); + + describe('"http" module', function () { + var server; + var port; + + // setup test HTTP server + before(function (done) { + server = http.createServer(); + server.listen(0, function () { + port = server.address().port; + done(); + }); + }); + + beforeEach(function () { + server.removeAllListeners('request'); + }); + + // shut down test HTTP server + after(function (done) { + server.once('close', function () { + done(); + }); + server.close(); + }); + + it('should work for basic HTTP requests', function (done) { + var called = false; + var agent = new Agent(function (req, opts, fn) { + called = true; + var socket = net.connect(opts); + fn(null, socket); + }); + + // add HTTP server "request" listener + var gotReq = false; + server.once('request', function (req, res) { + gotReq = true; + res.setHeader('X-Foo', 'bar'); + res.setHeader('X-Url', req.url); + res.end(); + }); + + var info = url.parse('http://127.0.0.1:' + port + '/foo'); + info.agent = agent; + http.get(info, function (res) { + assert.equal('bar', res.headers['x-foo']); + assert.equal('/foo', res.headers['x-url']); + assert(gotReq); + assert(called); + done(); + }); + }); + + it('should support direct return in `connect()`', function (done) { + var called = false; + var agent = new Agent(function (req, opts) { + called = true; + return net.connect(opts); + }); + + // add HTTP server "request" listener + var gotReq = false; + server.once('request', function (req, res) { + gotReq = true; + res.setHeader('X-Foo', 'bar'); + res.setHeader('X-Url', req.url); + res.end(); + }); + + var info = url.parse('http://127.0.0.1:' + port + '/foo'); + info.agent = agent; + http.get(info, function (res) { + assert.equal('bar', res.headers['x-foo']); + assert.equal('/foo', res.headers['x-url']); + assert(gotReq); + assert(called); + done(); + }); + }); + + it('should support returning a Promise in `connect()`', function (done) { + var called = false; + var agent = new Agent(function (req, opts) { + return new Promise(function (resolve, reject) { + called = true; + resolve(net.connect(opts)); + }); + }); + + // add HTTP server "request" listener + var gotReq = false; + server.once('request', function (req, res) { + gotReq = true; + res.setHeader('X-Foo', 'bar'); + res.setHeader('X-Url', req.url); + res.end(); + }); + + var info = url.parse('http://127.0.0.1:' + port + '/foo'); + info.agent = agent; + http.get(info, function (res) { + assert.equal('bar', res.headers['x-foo']); + assert.equal('/foo', res.headers['x-url']); + assert(gotReq); + assert(called); + done(); + }); + }); + + it('should set the `Connection: close` response header', function (done) { + var called = false; + var agent = new Agent(function (req, opts, fn) { + called = true; + var socket = net.connect(opts); + fn(null, socket); + }); + + // add HTTP server "request" listener + var gotReq = false; + server.once('request', function (req, res) { + gotReq = true; + res.setHeader('X-Url', req.url); + assert.equal('close', req.headers.connection); + res.end(); + }); + + var info = url.parse('http://127.0.0.1:' + port + '/bar'); + info.agent = agent; + http.get(info, function (res) { + assert.equal('/bar', res.headers['x-url']); + assert.equal('close', res.headers.connection); + assert(gotReq); + assert(called); + done(); + }); + }); + + it('should pass through options from `http.request()`', function (done) { + var agent = new Agent(function (req, opts, fn) { + assert.equal('google.com', opts.host); + assert.equal('bar', opts.foo); + done(); + }); + + http.get({ + host: 'google.com', + foo: 'bar', + agent: agent, + }); + }); + + it('should default to port 80', function (done) { + var agent = new Agent(function (req, opts, fn) { + assert.equal(80, opts.port); + done(); + }); + + // (probably) not hitting a real HTTP server here, + // so no need to add a httpServer request listener + http.get({ + host: '127.0.0.1', + path: '/foo', + agent: agent, + }); + }); + + it('should support the "timeout" option', function (done) { + // ensure we timeout after the "error" event had a chance to trigger + this.timeout(1000); + this.slow(800); + + var agent = new Agent( + function (req, opts, fn) { + // this function will time out + }, + { timeout: 100 } + ); + + var opts = url.parse('http://nodejs.org'); + opts.agent = agent; + + var req = http.get(opts); + req.once('error', function (err) { + assert.equal('ETIMEOUT', err.code); + req.abort(); + done(); + }); + }); + + it('should free sockets after use', function (done) { + var agent = new Agent(function (req, opts, fn) { + var socket = net.connect(opts); + fn(null, socket); + }); + + // add HTTP server "request" listener + var gotReq = false; + server.once('request', function (req, res) { + gotReq = true; + res.end(); + }); + + var info = url.parse('http://127.0.0.1:' + port + '/foo'); + info.agent = agent; + http.get(info, function (res) { + res.socket.emit('free'); + assert.equal(true, res.socket.destroyed); + assert(gotReq); + done(); + }); + }); + + describe('PassthroughAgent', function () { + it('should pass through to `http.globalAgent`', function (done) { + // add HTTP server "request" listener + var gotReq = false; + server.once('request', function (req, res) { + gotReq = true; + res.setHeader('X-Foo', 'bar'); + res.setHeader('X-Url', req.url); + res.end(); + }); + + var info = url.parse('http://127.0.0.1:' + port + '/foo'); + info.agent = PassthroughAgent; + http.get(info, function (res) { + assert.equal('bar', res.headers['x-foo']); + assert.equal('/foo', res.headers['x-url']); + assert(gotReq); + done(); + }); + }); + }); + }); + + describe('"https" module', function () { + var server; + var port; + + // setup test HTTPS server + before(function (done) { + var options = { + key: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.key'), + cert: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.pem'), + }; + server = https.createServer(options); + server.listen(0, function () { + port = server.address().port; + done(); + }); + }); + + beforeEach(function () { + server.removeAllListeners('request'); + }); + + // shut down test HTTP server + after(function (done) { + server.once('close', function () { + done(); + }); + server.close(); + }); + + it('should not modify the passed in Options object', function (done) { + var called = false; + var agent = new Agent(function (req, opts, fn) { + called = true; + assert.equal(true, opts.secureEndpoint); + assert.equal(443, opts.port); + assert.equal('localhost', opts.host); + }); + var opts = { agent: agent }; + var req = https.request(opts); + assert.equal(true, called); + assert.equal(false, 'secureEndpoint' in opts); + assert.equal(false, 'port' in opts); + done(); + }); + + it('should work with a String URL', function (done) { + var endpoint = 'https://127.0.0.1:' + port; + var req = https.get(endpoint); + + // it's gonna error out since `rejectUnauthorized` is not being passed in + req.on('error', function (err) { + assert.equal(err.code, 'DEPTH_ZERO_SELF_SIGNED_CERT'); + done(); + }); + }); + + it('should work for basic HTTPS requests', function (done) { + var called = false; + var agent = new Agent(function (req, opts, fn) { + called = true; + assert(opts.secureEndpoint); + var socket = tls.connect(opts); + fn(null, socket); + }); + + // add HTTPS server "request" listener + var gotReq = false; + server.once('request', function (req, res) { + gotReq = true; + res.setHeader('X-Foo', 'bar'); + res.setHeader('X-Url', req.url); + res.end(); + }); + + var info = url.parse('https://127.0.0.1:' + port + '/foo'); + info.agent = agent; + info.rejectUnauthorized = false; + https.get(info, function (res) { + assert.equal('bar', res.headers['x-foo']); + assert.equal('/foo', res.headers['x-url']); + assert(gotReq); + assert(called); + done(); + }); + }); + + it('should pass through options from `https.request()`', function (done) { + var agent = new Agent(function (req, opts, fn) { + assert.equal('google.com', opts.host); + assert.equal('bar', opts.foo); + done(); + }); + + https.get({ + host: 'google.com', + foo: 'bar', + agent: agent, + }); + }); + + it('should default to port 443', function (done) { + var agent = new Agent(function (req, opts, fn) { + assert.equal(true, opts.secureEndpoint); + assert.equal(false, opts.rejectUnauthorized); + assert.equal(443, opts.port); + done(); + }); + + // (probably) not hitting a real HTTPS server here, + // so no need to add a httpsServer request listener + https.get({ + host: '127.0.0.1', + path: '/foo', + agent: agent, + rejectUnauthorized: false, + }); + }); + + describe('PassthroughAgent', function () { + it('should pass through to `https.globalAgent`', function (done) { + // add HTTP server "request" listener + var gotReq = false; + server.once('request', function (req, res) { + gotReq = true; + res.setHeader('X-Foo', 'bar'); + res.setHeader('X-Url', req.url); + res.end(); + }); + + var info = url.parse('https://127.0.0.1:' + port + '/foo'); + info.agent = PassthroughAgent; + info.rejectUnauthorized = false; + https.get(info, function (res) { + assert.equal('bar', res.headers['x-foo']); + assert.equal('/foo', res.headers['x-url']); + assert(gotReq); + done(); + }); + }); + }); + }); + + describe('"ws" server', function () { + var wss; + var server; + var port; + + // setup test HTTP server + before(function (done) { + server = http.createServer(); + wss = new WebSocket.Server({ server: server }); + server.listen(0, function () { + port = server.address().port; + done(); + }); + }); + + beforeEach(function () { + server.removeAllListeners('request'); + wss.removeAllListeners('connection'); + }); + + // shut down test HTTP server + after(function (done) { + server.once('close', function () { + done(); + }); + server.close(); + }); + + it('should work for basic WebSocket connections', function (done) { + function onconnection(ws) { + ws.on('message', function (data) { + assert.equal('ping', data); + ws.send('pong'); + }); + } + wss.on('connection', onconnection); + + var agent = new Agent(function (req, opts, fn) { + var socket = net.connect(opts); + fn(null, socket); + }); + + var client = new WebSocket('ws://127.0.0.1:' + port + '/', { + agent: agent, + }); + + client.on('open', function () { + client.send('ping'); + }); + + client.on('message', function (data) { + assert.equal('pong', data); + client.close(); + done(); + }); + }); + }); + + describe('"wss" server', function () { + var wss; + var server; + var port; + + // setup test HTTP server + before(function (done) { + var options = { + key: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.key'), + cert: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.pem'), + }; + server = https.createServer(options); + wss = new WebSocket.Server({ server: server }); + server.listen(0, function () { + port = server.address().port; + done(); + }); + }); + + beforeEach(function () { + server.removeAllListeners('request'); + wss.removeAllListeners('connection'); + }); + + // shut down test HTTP server + after(function (done) { + server.once('close', function () { + done(); + }); + server.close(); + }); + + it('should work for secure WebSocket connections', function (done) { + function onconnection(ws) { + ws.on('message', function (data) { + assert.equal('ping', data); + ws.send('pong'); + }); + } + wss.on('connection', onconnection); + + var agent = new Agent(function (req, opts, fn) { + var socket = tls.connect(opts); + fn(null, socket); + }); + + var client = new WebSocket('wss://127.0.0.1:' + port + '/', { + agent: agent, + rejectUnauthorized: false, + }); + + client.on('open', function () { + client.send('ping'); + }); + + client.on('message', function (data) { + assert.equal('pong', data); + client.close(); + wss.removeListener('connection', onconnection); + done(); + }); + }); + }); +}); diff --git a/packages/agent-base/test/test.ts b/packages/agent-base/test/test.ts new file mode 100644 index 00000000..10192243 --- /dev/null +++ b/packages/agent-base/test/test.ts @@ -0,0 +1,393 @@ +import * as fs from 'fs'; +import * as net from 'net'; +import * as tls from 'tls'; +import * as url from 'url'; +import * as http from 'http'; +import * as https from 'https'; +import assert from 'assert'; +import listen from 'async-listen'; +import { Agent, AgentConnectOpts } from '../src'; + +const sleep = (n: number) => new Promise((r) => setTimeout(r, n)); + +const req = (opts: https.RequestOptions): Promise => + new Promise((resolve, reject) => { + (opts.protocol === 'https:' ? https : http) + .request(opts, resolve) + .once('error', reject) + .end(); + }); + +const sslOptions = { + key: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.key`), + cert: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.pem`), +}; + +function json(res: http.IncomingMessage): Promise> { + return new Promise((resolve) => { + let data = ''; + res.setEncoding('utf8'); + res.on('data', (b) => { + data += b; + }); + res.on('end', () => resolve(JSON.parse(data))); + }); +} + +describe('Agent (TypeScript)', () => { + describe('subclass', () => { + it('should be extendable (direct return)', () => { + class MyAgent extends Agent { + connect() { + return http.globalAgent; + } + } + const agent = new MyAgent(); + assert(agent instanceof Agent); + assert(agent instanceof MyAgent); + }); + + it('should be extendable (async return)', () => { + class MyAgent extends Agent { + async connect() { + return http.globalAgent; + } + } + const agent = new MyAgent(); + assert(agent instanceof Agent); + assert(agent instanceof MyAgent); + }); + }); + + describe('"http" module', () => { + it('should work for basic HTTP requests', async () => { + let gotReq = false; + let gotCallback = false; + + class MyAgent extends Agent { + async connect( + _req: http.ClientRequest, + opts: AgentConnectOpts + ) { + gotCallback = true; + assert(opts.secureEndpoint === false); + return net.connect(opts); + } + } + const agent = new MyAgent(); + + const server = http.createServer((req, res) => { + gotReq = true; + res.setHeader('X-Foo', 'bar'); + res.setHeader('X-Url', req.url || '/'); + res.end(); + }); + await listen(server); + + const addr = server.address(); + if (!addr || typeof addr === 'string') { + throw new Error('Server did not bind to a port'); + } + const { port } = addr; + + try { + const info = url.parse(`http://127.0.0.1:${port}/foo`); + const res = await req({ agent, ...info }); + assert.equal('bar', res.headers['x-foo']); + assert.equal('/foo', res.headers['x-url']); + assert(gotReq); + assert(gotCallback); + } finally { + server.close(); + } + }); + + it('should not send a port number for the default port', async () => { + class MyAgent extends Agent { + connect( + _req: http.ClientRequest, + opts: AgentConnectOpts + ): net.Socket { + assert(opts.secureEndpoint === false); + assert.equal(this.defaultPort, port); + assert.equal(opts.port, port); + return net.connect(opts); + } + } + + const agent = new MyAgent(); + const server = http.createServer((req, res) => { + res.end(JSON.stringify(req.headers)); + }); + await listen(server); + + const addr = server.address(); + if (!addr || typeof addr === 'string') { + throw new Error('Server did not bind to a port'); + } + const { port } = addr; + + agent.defaultPort = port; + + try { + const info = url.parse(`http://127.0.0.1:${port}/foo`); + const res = await req({ agent, ...info }); + const body = await json(res); + assert.equal(body.host, '127.0.0.1'); + } finally { + server.close(); + } + }); + + it('should work after the first tick of the `http.ClientRequest` instance', async () => { + let gotReq = false; + let gotCallback = false; + + class MyAgent extends Agent { + async connect( + _req: http.ClientRequest, + opts: AgentConnectOpts + ): Promise { + gotCallback = true; + assert(opts.secureEndpoint === false); + await sleep(10); + return net.connect(opts); + } + } + + const agent = new MyAgent(); + + const server = http.createServer((req, res) => { + gotReq = true; + res.setHeader('X-Foo', 'bar'); + res.setHeader('X-Url', req.url || '/'); + res.end(); + }); + await listen(server); + + const addr = server.address(); + if (!addr || typeof addr === 'string') { + throw new Error('Server did not bind to a port'); + } + const { port } = addr; + + try { + const info = url.parse(`http://127.0.0.1:${port}/foo`); + const res = await req({ agent, ...info }); + assert.equal('bar', res.headers['x-foo']); + assert.equal('/foo', res.headers['x-url']); + assert(gotReq); + assert(gotCallback); + } finally { + server.close(); + } + }); + + it('should emit an "error" event on `http.ClientRequest` instance when callback throws sync', async () => { + let gotError = false; + let gotCallback = false; + + class MyAgent extends Agent { + connect(): net.Socket { + gotCallback = true; + throw new Error('bad'); + } + } + + const agent = new MyAgent(); + + try { + const info = url.parse('http://127.0.0.1/throws'); + await req({ agent, ...info }); + } catch (err: unknown) { + gotError = true; + assert.equal((err as Error).message, 'bad'); + } + + assert(gotError); + assert(gotCallback); + }); + + it('should emit an "error" event on `http.ClientRequest` instance when callback throws async', async () => { + let gotError = false; + let gotCallback = false; + + class MyAgent extends Agent { + async connect(): Promise { + gotCallback = true; + await sleep(10); + throw new Error('bad'); + } + } + + const agent = new MyAgent(); + + try { + const info = url.parse('http://127.0.0.1/throws'); + await req({ agent, ...info }); + } catch (err: unknown) { + gotError = true; + assert.equal((err as Error).message, 'bad'); + } + + assert(gotError); + assert(gotCallback); + }); + }); + + describe('"https" module', () => { + it('should work for basic HTTPS requests', async () => { + let gotReq = false; + let gotCallback = false; + + class MyAgent extends Agent { + connect( + _req: http.ClientRequest, + opts: AgentConnectOpts + ): net.Socket { + gotCallback = true; + assert(opts.secureEndpoint === true); + return tls.connect(opts); + } + } + + const agent = new MyAgent(); + + const server = https.createServer(sslOptions, (req, res) => { + gotReq = true; + res.setHeader('X-Foo', 'bar'); + res.setHeader('X-Url', req.url || '/'); + res.end(); + }); + await listen(server); + + const addr = server.address(); + if (!addr || typeof addr === 'string') { + throw new Error('Server did not bind to a port'); + } + const { port } = addr; + + try { + const info = url.parse(`https://127.0.0.1:${port}/foo`); + const res = await req({ + agent, + rejectUnauthorized: false, + ...info, + }); + assert.equal('bar', res.headers['x-foo']); + assert.equal('/foo', res.headers['x-url']); + assert(gotReq); + assert(gotCallback); + } finally { + server.close(); + } + }); + + it('should work when returning another `agent-base`', async () => { + let gotReq = false; + let gotCallback1 = false; + let gotCallback2 = false; + + class MyAgent1 extends Agent { + async connect( + _req: http.ClientRequest, + opts: AgentConnectOpts + ): Promise { + gotCallback1 = true; + assert.equal(opts.secureEndpoint, true); + return agent2; + } + } + + class MyAgent2 extends Agent { + async connect( + _req: http.ClientRequest, + opts: AgentConnectOpts + ): Promise { + gotCallback2 = true; + assert.equal(opts.secureEndpoint, true); + return tls.connect(opts); + } + } + + const agent1 = new MyAgent1(); + const agent2 = new MyAgent2(); + + const server = https.createServer(sslOptions, (req, res) => { + gotReq = true; + res.setHeader('X-Foo', 'bar'); + res.setHeader('X-Url', req.url || '/'); + res.end(); + }); + await listen(server); + + const addr = server.address(); + if (!addr || typeof addr === 'string') { + throw new Error('Server did not bind to a port'); + } + const { port } = addr; + + try { + const info = url.parse(`https://127.0.0.1:${port}/foo`); + const res = await req({ + agent: agent1, + rejectUnauthorized: false, + ...info, + }); + assert.equal('bar', res.headers['x-foo']); + assert.equal('/foo', res.headers['x-url']); + assert(gotReq); + assert(gotCallback1); + assert(gotCallback2); + } finally { + server.close(); + } + }); + + it('should not send a port number for the default port', async () => { + let reqCount = 0; + + class MyAgent extends Agent { + connect( + _req: http.ClientRequest, + opts: AgentConnectOpts + ): net.Socket { + assert(opts.secureEndpoint === true); + assert.equal(agent.defaultPort, port); + assert.equal(opts.port, port); + return tls.connect(opts); + } + } + + const agent = new MyAgent(); + + const server = https.createServer(sslOptions, (req, res) => { + reqCount++; + res.end(JSON.stringify(req.headers)); + }); + await listen(server); + + const addr = server.address(); + if (!addr || typeof addr === 'string') { + throw new Error('Server did not bind to a port'); + } + const { port } = addr; + + agent.defaultPort = port; + + try { + const info = url.parse(`https://127.0.0.1:${port}/foo`); + const res = await req({ + agent, + rejectUnauthorized: false, + ...info, + }); + const body = await json(res); + assert.equal(body.host, '127.0.0.1'); + assert.equal(reqCount, 1); + } finally { + server.close(); + } + }); + }); +}); diff --git a/packages/agent-base/test/tsconfig.json b/packages/agent-base/test/tsconfig.json new file mode 100644 index 00000000..a79e2e63 --- /dev/null +++ b/packages/agent-base/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["test.ts"] +} diff --git a/packages/agent-base/tsconfig.json b/packages/agent-base/tsconfig.json new file mode 100644 index 00000000..24b6cf7e --- /dev/null +++ b/packages/agent-base/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/data-uri-to-buffer/README.md b/packages/data-uri-to-buffer/README.md new file mode 100644 index 00000000..d0f2e059 --- /dev/null +++ b/packages/data-uri-to-buffer/README.md @@ -0,0 +1,88 @@ +data-uri-to-buffer +================== +### Generate a Buffer instance from a [Data URI][rfc] string +[![Build Status](https://travis-ci.org/TooTallNate/node-data-uri-to-buffer.svg?branch=master)](https://travis-ci.org/TooTallNate/node-data-uri-to-buffer) + +This module accepts a ["data" URI][rfc] String of data, and returns a +node.js `Buffer` instance with the decoded data. + + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install data-uri-to-buffer +``` + + +Example +------- + +``` js +import dataUriToBuffer from 'data-uri-to-buffer'; + +// plain-text data is supported +let uri = 'data:,Hello%2C%20World!'; +let decoded = dataUriToBuffer(uri); +console.log(decoded.toString()); +// 'Hello, World!' + +// base64-encoded data is supported +uri = 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D'; +decoded = dataUriToBuffer(uri); +console.log(decoded.toString()); +// 'Hello, World!' +``` + + +API +--- + +### dataUriToBuffer(String uri) → Buffer + +The `type` property on the Buffer instance gets set to the main type portion of +the "mediatype" portion of the "data" URI, or defaults to `"text/plain"` if not +specified. + +The `typeFull` property on the Buffer instance gets set to the entire +"mediatype" portion of the "data" URI (including all parameters), or defaults +to `"text/plain;charset=US-ASCII"` if not specified. + +The `charset` property on the Buffer instance gets set to the Charset portion of +the "mediatype" portion of the "data" URI, or defaults to `"US-ASCII"` if the +entire type is not specified, or defaults to `""` otherwise. + +*Note*: If the only the main type is specified but not the charset, e.g. +`"data:text/plain,abc"`, the charset is set to the empty string. The spec only +defaults to US-ASCII as charset if the entire type is not specified. + + +License +------- + +(The MIT License) + +Copyright (c) 2014 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +[rfc]: http://tools.ietf.org/html/rfc2397 diff --git a/packages/data-uri-to-buffer/jest.config.js b/packages/data-uri-to-buffer/jest.config.js new file mode 100644 index 00000000..07978f56 --- /dev/null +++ b/packages/data-uri-to-buffer/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/packages/data-uri-to-buffer/package.json b/packages/data-uri-to-buffer/package.json new file mode 100644 index 00000000..3c0f065a --- /dev/null +++ b/packages/data-uri-to-buffer/package.json @@ -0,0 +1,47 @@ +{ + "name": "data-uri-to-buffer", + "version": "4.0.1", + "description": "Generate a Buffer instance from a Data URI string", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "test": "jest --env node --verbose --bail", + "lint": "eslint . --ext .ts", + "prepublishOnly": "npm run build" + }, + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/node-data-uri-to-buffer.git" + }, + "engines": { + "node": ">= 14" + }, + "keywords": [ + "data", + "uri", + "datauri", + "data-uri", + "buffer", + "convert", + "rfc2397", + "2397" + ], + "author": "Nathan Rajlich (http://n8.io/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/TooTallNate/node-data-uri-to-buffer/issues" + }, + "homepage": "https://github.com/TooTallNate/node-data-uri-to-buffer", + "devDependencies": { + "@types/jest": "^27.0.2", + "@types/node": "^14.18.43", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "tsconfig": "workspace:*", + "typescript": "^5.0.4" + } +} diff --git a/packages/data-uri-to-buffer/src/index.ts b/packages/data-uri-to-buffer/src/index.ts new file mode 100644 index 00000000..a43096c9 --- /dev/null +++ b/packages/data-uri-to-buffer/src/index.ts @@ -0,0 +1,68 @@ +export interface MimeBuffer extends Buffer { + type: string; + typeFull: string; + charset: string; +} + +/** + * Returns a `Buffer` instance from the given data URI `uri`. + * + * @param {String} uri Data URI to turn into a Buffer instance + * @returns {Buffer} Buffer instance from Data URI + * @api public + */ +export function dataUriToBuffer(uri: string): MimeBuffer { + if (!/^data:/i.test(uri)) { + throw new TypeError( + '`uri` does not appear to be a Data URI (must begin with "data:")' + ); + } + + // strip newlines + uri = uri.replace(/\r?\n/g, ''); + + // split the URI up into the "metadata" and the "data" portions + const firstComma = uri.indexOf(','); + if (firstComma === -1 || firstComma <= 4) { + throw new TypeError('malformed data: URI'); + } + + // remove the "data:" scheme and parse the metadata + const meta = uri.substring(5, firstComma).split(';'); + + let charset = ''; + let base64 = false; + const type = meta[0] || 'text/plain'; + let typeFull = type; + for (let i = 1; i < meta.length; i++) { + if (meta[i] === 'base64') { + base64 = true; + } else if (meta[i]) { + typeFull += `;${meta[i]}`; + if (meta[i].indexOf('charset=') === 0) { + charset = meta[i].substring(8); + } + } + } + // defaults to US-ASCII only if type is not provided + if (!meta[0] && !charset.length) { + typeFull += ';charset=US-ASCII'; + charset = 'US-ASCII'; + } + + // get the encoded data portion and decode URI-encoded chars + const encoding = base64 ? 'base64' : 'ascii'; + const data = unescape(uri.substring(firstComma + 1)); + const buffer = Buffer.from(data, encoding) as MimeBuffer; + + // set `.type` and `.typeFull` properties to MIME type + buffer.type = type; + buffer.typeFull = typeFull; + + // set the `.charset` property + buffer.charset = charset; + + return buffer; +} + +export default dataUriToBuffer; diff --git a/packages/data-uri-to-buffer/test/data-uri-to-buffer.test.ts b/packages/data-uri-to-buffer/test/data-uri-to-buffer.test.ts new file mode 100644 index 00000000..044420e4 --- /dev/null +++ b/packages/data-uri-to-buffer/test/data-uri-to-buffer.test.ts @@ -0,0 +1,185 @@ +import assert from 'assert'; +import dataUriToBuffer from '../src'; + +describe('data-uri-to-buffer', function () { + it('should decode bare-bones Data URIs', function () { + const uri = 'data:,Hello%2C%20World!'; + + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal('text/plain;charset=US-ASCII', buf.typeFull); + assert.equal('US-ASCII', buf.charset); + assert.equal('Hello, World!', buf.toString()); + }); + + it('should decode bare-bones "base64" Data URIs', function () { + const uri = 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D'; + + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal('Hello, World!', buf.toString()); + }); + + it('should decode plain-text Data URIs', function () { + const html = + '' + + '' + + 'Embedded Window' + + '

42

' + + ''; + + // Escape the HTML for URL formatting + const uri = 'data:text/html;charset=utf-8,' + encodeURIComponent(html); + + const buf = dataUriToBuffer(uri); + assert.equal('text/html', buf.type); + assert.equal('utf-8', buf.charset); + assert.equal(html, buf.toString()); + }); + + // the next 4 tests are from: + // https://bug161965.bugzilla.mozilla.org/attachment.cgi?id=94670&action=view + + it('should decode "ISO-8859-8 in Base64" URIs', function () { + const uri = 'data:text/plain;charset=iso-8859-8-i;base64,+ezl7Q=='; + + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal('iso-8859-8-i', buf.charset); + assert.equal(4, buf.length); + assert.equal(0xf9, buf[0]); + assert.equal(0xec, buf[1]); + assert.equal(0xe5, buf[2]); + assert.equal(0xed, buf[3]); + }); + + it('should decode "ISO-8859-8 in URL-encoding" URIs', function () { + const uri = 'data:text/plain;charset=iso-8859-8-i,%f9%ec%e5%ed'; + + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal('iso-8859-8-i', buf.charset); + assert.equal(4, buf.length); + assert.equal(0xf9, buf[0]); + assert.equal(0xec, buf[1]); + assert.equal(0xe5, buf[2]); + assert.equal(0xed, buf[3]); + }); + + it('should decode "UTF-8 in Base64" URIs', function () { + const uri = 'data:text/plain;charset=UTF-8;base64,16nXnNeV150='; + + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal('UTF-8', buf.charset); + assert.equal(8, buf.length); + assert.equal('שלום', buf.toString('utf8')); + }); + + it('should decode "UTF-8 in URL-encoding" URIs', function () { + const uri = 'data:text/plain;charset=UTF-8,%d7%a9%d7%9c%d7%95%d7%9d'; + + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal('UTF-8', buf.charset); + assert.equal(8, buf.length); + assert.equal('שלום', buf.toString('utf8')); + }); + + // this next one is from Wikipedia IIRC + + it('should decode "base64" Data URIs with newlines', function () { + const uri = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA\n' + + 'AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO\n' + + '9TXL0Y4OHwAAAABJRU5ErkJggg=='; + + const buf = dataUriToBuffer(uri); + assert.equal('image/png', buf.type); + assert.equal( + 'iVBORw0KGgoAAAANSUhEUgAAAAUA' + + 'AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO' + + '9TXL0Y4OHwAAAABJRU5ErkJggg==', + buf.toString('base64') + ); + }); + + it('should decode a plain-text URI with a space character in it', function () { + const uri = 'data:,foo bar'; + + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal('foo bar', buf.toString()); + }); + + it('should decode data with a "," comma char', function () { + const uri = 'data:,a,b'; + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal('a,b', buf.toString()); + }); + + it('should decode data with traditionally reserved characters like ";"', function () { + const uri = 'data:,;test'; + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal(';test', buf.toString()); + }); + + it('should not default to US-ASCII if main type is provided', function () { + const uri = 'data:text/plain,abc'; + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal('text/plain', buf.typeFull); + assert.equal('', buf.charset); + assert.equal('abc', buf.toString()); + }); + + it('should default to text/plain if main type is not provided', function () { + const uri = 'data:;charset=UTF-8,abc'; + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal('text/plain;charset=UTF-8', buf.typeFull); + assert.equal('UTF-8', buf.charset); + assert.equal('abc', buf.toString()); + }); + + it('should not allow charset without a leading ;', function () { + const uri = 'data:charset=UTF-8,abc'; + const buf = dataUriToBuffer(uri); + assert.equal('charset=UTF-8', buf.type); + assert.equal('charset=UTF-8', buf.typeFull); + assert.equal('', buf.charset); + assert.equal('abc', buf.toString()); + }); + + it('should allow custom media type parameters', function () { + const uri = 'data:application/javascript;version=1.8;charset=UTF-8,abc'; + const buf = dataUriToBuffer(uri); + assert.equal('application/javascript', buf.type); + assert.equal( + 'application/javascript;version=1.8;charset=UTF-8', + buf.typeFull + ); + assert.equal('UTF-8', buf.charset); + assert.equal('abc', buf.toString()); + }); + + it('should allow base64 annotation anywhere', function () { + const uri = 'data:text/plain;base64;charset=UTF-8,YWJj'; + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal('text/plain;charset=UTF-8', buf.typeFull); + assert.equal('UTF-8', buf.charset); + assert.equal('abc', buf.toString()); + }); + + it('should parse meta with unnecessary semicolons', function () { + const uri = 'data:text/plain;;charset=UTF-8;;;base64;;;;,YWJj'; + const buf = dataUriToBuffer(uri); + assert.equal('text/plain', buf.type); + assert.equal('text/plain;charset=UTF-8', buf.typeFull); + assert.equal('UTF-8', buf.charset); + assert.equal('abc', buf.toString()); + }); +}); diff --git a/packages/data-uri-to-buffer/test/tsconfig.json b/packages/data-uri-to-buffer/test/tsconfig.json new file mode 100644 index 00000000..e516b7fa --- /dev/null +++ b/packages/data-uri-to-buffer/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["**/*.ts"] +} diff --git a/packages/data-uri-to-buffer/tsconfig.json b/packages/data-uri-to-buffer/tsconfig.json new file mode 100644 index 00000000..24b6cf7e --- /dev/null +++ b/packages/data-uri-to-buffer/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/degenerator/.eslintignore b/packages/degenerator/.eslintignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/packages/degenerator/.eslintignore @@ -0,0 +1 @@ +dist diff --git a/packages/degenerator/README.md b/packages/degenerator/README.md new file mode 100644 index 00000000..02311199 --- /dev/null +++ b/packages/degenerator/README.md @@ -0,0 +1,129 @@ +degenerator +=========== +### Compiles sync functions into async functions +[![Build Status](https://github.com/TooTallNate/node-degenerator/workflows/Node%20CI/badge.svg)](https://github.com/TooTallNate/node-degenerator/actions?workflow=Node+CI) + +Sometimes you need to write sync looking code that's really async under the hood. +This module takes a String to one or more synchronous JavaScript functions, and +returns a new String that with those JS functions transpiled into `async` +functions. + +So this: + +```js +function foo() { + return a('bar') || b(); +} +``` + +Gets compiled into: + +```js +async function foo() { + return await a('bar') || await b(); +} +``` + +With the compiled output code, you can evaluate the code using the `vm` module +in Node.js, or save the code to a file and require it, or whatever. + + +Installation +------------ + +Install with `npm`: + +```bash +$ npm install degenerator +``` + + +Example +------- + +You must explicitly specify the names of the functions that should be +"asyncified". So say we wanted to expose a `get(url)` function that did +and HTTP request and returned the response body. + +The user has provided us with this implementation: + +``` js +function myFn() { + const one = get('https://google.com'); + const two = get('http://nodejs.org'); + const three = JSON.parse(get('http://jsonip.org')); + return [one, two, three]; +} +``` + +Now we can compile this into an asyncronous function, implement the +async `get()` function, and finally evaluate it into a real JavaScript function +instance with the `vm` module: + + +```typescript +import vm from 'vm'; +import degenerator from 'degenerator'; + +// The `get()` function is Promise-based (error handling omitted for brevity) +function get(endpoint: string) { + return new Promise((resolve, reject) => { + var mod = 0 == endpoint.indexOf('https:') ? require('https') : require('http'); + var req = mod.get(endpoint); + req.on('response', function (res) { + var data = ''; + res.setEncoding('utf8'); + res.on('data', function (b) { data += b; }); + res.on('end', function () { + resolve(data); + }); + }); + }); +} + +// Convert the JavaScript string provided from the user (assumed to be `str` var) +str = degenerator(str, [ 'get' ]); + +// Turn the JS String into a real async function instance +const asyncFn = vm.runInNewContext(`(${str})`, { get }); + +// Now we can invoke the function asynchronously +asyncFn().then((res) => { + // Do something with `res`... +}); +``` + + +API +--- + +### degenerator(code: string, names: Array): String + +Returns a "degeneratorified" JavaScript string, with `async`/`await` transplanted. + + +License +------- + +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/degenerator/jest.config.js b/packages/degenerator/jest.config.js new file mode 100644 index 00000000..07978f56 --- /dev/null +++ b/packages/degenerator/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/packages/degenerator/package.json b/packages/degenerator/package.json new file mode 100644 index 00000000..3ea663f6 --- /dev/null +++ b/packages/degenerator/package.json @@ -0,0 +1,41 @@ +{ + "name": "degenerator", + "version": "3.0.4", + "description": "Compiles sync functions into async generator functions", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "test": "jest --env node --verbose --bail", + "lint": "eslint . --ext .ts", + "prepublishOnly": "npm run build" + }, + "author": "Nathan Rajlich (http://n8.io/)", + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/node-degenerator.git" + }, + "engines": { + "node": ">= 14" + }, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.2", + "escodegen": "^1.8.1", + "esprima": "^4.0.0", + "vm2": "^3.9.17" + }, + "devDependencies": { + "@types/escodegen": "^0.0.6", + "@types/esprima": "^4.0.2", + "@types/jest": "^29.5.1", + "@types/node": "^14.18.43", + "jest": "^29.5.0", + "ts-jest": "^29.1.0", + "tsconfig": "workspace:*", + "typescript": "^5.0.4" + } +} diff --git a/packages/degenerator/src/index.ts b/packages/degenerator/src/index.ts new file mode 100644 index 00000000..196b0aea --- /dev/null +++ b/packages/degenerator/src/index.ts @@ -0,0 +1,215 @@ +import { isRegExp } from 'util'; +import { generate } from 'escodegen'; +import { parseScript } from 'esprima'; +import { visit, namedTypes as n, builders as b } from 'ast-types'; +import { Context, RunningScriptOptions } from 'vm'; +import { VM, VMScript } from 'vm2'; + +/** + * Compiles sync JavaScript code into JavaScript with async Functions. + * + * @param {String} code JavaScript string to convert + * @param {Array} names Array of function names to add `await` operators to + * @return {String} Converted JavaScript string with async/await injected + * @api public + */ + +export function degenerator(code: string, _names: DegeneratorNames): string { + if (!Array.isArray(_names)) { + throw new TypeError('an array of async function "names" is required'); + } + + // Duplicate the `names` array since it's rude to augment the user args + const names = _names.slice(0); + + const ast = parseScript(code); + + // First pass is to find the `function` nodes and turn them into async or + // generator functions only if their body includes `CallExpressions` to + // function in `names`. We also add the names of the functions to the `names` + // array. We'll iterate several time, as every iteration might add new items + // to the `names` array, until no new names were added in the iteration. + let lastNamesLength = 0; + do { + lastNamesLength = names.length; + visit(ast, { + visitVariableDeclaration(path) { + if (path.node.declarations) { + for (let i = 0; i < path.node.declarations.length; i++) { + const declaration = path.node.declarations[i]; + if ( + n.VariableDeclarator.check(declaration) && + n.Identifier.check(declaration.init) && + n.Identifier.check(declaration.id) && + checkName(declaration.init.name, names) && + !checkName(declaration.id.name, names) + ) { + names.push(declaration.id.name); + } + } + } + return false; + }, + visitAssignmentExpression(path) { + if ( + n.Identifier.check(path.node.left) && + n.Identifier.check(path.node.right) && + checkName(path.node.right.name, names) && + !checkName(path.node.left.name, names) + ) { + names.push(path.node.left.name); + } + return false; + }, + visitFunction(path) { + if (path.node.id) { + let shouldDegenerate = false; + visit(path.node, { + visitCallExpression(path) { + if (checkNames(path.node, names)) { + shouldDegenerate = true; + } + return false; + }, + }); + + if (!shouldDegenerate) { + return false; + } + + // Got a "function" expression/statement, + // convert it into an async function + path.node.async = true; + + // Add function name to `names` array + if (!checkName(path.node.id.name, names)) { + names.push(path.node.id.name); + } + } + + this.traverse(path); + }, + }); + } while (lastNamesLength !== names.length); + + // Second pass is for adding `await`/`yield` statements to any function + // invocations that match the given `names` array. + visit(ast, { + visitCallExpression(path) { + if (checkNames(path.node, names)) { + // A "function invocation" expression, + // we need to inject a `AwaitExpression`/`YieldExpression` + const delegate = false; + const { + name, + parent: { node: pNode }, + } = path; + + const expr = b.awaitExpression(path.node, delegate); + + if (n.CallExpression.check(pNode)) { + pNode.arguments[name] = expr; + } else { + pNode[name] = expr; + } + } + + this.traverse(path); + }, + }); + + return generate(ast); +} + +export type DegeneratorName = string | RegExp; +export type DegeneratorNames = DegeneratorName[]; +export interface CompileOptions extends RunningScriptOptions { + sandbox?: Context; +} +export function compile( + code: string, + returnName: string, + names: DegeneratorNames, + options: CompileOptions = {} +): (...args: A) => Promise { + const compiled = degenerator(code, names); + const vm = new VM(options); + const script = new VMScript(`${compiled};${returnName}`, { + filename: options.filename, + }); + const fn = vm.run(script); + if (typeof fn !== 'function') { + throw new Error( + `Expected a "function" to be returned for \`${returnName}\`, but got "${typeof fn}"` + ); + } + const r = function (this: unknown, ...args: A): Promise { + try { + const p = fn.apply(this, args); + if (typeof p?.then === 'function') { + return p; + } + return Promise.resolve(p); + } catch (err) { + return Promise.reject(err); + } + }; + Object.defineProperty(r, 'toString', { + value: fn.toString.bind(fn), + enumerable: false, + }); + return r; +} + +/** + * Returns `true` if `node` has a matching name to one of the entries in the + * `names` array. + * + * @param {types.Node} node + * @param {Array} names Array of function names to return true for + * @return {Boolean} + * @api private + */ + +function checkNames( + { callee }: n.CallExpression, + names: DegeneratorNames +): boolean { + let name: string; + if (n.Identifier.check(callee)) { + name = callee.name; + } else if (n.MemberExpression.check(callee)) { + if ( + n.Identifier.check(callee.object) && + n.Identifier.check(callee.property) + ) { + name = `${callee.object.name}.${callee.property.name}`; + } else { + return false; + } + } else if (n.FunctionExpression.check(callee)) { + if (callee.id) { + name = callee.id.name; + } else { + return false; + } + } else { + throw new Error(`Don't know how to get name for: ${callee.type}`); + } + return checkName(name, names); +} + +function checkName(name: string, names: DegeneratorNames): boolean { + // now that we have the `name`, check if any entries match in the `names` array + for (let i = 0; i < names.length; i++) { + const n = names[i]; + if (isRegExp(n)) { + if (n.test(name)) { + return true; + } + } else if (name === n) { + return true; + } + } + return false; +} diff --git a/packages/degenerator/test/assignment.expected.js b/packages/degenerator/test/assignment.expected.js new file mode 100644 index 00000000..b9905f98 --- /dev/null +++ b/packages/degenerator/test/assignment.expected.js @@ -0,0 +1,11 @@ +baz = foo; +var biz = foo; +function foo() { + return 42; +} +async function bar() { + return await baz(); +} +async function bir() { + return await biz(); +} diff --git a/packages/degenerator/test/assignment.js b/packages/degenerator/test/assignment.js new file mode 100644 index 00000000..5c9a4a6b --- /dev/null +++ b/packages/degenerator/test/assignment.js @@ -0,0 +1,14 @@ +// foo +baz = foo; +var biz = foo; +function foo () { + return 42; +} + +function bar () { + return baz(); +} + +function bir () { + return biz(); +} diff --git a/packages/degenerator/test/basic.expected.js b/packages/degenerator/test/basic.expected.js new file mode 100644 index 00000000..0c9eb5ad --- /dev/null +++ b/packages/degenerator/test/basic.expected.js @@ -0,0 +1,3 @@ +async function foo() { + return await a('bar') || await b(); +} diff --git a/packages/degenerator/test/basic.js b/packages/degenerator/test/basic.js new file mode 100644 index 00000000..49081c4c --- /dev/null +++ b/packages/degenerator/test/basic.js @@ -0,0 +1,3 @@ +function foo () { + return a('bar') || b(); +} diff --git a/packages/degenerator/test/get-example.expected.js b/packages/degenerator/test/get-example.expected.js new file mode 100644 index 00000000..ee7d5cef --- /dev/null +++ b/packages/degenerator/test/get-example.expected.js @@ -0,0 +1,10 @@ +async function myFn() { + var one = await get('https://google.com'); + var two = await get('http://nodejs.org'); + var three = JSON.parse(await get('http://jsonip.org')); + return [ + one, + two, + three + ]; +} diff --git a/packages/degenerator/test/get-example.js b/packages/degenerator/test/get-example.js new file mode 100644 index 00000000..abfbeb10 --- /dev/null +++ b/packages/degenerator/test/get-example.js @@ -0,0 +1,7 @@ +// get +function myFn () { + var one = get('https://google.com'); + var two = get('http://nodejs.org'); + var three = JSON.parse(get('http://jsonip.org')); + return [one, two, three]; +} diff --git a/packages/degenerator/test/multiple.expected.js b/packages/degenerator/test/multiple.expected.js new file mode 100644 index 00000000..cfc875a9 --- /dev/null +++ b/packages/degenerator/test/multiple.expected.js @@ -0,0 +1,9 @@ +async function foo() { + return await baz(); +} +async function bar() { + return await foo(baz); +} +async function baz() { + return await bar(); +} diff --git a/packages/degenerator/test/multiple.js b/packages/degenerator/test/multiple.js new file mode 100644 index 00000000..0c033075 --- /dev/null +++ b/packages/degenerator/test/multiple.js @@ -0,0 +1,12 @@ +// foo +function foo () { + return baz(); +} + +function bar () { + return foo(baz); +} + +function baz () { + return bar(); +} diff --git a/packages/degenerator/test/pac-resolver-gh-16.expected.js b/packages/degenerator/test/pac-resolver-gh-16.expected.js new file mode 100644 index 00000000..8cfde140 --- /dev/null +++ b/packages/degenerator/test/pac-resolver-gh-16.expected.js @@ -0,0 +1,25 @@ +var FindProxyForURL = function (init, profiles) { + return function (url, host) { + 'use strict'; + var result = init, scheme = url.substr(0, url.indexOf(':')); + do { + result = profiles[result]; + if (typeof result === 'function') + result = result(url, host, scheme); + } while (typeof result !== 'string' || result.charCodeAt(0) === 43); + return result; + }; +}('+google', { + '+google': function (url, host, scheme) { + 'use strict'; + if (/(?:^|\.)google\.com$/.test(host)) + return '+internal-vf'; + return 'DIRECT'; + }, + '+internal-vf': function (url, host, scheme) { + 'use strict'; + if (host === '127.0.0.1' || host === '::1' || host.indexOf('.') < 0) + return 'DIRECT'; + return 'PROXY 127.0.0.1:8123'; + } +}); diff --git a/packages/degenerator/test/pac-resolver-gh-16.js b/packages/degenerator/test/pac-resolver-gh-16.js new file mode 100644 index 00000000..a097976e --- /dev/null +++ b/packages/degenerator/test/pac-resolver-gh-16.js @@ -0,0 +1,23 @@ +// FindProxyForURL +var FindProxyForURL = function(init, profiles) { + return function(url, host) { + "use strict"; + var result = init, scheme = url.substr(0, url.indexOf(":")); + do { + result = profiles[result]; + if (typeof result === "function") result = result(url, host, scheme); + } while (typeof result !== "string" || result.charCodeAt(0) === 43); + return result; + }; +}("+google", { + "+google": function(url, host, scheme) { + "use strict"; + if (/(?:^|\.)google\.com$/.test(host)) return "+internal-vf"; + return "DIRECT"; + }, + "+internal-vf": function(url, host, scheme) { + "use strict"; + if (host === "127.0.0.1" || host === "::1" || host.indexOf(".") < 0) return "DIRECT"; + return "PROXY 127.0.0.1:8123"; + } +}); diff --git a/packages/degenerator/test/pac-resolver-gh-3.expected.js b/packages/degenerator/test/pac-resolver-gh-3.expected.js new file mode 100644 index 00000000..06d59864 --- /dev/null +++ b/packages/degenerator/test/pac-resolver-gh-3.expected.js @@ -0,0 +1,23 @@ +async function FindProxyForURL(url, host) { + if (await isHostInAnySubnet(host, [ + '10.1.2.0', + '10.1.3.0' + ], '255.255.255.0')) { + return 'HTTPS proxy.example.com'; + } + if (await isHostInAnySubnet(host, [ + '10.2.2.0', + '10.2.3.0' + ], '255.255.255.0')) { + return 'HTTPS proxy.example.com'; + } + return 'DIRECT'; +} +async function isHostInAnySubnet(host, subnets, mask) { + var subnets_length = subnets.length; + for (i = 0; i < subnets_length; i++) { + if (await isInNet(host, subnets[i], mask)) { + return true; + } + } +} diff --git a/packages/degenerator/test/pac-resolver-gh-3.js b/packages/degenerator/test/pac-resolver-gh-3.js new file mode 100644 index 00000000..8563c8cf --- /dev/null +++ b/packages/degenerator/test/pac-resolver-gh-3.js @@ -0,0 +1,23 @@ +// FindProxyForURL, isInNet +function FindProxyForURL(url, host) { + if (isHostInAnySubnet(host, ['10.1.2.0', '10.1.3.0'], '255.255.255.0')) { + return "HTTPS proxy.example.com"; + } + + if (isHostInAnySubnet(host, ['10.2.2.0', '10.2.3.0'], '255.255.255.0')) { + return "HTTPS proxy.example.com"; + } + + // Everything else, go direct: + return "DIRECT"; +} + +// Checks if the single host is within a list of subnets using the single mask. +function isHostInAnySubnet(host, subnets, mask) { + var subnets_length = subnets.length; + for (i = 0; i < subnets_length; i++) { + if (isInNet(host, subnets[i], mask)) { + return true; + } + } +} diff --git a/packages/degenerator/test/partial.expected.js b/packages/degenerator/test/partial.expected.js new file mode 100644 index 00000000..396fb4de --- /dev/null +++ b/packages/degenerator/test/partial.expected.js @@ -0,0 +1,12 @@ +async function foo() { + return await baz(); +} +async function bar() { + return await foo(baz); +} +async function baz() { + return await bar(); +} +function shouldntChange() { + return 42; +} diff --git a/packages/degenerator/test/partial.js b/packages/degenerator/test/partial.js new file mode 100644 index 00000000..dffb74d7 --- /dev/null +++ b/packages/degenerator/test/partial.js @@ -0,0 +1,16 @@ +// foo +function foo () { + return baz(); +} + +function bar () { + return foo(baz); +} + +function baz () { + return bar(); +} + +function shouldntChange () { + return 42; +} diff --git a/packages/degenerator/test/test.ts b/packages/degenerator/test/test.ts new file mode 100644 index 00000000..3322f9bf --- /dev/null +++ b/packages/degenerator/test/test.ts @@ -0,0 +1,204 @@ +import fs from 'fs'; +import path from 'path'; +import assert from 'assert'; +import { degenerator, compile } from '../src'; + +describe('degenerator()', () => { + it('should support "async" output functions', () => { + function aPlusB(a: () => string, b: () => string): string { + return a() + b(); + } + const compiled = degenerator('' + aPlusB, ['a']); + assert.equal( + compiled.replace(/\s+/g, ' '), + 'async function aPlusB(a, b) { return await a() + b(); }' + ); + }); + it('should be the default "output" mode (without options)', () => { + function foo(a: () => string): string { + return a(); + } + const compiled = degenerator('' + foo, ['a']); + assert.equal( + compiled.replace(/\s+/g, ' '), + 'async function foo(a) { return await a(); }' + ); + }); + + describe('"expected" fixture tests', () => { + fs.readdirSync(__dirname) + .sort() + .forEach((n) => { + if (n === 'tsconfig.json') return; + if (/\.expected\.js$/.test(n)) return; + if (/\.ts$/.test(n)) return; + if (/\.map/.test(n)) return; + + const expectedName = `${path.basename(n, '.js')}.expected.js`; + + it(`${n} → ${expectedName}`, function () { + const sourceName = path.resolve(__dirname, n); + const compiledName = path.resolve(__dirname, expectedName); + const js = fs.readFileSync(sourceName, 'utf8'); + const expected = fs.readFileSync(compiledName, 'utf8'); + + // the test case can define the `names` to use as a + // comment on the first line of the file + const m = js.match(/\/\/\s*(.*)/); + let names; + if (m) { + // the comment should be a comma-separated list of function names + names = m[1].split(/,\s*/); + } else { + // if no function names were passed in then convert them all + names = [/.*/]; + } + + const compiled = degenerator(js, names); + assert.equal( + compiled.trim().replace(/\r/g, ''), + expected.trim().replace(/\r/g, '') + ); + }); + }); + }); + + describe('`compile()`', () => { + it('should compile code into an invocable async function', async () => { + const a = (v: string) => Promise.resolve(v); + const b = () => 'b'; + function aPlusB(v: string): string { + return a(v) + b(); + } + const fn = compile('' + aPlusB, 'aPlusB', ['a'], { + sandbox: { a, b }, + }); + const val = await fn('c'); + assert.equal(val, 'cb'); + }); + it('should contain the compiled code in `toString()` output', () => { + const a = () => 'a'; + const b = () => 'b'; + function aPlusB(): string { + return a() + b(); + } + const fn = compile<() => Promise>( + '' + aPlusB, + 'aPlusB', + ['b'], + { + sandbox: { a, b }, + } + ); + assert(/await b\(\)/.test(fn + '')); + }); + it('should be able to await non-promises', () => { + const a = () => 'a'; + const b = () => 'b'; + function aPlusB(): string { + return a() + b(); + } + const fn = compile<() => Promise>( + '' + aPlusB, + 'aPlusB', + ['a'], + { + sandbox: { a, b }, + } + ); + return fn().then((val) => { + assert.equal(val, 'ab'); + }); + }); + it('should be able to compile functions with no async', () => { + const a = () => 'a'; + const b = () => 'b'; + function aPlusB(): string { + return a() + b(); + } + const fn = compile('' + aPlusB, 'aPlusB', [], { + sandbox: { a, b }, + }); + return fn().then((val: string) => { + assert.equal(val, 'ab'); + }); + }); + it('should throw an Error if no function is returned from the `vm`', () => { + let err: Error | undefined; + try { + compile<() => Promise>('const foo = 1', 'foo', []); + } catch (_err) { + err = _err as Error; + } + assert(err); + assert.equal( + err.message, + 'Expected a "function" to be returned for `foo`, but got "number"' + ); + }); + it('should compile if branches', () => { + function ifA(): string { + if (a()) { + return 'foo'; + } + return 'bar'; + } + function a() { + if (b()) { + return false; + } + return true; + } + function b() { + return false; + } + const fn = compile(`${ifA};${a}`, 'ifA', ['b'], { + sandbox: { b }, + }); + return fn().then((val: string) => { + assert.equal(val, 'foo'); + }); + }); + it('should prevent privilege escalation of untrusted code', async () => { + let err: Error | undefined; + try { + const fn = compile( + `const f = this.constructor.constructor('return process');`, + 'f', + [] + ); + await fn(); + } catch (_err) { + err = _err as Error; + } + assert(err); + assert.equal(err.message, 'process is not defined'); + }); + it('should allow to return synchronous undefined', () => { + function u() { + // empty + } + const fn = compile(`${u}`, 'u', ['']); + return fn().then((val) => { + assert.strictEqual(val, undefined); + }); + }); + it('should support "filename" option', async () => { + function u() { + throw new Error('fail'); + } + let err: Error | undefined; + const fn = compile(`${u}`, 'u', [''], { + filename: '/foo/bar/baz.js', + }); + try { + await fn(); + } catch (_err) { + err = _err as Error; + } + assert(err); + assert.strictEqual(err.message, 'fail'); + assert(err.stack?.includes('at u (/foo/bar/baz.js:')); + }); + }); +}); diff --git a/packages/degenerator/test/tsconfig.json b/packages/degenerator/test/tsconfig.json new file mode 100644 index 00000000..a79e2e63 --- /dev/null +++ b/packages/degenerator/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["test.ts"] +} diff --git a/packages/degenerator/tsconfig.json b/packages/degenerator/tsconfig.json new file mode 100644 index 00000000..24b6cf7e --- /dev/null +++ b/packages/degenerator/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/get-uri/.eslintignore b/packages/get-uri/.eslintignore new file mode 100644 index 00000000..53c37a16 --- /dev/null +++ b/packages/get-uri/.eslintignore @@ -0,0 +1 @@ +dist \ No newline at end of file diff --git a/packages/get-uri/README.md b/packages/get-uri/README.md new file mode 100644 index 00000000..69e29bb5 --- /dev/null +++ b/packages/get-uri/README.md @@ -0,0 +1,147 @@ +get-uri +======= +### Returns a `stream.Readable` from a URI string +[![Build Status](https://github.com/TooTallNate/node-get-uri/workflows/Node%20CI/badge.svg)](https://github.com/TooTallNate/node-get-uri/actions?workflow=Node+CI) + +This high-level module accepts a URI string and returns a `Readable` stream +instance. There is built-in support for a variety of "protocols", and it's +easily extensible with more: + +| Protocol | Description | Example +|:---------:|:-------------------------------:|:---------------------------------: +| `data` | [Data URIs][data] | `data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D` +| `file` | [File URIs][file] | `file:///c:/windows/example.ini` +| `ftp` | [FTP URIs][ftp] | `ftp://ftp.kernel.org/pub/site/README` +| `http` | [HTTP URIs][http] | `http://www.example.com/path/to/name` +| `https` | [HTTPS URIs][https] | `https://www.example.com/path/to/name` + + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install get-uri +``` + + +Example +------- + +To simply get a `stream.Readable` instance from a `file:` URI, try something like: + +```ts +import { getUri } from 'get-uri'; + +// `file:` maps to a `fs.ReadStream` instance… +const stream = await getUri('file:///Users/nrajlich/wat.json'); +stream.pipe(process.stdout); +``` + + +Missing Endpoints +----------------- + +When you pass in a URI in which the resource referenced does not exist on the +destination server, then a `NotFoundError` will be returned. The `code` of the +error instance is set to `"ENOTFOUND"`, so you can special-case that in your code +to detect when a bad filename is requested: + +```ts +try { + await getUri('http://example.com/resource.json'); +} catch (err) { + if (err.code === 'ENOTFOUND') { + // bad file path requested + } else { + // something else bad happened... + throw err; + } +} +``` + + +Cacheability +------------ + +When calling `getUri()` with the same URI multiple times, the `get-uri` module +supports sending an indicator that the remote resource has not been modified +since the last time it has been retreived from that node process. + +To do this, pass in a `cache` option to the "options object" argument +with the value set to the `stream.Readable` instance that was previously +returned. If the remote resource has not been changed since the last call for +that same URI, then a `NotModifiedError` instance will be returned with it's +`code` property set to `"ENOTMODIFIED"`. + +When the `"ENOTMODIFIED"` error occurs, then you can safely re-use the +results from the previous `getUri()` call for that same URI: + +``` js +// First time fetches for real +const stream = await getUri('http://example.com/resource.json'); + +try { + // … some time later, if you need to get this same URI again, pass in the + // previous `stream.Readable` instance as `cache` option to potentially + // have an "ENOTMODIFIED" error thrown: + await getUri('http://example.com/resource.json', { cache: stream }); +} catch (err) { + if (err.code === 'ENOTMODIFIED') { + // source file has not been modified since last time it was requested, + // so you are expected to re-use results from a previous call to `getUri()` + } else { + // something else bad happened... + throw err; + } +} +``` + + +API +--- + +### getUri(uri: string | URL, options?: Object]): Promise + +A `uri` is required. An optional `options` object may be passed in: + + - `cache` - A `stream.Readable` instance from a previous call to `getUri()` with the same URI. If this option is passed in, and the destination endpoint has not been modified, then an `ENOTMODIFIED` error is thrown + +Any other options passed in to the `options` object will be passed through +to the low-level connection creation functions (`http.get()`, `ftp.connect()`, +etc). + +Returns a `stream.Readable` instance to read the resource at the given `uri`. + +License +------- + +(The MIT License) + +Copyright (c) 2014 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +[data]: http://tools.ietf.org/html/rfc2397 +[file]: http://tools.ietf.org/html/draft-hoffman-file-uri-03 +[ftp]: http://www.w3.org/Protocols/rfc959/ +[http]: http://www.w3.org/Protocols/rfc2616/rfc2616.html +[https]: http://wikipedia.org/wiki/HTTP_Secure diff --git a/packages/get-uri/jest.config.js b/packages/get-uri/jest.config.js new file mode 100644 index 00000000..3745fc22 --- /dev/null +++ b/packages/get-uri/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/packages/get-uri/package.json b/packages/get-uri/package.json new file mode 100644 index 00000000..d616bfa3 --- /dev/null +++ b/packages/get-uri/package.json @@ -0,0 +1,63 @@ +{ + "name": "get-uri", + "version": "5.0.0", + "description": "Returns a `stream.Readable` from a URI string", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "test": "jest --env node --verbose --bail", + "lint": "eslint . --ext .ts", + "prepublishOnly": "npm run build" + }, + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/node-get-uri.git" + }, + "keywords": [ + "uri", + "read", + "readstream", + "stream", + "get", + "http", + "https", + "ftp", + "file", + "data", + "protocol", + "url" + ], + "author": "Nathan Rajlich (http://n8.io/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/TooTallNate/node-get-uri/issues" + }, + "devDependencies": { + "@types/debug": "^4.1.7", + "@types/fs-extra": "^8.1.2", + "@types/ftpd": "^0.2.35", + "@types/jest": "^29.5.1", + "@types/node": "^14.18.43", + "async-listen": "^2.1.0", + "ftpd": "https://files-jg1s1zt9l.n8.io/ftpd-v0.2.14.tgz", + "jest": "^29.5.0", + "st": "^1.2.2", + "ts-jest": "^29.1.0", + "tsconfig": "workspace:*", + "typescript": "^5.0.4" + }, + "dependencies": { + "@tootallnate/once": "^2.0.0", + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^4.0.1", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">= 14" + } +} diff --git a/packages/get-uri/src/data.ts b/packages/get-uri/src/data.ts new file mode 100644 index 00000000..f4dbf1ae --- /dev/null +++ b/packages/get-uri/src/data.ts @@ -0,0 +1,48 @@ +import createDebug from 'debug'; +import { Readable } from 'stream'; +import { createHash } from 'crypto'; +import dataUriToBuffer from 'data-uri-to-buffer'; +import { GetUriProtocol } from './'; +import NotModifiedError from './notmodified'; + +const debug = createDebug('get-uri:data'); + +class DataReadable extends Readable { + public hash?: string; + + constructor(hash: string, buf: Buffer) { + super(); + this.push(buf); + this.push(null); + this.hash = hash; + } +} + +export interface DataOptions { + cache?: DataReadable; +} + +/** + * Returns a Readable stream from a "data:" URI. + */ +export const data: GetUriProtocol = async ( + { href: uri }, + { cache } = {} +) => { + // need to create a SHA1 hash of the URI string, for cacheability checks + // in future `getUri()` calls with the same data URI passed in. + const shasum = createHash('sha1'); + shasum.update(uri); + const hash = shasum.digest('hex'); + debug('generated SHA1 hash for "data:" URI: %o', hash); + + // check if the cache is the same "data:" URI that was previously passed in. + if (cache?.hash === hash) { + debug('got matching cache SHA1 hash: %o', hash); + throw new NotModifiedError(); + } else { + debug('creating Readable stream from "data:" URI buffer'); + const buf = dataUriToBuffer(uri); + return new DataReadable(hash, buf); + } +}; diff --git a/packages/get-uri/src/file.ts b/packages/get-uri/src/file.ts new file mode 100644 index 00000000..292c3fe0 --- /dev/null +++ b/packages/get-uri/src/file.ts @@ -0,0 +1,77 @@ +import { Readable } from 'stream'; +import createDebug from 'debug'; +import { Stats, createReadStream } from 'fs'; +import { fstat, open } from 'fs-extra'; +import { GetUriProtocol } from './'; +import NotFoundError from './notfound'; +import NotModifiedError from './notmodified'; +import { fileURLToPath } from 'url'; + +const debug = createDebug('get-uri:file'); + +type ReadStreamOptions = NonNullable< + Exclude[1], string> +>; + +interface FileReadable extends Readable { + stat?: Stats; +} + +export interface FileOptions extends ReadStreamOptions { + cache?: FileReadable; +} + +/** + * Returns a `fs.ReadStream` instance from a "file:" URI. + */ + +export const file: GetUriProtocol = async ( + { href: uri }, + opts = {} +) => { + const { + cache, + flags = 'r', + mode = 438, // =0666 + } = opts; + + try { + // Convert URI → Path + const filepath = fileURLToPath(uri); + debug('Normalized pathname: %o', filepath); + + // `open()` first to get a file descriptor and ensure that the file + // exists. + const fd = await open(filepath, flags, mode); + + // Now `fstat()` to check the `mtime` and store the stat object for + // the cache. + const stat = await fstat(fd); + + // if a `cache` was provided, check if the file has not been modified + if (cache && cache.stat && stat && isNotModified(cache.stat, stat)) { + throw new NotModifiedError(); + } + + // `fs.ReadStream` takes care of calling `fs.close()` on the + // fd after it's done reading + // @ts-expect-error `@types/node` doesn't allow `null` as file path :/ + const rs = createReadStream(null, { + autoClose: true, + ...opts, + fd, + }) as FileReadable; + rs.stat = stat; + return rs; + } catch (err: unknown) { + if ((err as NodeJS.ErrnoException).code === 'ENOENT') { + throw new NotFoundError(); + } + throw err; + } +}; + +// returns `true` if the `mtime` of the 2 stat objects are equal +function isNotModified(prev: Stats, curr: Stats): boolean { + return +prev.mtime === +curr.mtime; +} diff --git a/packages/get-uri/src/ftp.ts b/packages/get-uri/src/ftp.ts new file mode 100644 index 00000000..27bc3318 --- /dev/null +++ b/packages/get-uri/src/ftp.ts @@ -0,0 +1,103 @@ +import { Client, AccessOptions } from 'basic-ftp'; +import { PassThrough, Readable } from 'stream'; +import { basename, dirname } from 'path'; +import createDebug from 'debug'; +import NotFoundError from './notfound'; +import NotModifiedError from './notmodified'; +import { GetUriProtocol } from '.'; + +const debug = createDebug('get-uri:ftp'); + +export interface FTPReadable extends Readable { + lastModified?: Date; +} + +export interface FTPOptions extends AccessOptions { + cache?: FTPReadable; +} + +/** + * Returns a Readable stream from an "ftp:" URI. + */ +export const ftp: GetUriProtocol = async (url, opts = {}) => { + const { cache } = opts; + const filepath = decodeURIComponent(url.pathname); + let lastModified: Date | undefined; + + if (!filepath) { + throw new TypeError('No "pathname"!'); + } + + const client = new Client(); + + try { + const host = url.hostname || url.host || 'localhost'; + const port = parseInt(url.port || '0', 10) || 21; + const user = url.username + ? decodeURIComponent(url.username) + : undefined; + const password = url.password + ? decodeURIComponent(url.password) + : undefined; + + await client.access({ + host, + port, + user, + password, + ...opts, + }); + + // first we have to figure out the Last Modified date. + // try the MDTM command first, which is an optional extension command. + try { + lastModified = await client.lastMod(filepath); + } catch (err: unknown) { + // handle the "file not found" error code + if ((err as { code: number }).code === 550) { + throw new NotFoundError(); + } + } + + if (!lastModified) { + // Try to get the last modified date via the LIST command (uses + // more bandwidth, but is more compatible with older FTP servers + const list = await client.list(dirname(filepath)); + + // attempt to find the "entry" with a matching "name" + const name = basename(filepath); + const entry = list.find((e) => e.name === name); + if (entry) { + lastModified = entry.modifiedAt; + } + } + + if (lastModified) { + if (isNotModified()) { + throw new NotModifiedError(); + } + } else { + throw new NotFoundError(); + } + + const stream = new PassThrough(); + const rs = stream as FTPReadable; + client.downloadTo(stream, filepath).then((result) => { + debug(result.message); + client.close(); + }); + rs.lastModified = lastModified; + return rs; + } catch (err) { + client.close(); + throw err; + } + + // called when `lastModified` is set, and a "cache" stream was provided + function isNotModified(): boolean { + if (cache?.lastModified && lastModified) { + return +cache.lastModified === +lastModified; + } + return false; + } +}; diff --git a/packages/get-uri/src/http-error.ts b/packages/get-uri/src/http-error.ts new file mode 100644 index 00000000..34fb235c --- /dev/null +++ b/packages/get-uri/src/http-error.ts @@ -0,0 +1,15 @@ +import { STATUS_CODES } from 'http'; + +/** + * Error subclass to use when an HTTP application error has occurred. + */ +export default class HTTPError extends Error { + public code: string; + public statusCode: number; + + constructor(statusCode: number, message = STATUS_CODES[statusCode]) { + super(message); + this.statusCode = statusCode; + this.code = `E${String(message).toUpperCase().replace(/\s+/g, '')}`; + } +} diff --git a/packages/get-uri/src/http.ts b/packages/get-uri/src/http.ts new file mode 100644 index 00000000..11ce12d3 --- /dev/null +++ b/packages/get-uri/src/http.ts @@ -0,0 +1,241 @@ +import http_ from 'http'; +import https from 'https'; +import once from '@tootallnate/once'; +import createDebug from 'debug'; +import { Readable } from 'stream'; +import { GetUriProtocol } from '.'; +import HTTPError from './http-error'; +import NotFoundError from './notfound'; +import NotModifiedError from './notmodified'; + +const debug = createDebug('get-uri:http'); + +type HttpOrHttpsModule = typeof http_ | typeof https; + +export interface HttpReadableProps { + date?: number; + parsed?: URL; + redirects?: HttpReadable[]; +} + +export interface HttpReadable extends Readable, HttpReadableProps {} + +export interface HttpIncomingMessage + extends http_.IncomingMessage, + HttpReadableProps {} + +export interface HttpOptions extends https.RequestOptions { + cache?: HttpReadable; + http?: HttpOrHttpsModule; + redirects?: HttpReadable[]; + maxRedirects?: number; +} + +/** + * Returns a Readable stream from an "http:" URI. + */ +export const http: GetUriProtocol = async (url, opts = {}) => { + debug('GET %o', url.href); + + const cache = getCache(url, opts.cache); + + // first check the previous Expires and/or Cache-Control headers + // of a previous response if a `cache` was provided + if (cache && isFresh(cache) && typeof cache.statusCode === 'number') { + // check for a 3xx "redirect" status code on the previous cache + const type = (cache.statusCode / 100) | 0; + if (type === 3 && cache.headers.location) { + debug('cached redirect'); + throw new Error('TODO: implement cached redirects!'); + } + // otherwise we assume that it's the destination endpoint, + // since there's nowhere else to redirect to + throw new NotModifiedError(); + } + + // 5 redirects allowed by default + const maxRedirects = + typeof opts.maxRedirects === 'number' ? opts.maxRedirects : 5; + debug('allowing %o max redirects', maxRedirects); + + let mod; + if (opts.http) { + // the `https` module passed in from the "http.js" file + mod = opts.http; + debug('using secure `https` core module'); + } else { + mod = http_; + debug('using `http` core module'); + } + + const options = { ...opts }; + + // add "cache validation" headers if a `cache` was provided + if (cache) { + if (!options.headers) { + options.headers = {}; + } + + const lastModified = cache.headers['last-modified']; + if (lastModified) { + options.headers['If-Modified-Since'] = lastModified; + debug('added "If-Modified-Since" request header: %o', lastModified); + } + + const etag = cache.headers.etag; + if (etag) { + options.headers['If-None-Match'] = etag; + debug('added "If-None-Match" request header: %o', etag); + } + } + + const req = mod.get(url, options); + const [res]: [HttpIncomingMessage] = await once(req, 'response'); + const code = res.statusCode || 0; + + // assign a Date to this response for the "Cache-Control" delta calculation + res.date = Date.now(); + res.parsed = url; + + debug('got %o response status code', code); + + // any 2xx response is a "success" code + const type = (code / 100) | 0; + + // check for a 3xx "redirect" status code + const location = res.headers.location; + if (type === 3 && location) { + if (!opts.redirects) opts.redirects = []; + const redirects = opts.redirects; + + if (redirects.length < maxRedirects) { + debug('got a "redirect" status code with Location: %o', location); + + // flush this response - we're not going to use it + res.resume(); + + // hang on to this Response object for the "redirects" Array + redirects.push(res); + + const newUri = new URL(location, url.href); + debug('resolved redirect URL: %o', newUri.href); + + const left = maxRedirects - redirects.length; + debug('%o more redirects allowed after this one', left); + + // check if redirecting to a different protocol + if (newUri.protocol !== url.protocol) { + opts.http = newUri.protocol === 'https:' ? https : undefined; + } + + return http(newUri, opts); + } + } + + // if we didn't get a 2xx "success" status code, then create an Error object + if (type !== 2) { + res.resume(); + if (code === 304) { + throw new NotModifiedError(); + } else if (code === 404) { + throw new NotFoundError(); + } + // other HTTP-level error + throw new HTTPError(code); + } + + if (opts.redirects) { + // store a reference to the "redirects" Array on the Response object so that + // they can be inspected during a subsequent call to GET the same URI + res.redirects = opts.redirects; + } + + return res; +}; + +/** + * Returns `true` if the provided cache's "freshness" is valid. That is, either + * the Cache-Control header or Expires header values are still within the allowed + * time period. + * + * @return {Boolean} + * @api private + */ + +function isFresh(cache: HttpIncomingMessage): boolean { + let fresh = false; + let expires = parseInt(cache.headers.expires || '', 10); + const cacheControl = cache.headers['cache-control']; + + if (cacheControl) { + // for Cache-Control rules, see: http://www.mnot.net/cache_docs/#CACHE-CONTROL + debug('Cache-Control: %o', cacheControl); + + const parts = cacheControl.split(/,\s*?\b/); + for (let i = 0; i < parts.length; i++) { + const part = parts[i]; + const subparts = part.split('='); + const name = subparts[0]; + switch (name) { + case 'max-age': + expires = + (cache.date || 0) + parseInt(subparts[1], 10) * 1000; + fresh = Date.now() < expires; + if (fresh) { + debug( + 'cache is "fresh" due to previous %o Cache-Control param', + part + ); + } + return fresh; + case 'must-revalidate': + // XXX: what we supposed to do here? + break; + case 'no-cache': + case 'no-store': + debug( + 'cache is "stale" due to explicit %o Cache-Control param', + name + ); + return false; + default: + // ignore unknown cache value + break; + } + } + } else if (expires) { + // for Expires rules, see: http://www.mnot.net/cache_docs/#EXPIRES + debug('Expires: %o', expires); + fresh = Date.now() < expires; + if (fresh) { + debug('cache is "fresh" due to previous Expires response header'); + } + return fresh; + } + + return false; +} + +/** + * Attempts to return a previous Response object from a previous GET call to the + * same URI. + * + * @api private + */ + +function getCache(url: URL, cache?: HttpReadable): HttpIncomingMessage | null { + if (cache) { + if (cache.parsed && cache.parsed.href === url.href) { + return cache as HttpIncomingMessage; + } + if (cache.redirects) { + for (let i = 0; i < cache.redirects.length; i++) { + const c = getCache(url, cache.redirects[i]); + if (c) { + return c as HttpIncomingMessage; + } + } + } + } + return null; +} diff --git a/packages/get-uri/src/https.ts b/packages/get-uri/src/https.ts new file mode 100644 index 00000000..21e56eac --- /dev/null +++ b/packages/get-uri/src/https.ts @@ -0,0 +1,10 @@ +import https_ from 'https'; +import { http, HttpOptions } from './http'; +import type { GetUriProtocol } from '.'; + +/** + * Returns a Readable stream from an "https:" URI. + */ +export const https: GetUriProtocol = (url, opts) => { + return http(url, { ...opts, http: https_ }); +}; diff --git a/packages/get-uri/src/index.ts b/packages/get-uri/src/index.ts new file mode 100644 index 00000000..1ae32ebf --- /dev/null +++ b/packages/get-uri/src/index.ts @@ -0,0 +1,81 @@ +import createDebug from 'debug'; +import { Readable } from 'stream'; + +// Built-in protocols +import { data } from './data'; +import { file } from './file'; +import { ftp } from './ftp'; +import { http } from './http'; +import { https } from './https'; + +const debug = createDebug('get-uri'); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type Protocol = T extends `${infer Protocol}:${infer _}` ? Protocol : never; + +export type GetUriProtocol = (parsed: URL, opts?: T) => Promise; + +export const protocols = { + data, + file, + ftp, + http, + https, +}; + +export type Protocols = typeof protocols; + +export type ProtocolsOptions = { + [P in keyof Protocols]: NonNullable[1]> +} + +export type ProtocolOpts = { + [P in keyof ProtocolsOptions]: Protocol extends P + ? ProtocolsOptions[P] + : never; +}[keyof Protocols]; + +const VALID_PROTOCOLS = new Set(Object.keys(protocols)); + +export function isValidProtocol(p: string): p is keyof Protocols { + return VALID_PROTOCOLS.has(p); +} + +/** + * Async function that returns a `stream.Readable` instance that will output + * the contents of the given URI. + * + * For caching purposes, you can pass in a `stream` instance from a previous + * `getUri()` call as a `cache: stream` option, and if the destination has + * not changed since the last time the endpoint was retreived then the callback + * will be invoked with an Error object with `code` set to "ENOTMODIFIED" and + * `null` for the "stream" instance argument. In this case, you can skip + * retreiving the file again and continue to use the previous payload. + * + * @param {String} uri URI to retrieve + * @param {Object} opts optional "options" object + * @api public + */ +export async function getUri( + uri: Uri | URL, + opts?: ProtocolOpts +): Promise { + debug('getUri(%o)', uri); + + if (!uri) { + throw new TypeError('Must pass in a URI to "getUri()"'); + } + + const url = typeof uri === 'string' ? new URL(uri) : uri; + + // Strip trailing `:` + const protocol = url.protocol.replace(/:$/, ''); + if (!isValidProtocol(protocol)) { + throw new TypeError( + `Unsupported protocol "${protocol}" specified in URI: "${uri}"` + ); + } + + const getter = protocols[protocol]; + return getter(url, opts as never); +} diff --git a/packages/get-uri/src/notfound.ts b/packages/get-uri/src/notfound.ts new file mode 100644 index 00000000..a853a123 --- /dev/null +++ b/packages/get-uri/src/notfound.ts @@ -0,0 +1,14 @@ +/** + * Error subclass to use when the source does not exist at the specified endpoint. + * + * @param {String} message optional "message" property to set + * @api protected + */ + +export default class NotFoundError extends Error { + public code = 'ENOTFOUND'; + + constructor(message?: string) { + super(message || 'File does not exist at the specified endpoint'); + } +} diff --git a/packages/get-uri/src/notmodified.ts b/packages/get-uri/src/notmodified.ts new file mode 100644 index 00000000..264f63e7 --- /dev/null +++ b/packages/get-uri/src/notmodified.ts @@ -0,0 +1,16 @@ +/** + * Error subclass to use when the source has not been modified. + * + * @param {String} message optional "message" property to set + * @api protected + */ +export default class NotModifiedError extends Error { + public code = 'ENOTMODIFIED'; + + constructor(message?: string) { + super( + message || + 'Source has not been modified since the provied "cache", re-use previous results' + ); + } +} diff --git a/packages/get-uri/test/data.test.ts b/packages/get-uri/test/data.test.ts new file mode 100644 index 00000000..3ea4fdcf --- /dev/null +++ b/packages/get-uri/test/data.test.ts @@ -0,0 +1,31 @@ +/** + * Module dependencies. + */ + +import { getUri } from '../src'; +import { toBuffer } from './util'; + +describe('get-uri', () => { + describe('"data:" protocol', () => { + it('should work for URL-encoded data', async () => { + const stream = await getUri('data:,Hello%2C%20World!'); + const buf = await toBuffer(stream); + expect(buf.toString()).toEqual('Hello, World!'); + }); + + it('should work for base64-encoded data', async () => { + const stream = await getUri( + 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D' + ); + const buf = await toBuffer(stream); + expect(buf.toString()).toEqual('Hello, World!'); + }); + + it('should return ENOTMODIFIED for the same URI with `cache`', async () => { + const cache = await getUri('data:,Hello%2C%20World!'); + await expect( + getUri('data:,Hello%2C%20World!', { cache }) + ).rejects.toHaveProperty('code', 'ENOTMODIFIED'); + }); + }); +}); diff --git a/packages/get-uri/test/file with special chars! b/packages/get-uri/test/file with special chars! new file mode 100644 index 00000000..725d8875 --- /dev/null +++ b/packages/get-uri/test/file with special chars! @@ -0,0 +1 @@ +this is the file with special chars diff --git a/packages/get-uri/test/file.test.ts b/packages/get-uri/test/file.test.ts new file mode 100644 index 00000000..084611cf --- /dev/null +++ b/packages/get-uri/test/file.test.ts @@ -0,0 +1,43 @@ +import { join } from 'path'; +import { pathToFileURL } from 'url'; +import { readFile } from 'fs-extra'; +import { getUri } from '../src'; +import { toBuffer } from './util'; + +describe('get-uri', () => { + describe('"file:" protocol', () => { + it('should work for local files', async () => { + const actual = await readFile(__filename, 'utf8'); + const uri = pathToFileURL(__filename); + const stream = await getUri(uri); + const buf = await toBuffer(stream); + expect(buf.toString()).toEqual(actual); + }); + + it('should work for files with special characters in name', async () => { + const file = join(__dirname, 'file with special chars!'); + const actual = await readFile(file, 'utf8'); + const uri = pathToFileURL(file); + const stream = await getUri(uri); + const buf = await toBuffer(stream); + expect(buf.toString()).toEqual(actual); + }); + + it('should return ENOTFOUND for bad filenames', async () => { + const uri = pathToFileURL(`${__filename}does-not-exist`); + await expect(getUri(uri)).rejects.toHaveProperty( + 'code', + 'ENOTFOUND' + ); + }); + + it('should return ENOTMODIFIED for the same URI with `cache`', async () => { + const uri = pathToFileURL(__filename); + const cache = await getUri(uri); + await expect(getUri(uri, { cache })).rejects.toHaveProperty( + 'code', + 'ENOTMODIFIED' + ); + }); + }); +}); diff --git a/packages/get-uri/test/ftp.test.ts b/packages/get-uri/test/ftp.test.ts new file mode 100644 index 00000000..8014c154 --- /dev/null +++ b/packages/get-uri/test/ftp.test.ts @@ -0,0 +1,87 @@ +import { basename, join } from 'path'; +import { FtpServer } from 'ftpd'; +import { getUri } from '../src'; +import { readFile } from 'fs-extra'; +import { toBuffer } from './util'; + +describe('get-uri', () => { + describe('"ftp:" protocol', () => { + let port: number; + let server: FtpServer; + + beforeAll(async () => { + server = new FtpServer('127.0.0.1', { + // @ts-expect-error ... + logLevel: -1, + getInitialCwd(_socket, fn) { + // @ts-expect-error ... + fn?.(undefined, '/'); + }, + getRoot() { + return __dirname; + }, + }); + + server.on('client:connected', (conn) => { + let username: string; + // @ts-expect-error ... + conn.on('command:user', (user, success) => { + username = user; + success(); + }); + // @ts-expect-error ... + conn.on('command:pass', (pass, success) => { + success(username); + }); + }); + + port = await new Promise((r) => + // @ts-expect-error ... + server.listen(0, () => { + // @ts-expect-error ... + r(server.server.address().port); + }) + ); + }); + + afterAll(() => { + server.close(); + }); + + it('should work for ftp endpoints', async () => { + const actual = await readFile(__filename, 'utf8'); + const stream = await getUri( + `ftp://127.0.0.1:${port}/${basename(__filename)}` + ); + const buf = await toBuffer(stream); + expect(buf.toString()).toEqual(actual); + }); + + it('should work for files with special characters in name', async () => { + const file = join(__dirname, 'file with special chars!'); + const actual = await readFile(file, 'utf8'); + const url = new URL(`ftp://127.0.0.1:${port}`); + url.pathname = basename(file); + const stream = await getUri(url); + const buf = await toBuffer(stream); + expect(buf.toString()).toEqual(actual); + }); + + it('should return ENOTFOUND for bad filenames', async () => { + await expect( + getUri(`ftp://127.0.0.1:${port}/does-not-exist`) + ).rejects.toHaveProperty('code', 'ENOTFOUND'); + }); + + it('should return ENOTMODIFIED for the same URI with `cache`', async () => { + const cache = await getUri( + `ftp://127.0.0.1:${port}/${basename(__filename)}` + ); + await expect( + getUri(`ftp://127.0.0.1:${port}/${basename(__filename)}`, { + cache, + }) + ).rejects.toHaveProperty('code', 'ENOTMODIFIED'); + }); + }); +}); diff --git a/packages/get-uri/test/http.test.ts b/packages/get-uri/test/http.test.ts new file mode 100644 index 00000000..7ee20e04 --- /dev/null +++ b/packages/get-uri/test/http.test.ts @@ -0,0 +1,56 @@ +// @ts-expect-error no `@types/st` +import st from 'st'; +import path from 'path'; +import http from 'http'; +import { listen } from 'async-listen'; +import { readFile } from 'fs-extra'; +import { getUri } from '../src'; +import { toBuffer } from './util'; + +describe('get-uri', () => { + describe('"http:" protocol', () => { + let port: number; + let server: http.Server; + + beforeAll(async () => { + // setup target HTTP server + server = http.createServer(st(__dirname)); + await listen(server); + // @ts-expect-error `port` definitely exists + port = server.address().port; + }); + + afterAll(() => { + server.close(); + }); + + it('should work for HTTP endpoints', async () => { + const actual = await readFile(__filename, 'utf8'); + const stream = await getUri( + `http://127.0.0.1:${port}/${path.basename(__filename)}` + ); + const buf = await toBuffer(stream); + expect(buf.toString()).toEqual(actual); + }); + + it('should return ENOTFOUND for bad filenames', async () => { + await expect( + getUri(`http://127.0.0.1:${port}/does-not-exist`) + ).rejects.toHaveProperty('code', 'ENOTFOUND'); + }); + + it('should return ENOTMODIFIED for the same URI with `cache`', async () => { + const cache = await getUri( + `http://127.0.0.1:${port}/${path.basename(__filename)}` + ); + await expect( + getUri( + `http://127.0.0.1:${port}/${path.basename(__filename)}`, + { + cache, + } + ) + ).rejects.toHaveProperty('code', 'ENOTMODIFIED'); + }); + }); +}); diff --git a/packages/get-uri/test/https.test.ts b/packages/get-uri/test/https.test.ts new file mode 100644 index 00000000..ae63396f --- /dev/null +++ b/packages/get-uri/test/https.test.ts @@ -0,0 +1,67 @@ +import { readFile, readFileSync } from 'fs-extra'; +// @ts-expect-error no `@types/st` +import st from 'st'; +import path from 'path'; +import https from 'https'; +import { listen } from 'async-listen'; +import { getUri } from '../src'; +import { toBuffer } from './util'; + +describe('get-uri', () => { + describe('"https:" protocol', () => { + let port: number; + let server: https.Server; + + beforeAll(async () => { + // setup target HTTPS server + const options = { + key: readFileSync(`${__dirname}/server.key`), + cert: readFileSync(`${__dirname}/server.crt`), + }; + server = https.createServer(options, st(__dirname)); + await listen(server); + // @ts-expect-error `port` is definitely defined + port = server.address().port; + }); + + afterAll(() => { + server.close(); + }); + + it('should work for HTTPS endpoints', async () => { + const actual = await readFile(__filename, 'utf8'); + const stream = await getUri( + `https://127.0.0.1:${port}/${path.basename(__filename)}`, + { headers: { connection: 'close' }, rejectUnauthorized: false } + ); + const buf = await toBuffer(stream); + expect(buf.toString()).toEqual(actual); + }); + + it('should return ENOTFOUND for bad filenames', async () => { + await expect( + getUri(`https://127.0.0.1:${port}/does-not-exist`, { + headers: { connection: 'close' }, + rejectUnauthorized: false, + }) + ).rejects.toHaveProperty('code', 'ENOTFOUND'); + }); + + it('should return ENOTMODIFIED for the same URI with `cache`', async () => { + const cache = await getUri( + `https://127.0.0.1:${port}/${path.basename(__filename)}`, + { headers: { connection: 'close' }, rejectUnauthorized: false } + ); + await expect( + getUri( + `https://127.0.0.1:${port}/${path.basename(__filename)}`, + { + cache, + headers: { connection: 'close' }, + rejectUnauthorized: false, + } + ) + ).rejects.toHaveProperty('code', 'ENOTMODIFIED'); + }); + }); +}); diff --git a/packages/get-uri/test/redirect.test.ts b/packages/get-uri/test/redirect.test.ts new file mode 100644 index 00000000..a134a549 --- /dev/null +++ b/packages/get-uri/test/redirect.test.ts @@ -0,0 +1,80 @@ +// @ts-expect-error no `@types/st` +import st from 'st'; +import path from 'path'; +import http from 'http'; +import https from 'https'; +import { listen } from 'async-listen'; +import { readFile, readFileSync } from 'fs-extra'; +import { getUri } from '../src'; +import { toBuffer } from './util'; + +describe('get-uri', () => { + describe('http/https redirects', () => { + let httpServer: http.Server; + let httpsServer: https.Server; + let httpPort: number; + let httpsPort: number; + + beforeAll(async () => { + httpServer = http.createServer(); + await listen(httpServer); + // @ts-expect-error `port` is definitely a number + httpPort = httpServer.address().port; + }); + + beforeAll(async () => { + httpsServer = https.createServer({ + key: readFileSync(`${__dirname}/server.key`), + cert: readFileSync(`${__dirname}/server.crt`), + }); + await listen(httpsServer); + // @ts-expect-error `port` is definitely a number + httpsPort = httpsServer.address().port; + }); + + afterAll(() => { + httpsServer.close(); + httpServer.close(); + }); + + it('should handle http -> https redirect', async () => { + const dest = `https://127.0.0.1:${httpsPort}/${path.basename( + __filename + )}`; + httpsServer.once('request', st(__dirname)); + httpServer.once('request', (_req, res) => { + res.statusCode = 301; + res.setHeader('location', dest); + res.end('Moved'); + }); + + const actual = await readFile(__filename, 'utf8'); + const stream = await getUri( + `http://127.0.0.1:${httpPort}/${path.basename(__filename)}`, + { headers: { connection: 'close' }, rejectUnauthorized: false } + ); + const buf = await toBuffer(stream); + expect(buf.toString()).toEqual(actual); + }); + + it('should handle https -> http redirect', async () => { + const dest = `http://127.0.0.1:${httpPort}/${path.basename( + __filename + )}`; + httpServer.once('request', st(__dirname)); + httpsServer.once('request', (_req, res) => { + res.statusCode = 301; + res.setHeader('location', dest); + res.end('Moved'); + }); + + const actual = await readFile(__filename, 'utf8'); + const stream = await getUri( + `https://127.0.0.1:${httpsPort}/${path.basename(__filename)}`, + { headers: { connection: 'close' }, rejectUnauthorized: false } + ); + const buf = await toBuffer(stream); + expect(buf.toString()).toEqual(actual); + }); + }); +}); diff --git a/packages/get-uri/test/server.crt b/packages/get-uri/test/server.crt new file mode 100644 index 00000000..9580116c --- /dev/null +++ b/packages/get-uri/test/server.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICATCCAWoCCQCSMIVZI8DGgTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB +VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMB4XDTEzMDkwOTIyNTI1MVoXDTE0MDkwOTIyNTI1MVowRTELMAkG +A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0 +IFdpZGdpdHMgUHR5IEx0ZDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwQO2 +jlSxR12EvpF1hROxQAyQzDsxVv7AjDGhSVhizn7anxo5mCE9+5jHRJ6hgxF/3RJO +q157J49W7hlgiJfN3X4Q3WCqkTnfj1wFr8aSjWUl6TWeLMrhKZgzGCmZH0GV4Kpu +4jQ4lyjl/dIBw8HiJmKvaEagdUb5UJCKBDrDtvECAwEAATANBgkqhkiG9w0BAQUF +AAOBgQB33WAM5Yr2jkaeRog6rEglMC8i+Jab12amnFFJEMoWnH6csXVGSXxCtlX8 +FWnCoNb/D71dnEusS4JxbYluRg2Xrdfb/pmHje9pE2TTprZRBFAIoh4CmDh129Ka +HJwYPZi59XRnac8ghiF2l4d2yOQPznrJDekj6pfLVdIcVowSvg== +-----END CERTIFICATE----- diff --git a/packages/get-uri/test/server.key b/packages/get-uri/test/server.key new file mode 100644 index 00000000..25ade3cd --- /dev/null +++ b/packages/get-uri/test/server.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDBA7aOVLFHXYS+kXWFE7FADJDMOzFW/sCMMaFJWGLOftqfGjmY +IT37mMdEnqGDEX/dEk6rXnsnj1buGWCIl83dfhDdYKqROd+PXAWvxpKNZSXpNZ4s +yuEpmDMYKZkfQZXgqm7iNDiXKOX90gHDweImYq9oRqB1RvlQkIoEOsO28QIDAQAB +AoGBAJGXlm34bp0Rat9A46/VMd/JWrPjdo1TrrRRf4LO3AE9aPWYl5cshA+zp6QY +MGaonZWJiLP1mdo2YnFJzSpbr9mzEBEIjCsKdzeKbmnaEpCZY5YUj/ypVWYVJqXx +jZ6/9VEIxCrB9WmXi9fs97IZtZJcHI4M+0FXakjF9AmxtVvRAkEA5MNakvgLPn5s +GH5yuu0P0vSQ6d7EEgcM/89pjEpfKCvsYBh92VvmKspjBV71OuQ3Eh7/0GB/5UGC +gaJwID7ibQJBANf+wBky99/+ffzwrUGavIbLO4NOwnbQsz7v49PwJHoGIhlfoLW7 +21JwDwWUteFyYOwzHxRdKedolT5Ul+PxNBUCQCXYU7Ggq2uJSqS6toxKD6Yco6St +H87Dr9jaHWICI7/nlFFJe/hrhaZqmPsYfIVjn+C1lCiK7l2k+swrbVVIUfkCQQCA +8MgWgvGsWw00+SxElK3kveAaI+M88JuAf85+z8XGvnCOuyKCOtHT5adiCoOFQTWQ +63erPW5tgWZOnktKPMx9AkEAru3G68AjJN6e14aHkK9KFD0DV1RjrIe7E5iQq5Tn +QXyiyiu624him6pov6UIGs5429z+gl3JjRM3A2rl//j//w== +-----END RSA PRIVATE KEY----- diff --git a/packages/get-uri/test/test.test.ts b/packages/get-uri/test/test.test.ts new file mode 100644 index 00000000..69811ea7 --- /dev/null +++ b/packages/get-uri/test/test.test.ts @@ -0,0 +1,23 @@ +/** + * Module dependencies. + */ + +import { getUri } from '../src'; + +describe('get-uri', () => { + it('should throw a TypeError when no URI is given', async () => { + await expect(getUri('')).rejects.toThrow( + 'Must pass in a URI to "getUri()"' + ); + }); + + it('should throw a TypeError when no protocol is specified', async () => { + await expect(getUri('://bad')).rejects.toThrow('Invalid URL'); + }); + + it('should throw a TypeError when an unsupported protocol is specified', async () => { + await expect(getUri('bad://bad')).rejects.toThrow( + 'Unsupported protocol "bad" specified in URI: "bad://bad"' + ); + }); +}); diff --git a/packages/get-uri/test/tsconfig.json b/packages/get-uri/test/tsconfig.json new file mode 100644 index 00000000..e516b7fa --- /dev/null +++ b/packages/get-uri/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["**/*.ts"] +} diff --git a/packages/get-uri/test/util.ts b/packages/get-uri/test/util.ts new file mode 100644 index 00000000..ac97bc73 --- /dev/null +++ b/packages/get-uri/test/util.ts @@ -0,0 +1,9 @@ +import { Readable } from 'stream'; + +export async function toBuffer(stream: Readable): Promise { + const chunks: Buffer[] = []; + for await (const chunk of stream) { + chunks.push(chunk); + } + return Buffer.concat(chunks); +} diff --git a/tsconfig.json b/packages/get-uri/tsconfig.json similarity index 93% rename from tsconfig.json rename to packages/get-uri/tsconfig.json index 63692a77..d43bf6a4 100644 --- a/tsconfig.json +++ b/packages/get-uri/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "strict": true, "module": "CommonJS", - "target": "es2015", + "target": "es2020", "esModuleInterop": true, "lib": ["esnext"], "outDir": "dist", diff --git a/packages/http-proxy-agent/.eslintignore b/packages/http-proxy-agent/.eslintignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/packages/http-proxy-agent/.eslintignore @@ -0,0 +1 @@ +dist diff --git a/packages/http-proxy-agent/LICENSE b/packages/http-proxy-agent/LICENSE new file mode 100644 index 00000000..aad14057 --- /dev/null +++ b/packages/http-proxy-agent/LICENSE @@ -0,0 +1,25 @@ +License +------- + +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/http-proxy-agent/README.md b/packages/http-proxy-agent/README.md new file mode 100644 index 00000000..d60e2066 --- /dev/null +++ b/packages/http-proxy-agent/README.md @@ -0,0 +1,74 @@ +http-proxy-agent +================ +### An HTTP(s) proxy `http.Agent` implementation for HTTP +[![Build Status](https://github.com/TooTallNate/node-http-proxy-agent/workflows/Node%20CI/badge.svg)](https://github.com/TooTallNate/node-http-proxy-agent/actions?workflow=Node+CI) + +This module provides an `http.Agent` implementation that connects to a specified +HTTP or HTTPS proxy server, and can be used with the built-in `http` module. + +__Note:__ For HTTP proxy usage with the `https` module, check out +[`node-https-proxy-agent`](https://github.com/TooTallNate/node-https-proxy-agent). + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install http-proxy-agent +``` + + +Example +------- + +``` js +var url = require('url'); +var http = require('http'); +var HttpProxyAgent = require('http-proxy-agent'); + +// HTTP/HTTPS proxy to connect to +var proxy = process.env.http_proxy || 'http://168.63.76.32:3128'; +console.log('using proxy server %j', proxy); + +// HTTP endpoint for the proxy to connect to +var endpoint = process.argv[2] || 'http://nodejs.org/api/'; +console.log('attempting to GET %j', endpoint); +var opts = url.parse(endpoint); + +// create an instance of the `HttpProxyAgent` class with the proxy server information +var agent = new HttpProxyAgent(proxy); +opts.agent = agent; + +http.get(opts, function (res) { + console.log('"response" event!', res.headers); + res.pipe(process.stdout); +}); +``` + + +License +------- + +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/http-proxy-agent/package.json b/packages/http-proxy-agent/package.json new file mode 100644 index 00000000..50cfa5db --- /dev/null +++ b/packages/http-proxy-agent/package.json @@ -0,0 +1,47 @@ +{ + "name": "http-proxy-agent", + "version": "5.0.0", + "description": "An HTTP(s) proxy `http.Agent` implementation for HTTP", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "test": "mocha", + "lint": "eslint . --ext .ts", + "prepublishOnly": "npm run build" + }, + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/node-http-proxy-agent.git" + }, + "keywords": [ + "http", + "proxy", + "endpoint", + "agent" + ], + "author": "Nathan Rajlich (http://n8.io/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/TooTallNate/node-http-proxy-agent/issues" + }, + "dependencies": { + "@tootallnate/once": "^2.0.0", + "agent-base": "^6.0.2", + "debug": "^4.3.4" + }, + "devDependencies": { + "@types/debug": "^4.1.7", + "@types/node": "^14.18.43", + "mocha": "^6.2.3", + "proxy": "workspace:*", + "tsconfig": "workspace:*", + "typescript": "^5.0.4" + }, + "engines": { + "node": ">= 14" + } +} diff --git a/packages/http-proxy-agent/src/index.ts b/packages/http-proxy-agent/src/index.ts new file mode 100644 index 00000000..8f320148 --- /dev/null +++ b/packages/http-proxy-agent/src/index.ts @@ -0,0 +1,136 @@ +import * as net from 'net'; +import * as tls from 'tls'; +import * as http from 'http'; +import createDebug from 'debug'; +import once from '@tootallnate/once'; +import { Agent, AgentConnectOpts } from 'agent-base'; + +const debug = createDebug('http-proxy-agent'); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type Protocol = T extends `${infer Protocol}:${infer _}` ? Protocol : never; + +type ConnectOptsMap = { + http: Omit; + https: Omit; +} + +export type HttpProxyAgentOptions = { + [P in keyof ConnectOptsMap]: Protocol extends P + ? ConnectOptsMap[P] + : never; +}[keyof ConnectOptsMap]; + +interface HttpProxyAgentClientRequest extends http.ClientRequest { + outputData?: { + data: string; + }[]; + _header?: string | null; + _implicitHeader(): void; +} + +function isHTTPS(protocol?: string | null): boolean { + return typeof protocol === 'string' ? /^https:?$/i.test(protocol) : false; +} + +/** + * The `HttpProxyAgent` implements an HTTP Agent subclass that connects + * to the specified "HTTP proxy server" in order to proxy HTTP requests. + */ +export class HttpProxyAgent extends Agent { + static protocols = ['http', 'https'] as const; + + readonly proxy: URL; + connectOpts: net.TcpNetConnectOpts & tls.ConnectionOptions; + + get secureProxy() { + return isHTTPS(this.proxy.protocol); + } + + constructor(proxy: Uri | URL, opts?: HttpProxyAgentOptions) { + super(); + this.proxy = typeof proxy === 'string' ? new URL(proxy) : proxy; + debug('Creating new HttpProxyAgent instance: %o', this.proxy.href); + + // Trim off the brackets from IPv6 addresses + const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, ''); + const port = this.proxy.port + ? parseInt(this.proxy.port, 10) + : this.secureProxy + ? 443 + : 80; + this.connectOpts = { + ...opts, + host, + port, + }; + } + + async connect( + req: HttpProxyAgentClientRequest, + opts: AgentConnectOpts + ): Promise { + const { proxy } = this; + + const protocol = opts.secureEndpoint ? 'https:' : 'http:'; + const hostname = req.getHeader('host') || 'localhost'; + const base = `${protocol}//${hostname}`; + const url = new URL(req.path, base); + if (opts.port !== 80) { + url.port = String(opts.port); + } + + // Change the `http.ClientRequest` instance's "path" field + // to the absolute path of the URL that will be requested. + req.path = String(url); + + // Inject the `Proxy-Authorization` header if necessary. + req._header = null; + if (proxy.username || proxy.password) { + const auth = `${decodeURIComponent( + proxy.username + )}:${decodeURIComponent(proxy.password)}`; + req.setHeader( + 'Proxy-Authorization', + `Basic ${Buffer.from(auth).toString('base64')}` + ); + } + + // Create a socket connection to the proxy server. + let socket: net.Socket; + if (this.secureProxy) { + debug('Creating `tls.Socket`: %o', this.connectOpts); + socket = tls.connect(this.connectOpts); + } else { + debug('Creating `net.Socket`: %o', this.connectOpts); + socket = net.connect(this.connectOpts); + } + + // At this point, the http ClientRequest's internal `_header` field + // might have already been set. If this is the case then we'll need + // to re-generate the string since we just changed the `req.path`. + let first: string; + let endOfHeaders: number; + debug('Regenerating stored HTTP header string for request'); + req._implicitHeader(); + if (req.outputData && req.outputData.length > 0) { + // Node >= 12 + debug( + 'Patching connection write() output buffer with updated header' + ); + first = req.outputData[0].data; + endOfHeaders = first.indexOf('\r\n\r\n') + 4; + req.outputData[0].data = + req._header + first.substring(endOfHeaders); + debug('Output buffer: %o', req.outputData[0].data); + } + + // Wait for the socket's `connect` event, so that this `callback()` + // function throws instead of the `http` request machinery. This is + // important for i.e. `PacProxyAgent` which determines a failed proxy + // connection via the `callback()` function throwing. + await once(socket, 'connect'); + + return socket; + } +} diff --git a/packages/http-proxy-agent/test/ssl-cert-snakeoil.key b/packages/http-proxy-agent/test/ssl-cert-snakeoil.key new file mode 100644 index 00000000..fd125012 --- /dev/null +++ b/packages/http-proxy-agent/test/ssl-cert-snakeoil.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCzURxIqzer0ACAbX/lHdsn4Gd9PLKrf7EeDYfIdV0HZKPD8WDr +bBx2/fBu0OW2sjnzv/SVZbJ0DAuPE/p0+eT0qb2qC10iz9iTD7ribd7gxhirVb8y +b3fBjXsxc8V8p4Ny1LcvNSqCjwUbJqdRogfoJeTiqPM58z5sNzuv5iq7iwIDAQAB +AoGAPMQy4olrP0UotlzlJ36bowLP70ffgHCwU+/f4NWs5fF78c3du0oSx1w820Dd +Z7E0JF8bgnlJJTxjumPZz0RUCugrEHBKJmzEz3cxF5E3+7NvteZcjKn9D67RrM5x +1/uSZ9cqKE9cYvY4fSuHx18diyZ4axR/wB1Pea2utjjDM+ECQQDb9ZbmmaWMiRpQ +5Up+loxP7BZNPsEVsm+DVJmEFbaFgGfncWBqSIqnPNjMwTwj0OigTwCAEGPkfRVW +T0pbYWCxAkEA0LK7SCTwzyDmhASUalk0x+3uCAA6ryFdwJf/wd8TRAvVOmkTEldX +uJ7ldLvfrONYO3v56uKTU/SoNdZYzKtO+wJAX2KM4ctXYy5BXztPpr2acz4qHa1N +Bh+vBAC34fOYhyQ76r3b1btHhWZ5jbFuZwm9F2erC94Ps5IaoqcX07DSwQJAPKGw +h2U0EPkd/3zVIZCJJQya+vgWFIs9EZcXVtvYXQyTBkVApTN66MhBIYjzkub5205J +bVQmOV37AKklY1DhwQJAA1wos0cYxro02edzatxd0DIR2r4qqOqLkw6BhYHhq6HJ +ZvIcQkHqdSXzdETFc01I1znDGGIrJHcnvKWgBPoEUg== +-----END RSA PRIVATE KEY----- diff --git a/packages/http-proxy-agent/test/ssl-cert-snakeoil.pem b/packages/http-proxy-agent/test/ssl-cert-snakeoil.pem new file mode 100644 index 00000000..b115a5e9 --- /dev/null +++ b/packages/http-proxy-agent/test/ssl-cert-snakeoil.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB1TCCAT4CCQDV5mPlzm9+izANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDEyQ3 +NTI3YmQ3Ny1hYjNlLTQ3NGItYWNlNy1lZWQ2MDUzOTMxZTcwHhcNMTUwNzA2MjI0 +NTA3WhcNMjUwNzAzMjI0NTA3WjAvMS0wKwYDVQQDEyQ3NTI3YmQ3Ny1hYjNlLTQ3 +NGItYWNlNy1lZWQ2MDUzOTMxZTcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +ALNRHEirN6vQAIBtf+Ud2yfgZ308sqt/sR4Nh8h1XQdko8PxYOtsHHb98G7Q5bay +OfO/9JVlsnQMC48T+nT55PSpvaoLXSLP2JMPuuJt3uDGGKtVvzJvd8GNezFzxXyn +g3LUty81KoKPBRsmp1GiB+gl5OKo8znzPmw3O6/mKruLAgMBAAEwDQYJKoZIhvcN +AQEFBQADgYEACzoHUF8UV2Z6541Q2wKEA0UFUzmUjf/E1XwBO+1P15ZZ64uw34B4 +1RwMPtAo9RY/PmICTWtNxWGxkzwb2JtDWtnxVER/lF8k2XcXPE76fxTHJF/BKk9J +QU8OTD1dd9gHCBviQB9TqntRZ5X7axjtuWjb2umY+owBYzAHZkp1HKI= +-----END CERTIFICATE----- diff --git a/packages/http-proxy-agent/test/test.js b/packages/http-proxy-agent/test/test.js new file mode 100644 index 00000000..8170ac9e --- /dev/null +++ b/packages/http-proxy-agent/test/test.js @@ -0,0 +1,333 @@ +const fs = require('fs'); +const url = require('url'); +const http = require('http'); +const https = require('https'); +const assert = require('assert'); +const { createProxy } = require('proxy'); +const { Agent } = require('agent-base'); +const { HttpProxyAgent } = require('../'); + +const sleep = (n) => new Promise((r) => setTimeout(r, n)); + +describe('HttpProxyAgent', () => { + let server; + let serverPort; + + let proxy; + let proxyPort; + + let sslProxy; + let sslProxyPort; + + before((done) => { + // setup HTTP proxy server + proxy = createProxy(); + proxy.listen(() => { + proxyPort = proxy.address().port; + done(); + }); + }); + + before((done) => { + // setup target HTTP server + server = http.createServer(); + server.listen(() => { + serverPort = server.address().port; + done(); + }); + }); + + beforeEach(() => { + server.removeAllListeners('request'); + }); + + before((done) => { + // setup SSL HTTP proxy server + let options = { + key: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.key`), + cert: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.pem`), + }; + sslProxy = createProxy(https.createServer(options)); + sslProxy.listen(() => { + sslProxyPort = sslProxy.address().port; + done(); + }); + }); + + // shut down test HTTP server + after((done) => { + proxy.once('close', () => { + done(); + }); + proxy.close(); + }); + + after((done) => { + server.once('close', () => { + done(); + }); + server.close(); + }); + + after((done) => { + sslProxy.once('close', () => { + done(); + }); + sslProxy.close(); + }); + + describe('constructor', () => { + it('should throw an Error if no "proxy" argument is given', () => { + assert.throws(() => { + new HttpProxyAgent(); + }); + }); + it('should accept a "string" proxy argument', () => { + let agent = new HttpProxyAgent(`http://127.0.0.1:${proxyPort}`); + assert.equal('127.0.0.1', agent.proxy.hostname); + assert.equal(proxyPort, agent.proxy.port); + }); + it('should accept a `URL` instance proxy argument', () => { + let agent = new HttpProxyAgent( + new URL(`http://127.0.0.1:${proxyPort}`) + ); + assert.equal('127.0.0.1', agent.proxy.hostname); + assert.equal(proxyPort, agent.proxy.port); + }); + it('should set a `defaultPort` property', () => { + let opts = url.parse(`http://127.0.0.1:${proxyPort}`); + let agent = new HttpProxyAgent(opts); + assert.equal(80, agent.defaultPort); + }); + describe('secureProxy', () => { + it('should be `false` when "http:" protocol is used', () => { + let agent = new HttpProxyAgent(`http://127.0.0.1:${proxyPort}`); + assert.equal(false, agent.secureProxy); + }); + it('should be `true` when "https:" protocol is used', () => { + let agent = new HttpProxyAgent(`https://127.0.0.1:${proxyPort}`); + assert.equal(true, agent.secureProxy); + }); + }); + }); + + describe('"http" module', () => { + it('should work over an HTTP proxy', (done) => { + // set HTTP "request" event handler for this test + server.once('request', (req, res) => { + res.end(JSON.stringify(req.headers)); + }); + + let proxy = `http://127.0.0.1:${proxyPort}`; + let agent = new HttpProxyAgent(proxy); + + let opts = url.parse(`http://127.0.0.1:${serverPort}`); + opts.agent = agent; + + http.get(opts, (res) => { + let data = ''; + res.setEncoding('utf8'); + res.on('data', (b) => { + data += b; + }); + res.on('end', () => { + data = JSON.parse(data); + assert.equal(`127.0.0.1:${serverPort}`, data.host); + assert('via' in data); + done(); + }); + }); + }); + it('should work over an HTTPS proxy', (done) => { + // set HTTP "request" event handler for this test + server.once('request', (req, res) => { + res.end(JSON.stringify(req.headers)); + }); + + let proxy = + process.env.HTTPS_PROXY || + process.env.https_proxy || + `https://127.0.0.1:${sslProxyPort}`; + let agent = new HttpProxyAgent(proxy, { + rejectUnauthorized: false, + }); + assert.equal(true, agent.secureProxy); + + http.get(`http://127.0.0.1:${serverPort}`, { agent }, (res) => { + let data = ''; + res.setEncoding('utf8'); + res.on('data', (b) => { + data += b; + }); + res.on('end', () => { + data = JSON.parse(data); + assert.equal(`127.0.0.1:${serverPort}`, data.host); + assert('via' in data); + done(); + }); + }); + }); + it('should proxy the query string of the request path', (done) => { + // set HTTP "request" event handler for this test + server.once('request', (req, res) => { + res.end( + JSON.stringify({ + url: req.url, + }) + ); + }); + + let proxy = `http://127.0.0.1:${proxyPort}`; + let agent = new HttpProxyAgent(proxy); + + let opts = url.parse( + `http://127.0.0.1:${serverPort}/test?foo=bar&1=2` + ); + opts.agent = agent; + + http.get(opts, (res) => { + let data = ''; + res.setEncoding('utf8'); + res.on('data', (b) => { + data += b; + }); + res.on('end', () => { + data = JSON.parse(data); + assert.equal('/test?foo=bar&1=2', data.url); + done(); + }); + }); + }); + it('should receive the 407 authorization code on the `http.ClientResponse`', (done) => { + // reject all requests + proxy.authenticate = () => false + + let proxyUri = `http://127.0.0.1:${proxyPort}`; + let agent = new HttpProxyAgent(proxyUri); + + let opts = {}; + // `host` and `port` don't really matter since the proxy will reject anyways + opts.host = '127.0.0.1'; + opts.port = 80; + opts.agent = agent; + + http.get(opts, (res) => { + assert.equal(407, res.statusCode); + assert('proxy-authenticate' in res.headers); + delete proxy.authenticate; + done(); + }); + }); + it('should send the "Proxy-Authorization" request header', (done) => { + // set a proxy authentication function for this test + proxy.authenticate = (req) => { + // username:password is "foo:bar" + return req.headers['proxy-authorization'] === 'Basic Zm9vOmJhcg==' + }; + + // set HTTP "request" event handler for this test + server.once('request', (req, res) => { + res.end(JSON.stringify(req.headers)); + }); + + let proxyUri = `http://foo:bar@127.0.0.1:${proxyPort}`; + let agent = new HttpProxyAgent(proxyUri); + + let opts = url.parse(`http://127.0.0.1:${serverPort}`); + opts.agent = agent; + + http.get(opts, (res) => { + let data = ''; + res.setEncoding('utf8'); + res.on('data', (b) => { + data += b; + }); + res.on('end', () => { + data = JSON.parse(data); + assert.equal(`127.0.0.1:${serverPort}`, data.host); + assert('via' in data); + delete proxy.authenticate; + done(); + }); + }); + }); + it('should emit an "error" event on the `http.ClientRequest` if the proxy does not exist', (done) => { + // port 4 is a reserved, but "unassigned" port + let proxyUri = 'http://127.0.0.1:4'; + let agent = new HttpProxyAgent(proxyUri); + + let opts = url.parse('http://nodejs.org'); + opts.agent = agent; + + let req = http.get(opts); + req.once('error', (err) => { + assert.equal('ECONNREFUSED', err.code); + req.abort(); + done(); + }); + }); + it('should work after the first tick of the `http.ClientRequest` instance', (done) => { + // set HTTP "request" event handler for this test + server.once('request', (req, res) => { + res.end(JSON.stringify(req.url)); + }); + + let proxy = `http://127.0.0.1:${proxyPort}`; + let httpProxyAgent = new HttpProxyAgent(proxy); + + // Defer the "connect()" function logic, since calling `req.end()` + // before the socket is returned causes the HTTP header to be + // generated *before* `HttpProxyAgent` can patch the `req.path` + // property, making the header incorrect. + class SleepAgent extends Agent { + async connect(_req, opts) { + assert.equal(opts.secureEndpoint, false); + assert.equal(opts.protocol, 'http:'); + await sleep(10); + return httpProxyAgent; + } + } + const sleepAgent = new SleepAgent(); + + http.get( + `http://127.0.0.1:${serverPort}/test`, + { agent: sleepAgent }, + (res) => { + let data = ''; + res.setEncoding('utf8'); + res.on('data', (b) => { + data += b; + }); + res.on('end', () => { + data = JSON.parse(data); + assert.equal('/test', data); + done(); + }); + } + ); + }); + it('should not send a port number for the default port', (done) => { + server.once('request', (req, res) => { + res.end(JSON.stringify(req.headers)); + }); + let proxy = `http://127.0.0.1:${proxyPort}`; + proxy = url.parse(proxy); + let agent = new HttpProxyAgent(proxy); + agent.defaultPort = serverPort; + let opts = url.parse(`http://127.0.0.1:${serverPort}`); + opts.agent = agent; + http.get(opts, (res) => { + let data = ''; + res.setEncoding('utf8'); + res.on('data', (b) => { + data += b; + }); + res.on('end', () => { + data = JSON.parse(data); + assert.equal('127.0.0.1', data.host); + done(); + }); + }); + }); + }); +}); diff --git a/packages/http-proxy-agent/tsconfig.json b/packages/http-proxy-agent/tsconfig.json new file mode 100644 index 00000000..24b6cf7e --- /dev/null +++ b/packages/http-proxy-agent/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/README.md b/packages/https-proxy-agent/README.md similarity index 100% rename from README.md rename to packages/https-proxy-agent/README.md diff --git a/packages/https-proxy-agent/jest.config.js b/packages/https-proxy-agent/jest.config.js new file mode 100644 index 00000000..ee66e76e --- /dev/null +++ b/packages/https-proxy-agent/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/packages/https-proxy-agent/package.json b/packages/https-proxy-agent/package.json new file mode 100644 index 00000000..87364597 --- /dev/null +++ b/packages/https-proxy-agent/package.json @@ -0,0 +1,49 @@ +{ + "name": "https-proxy-agent", + "version": "5.0.1", + "description": "An HTTP(s) proxy `http.Agent` implementation for HTTPS", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "test": "jest --env node --verbose --bail", + "lint": "eslint src --ext .js,.ts", + "prepublishOnly": "npm run build" + }, + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/node-https-proxy-agent.git" + }, + "keywords": [ + "https", + "proxy", + "endpoint", + "agent" + ], + "author": "Nathan Rajlich (http://n8.io/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/TooTallNate/node-https-proxy-agent/issues" + }, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "4" + }, + "devDependencies": { + "@types/debug": "4", + "@types/jest": "^29.5.1", + "@types/node": "^14.18.43", + "async-listen": "^2.1.0", + "jest": "^29.5.0", + "proxy": "workspace:*", + "ts-jest": "^29.1.0", + "tsconfig": "workspace:*", + "typescript": "^5.0.4" + }, + "engines": { + "node": ">= 14" + } +} diff --git a/src/agent.ts b/packages/https-proxy-agent/src/index.ts similarity index 53% rename from src/agent.ts rename to packages/https-proxy-agent/src/index.ts index f2014adc..fdeacf2b 100644 --- a/src/agent.ts +++ b/packages/https-proxy-agent/src/index.ts @@ -1,14 +1,31 @@ -import net from 'net'; -import tls from 'tls'; -import url from 'url'; +import * as net from 'net'; +import * as tls from 'tls'; +import * as http from 'http'; import assert from 'assert'; import createDebug from 'debug'; import { OutgoingHttpHeaders } from 'http'; -import { Agent, ClientRequest, RequestOptions } from 'agent-base'; -import { HttpsProxyAgentOptions } from '.'; +import { Agent, AgentConnectOpts } from 'agent-base'; import parseProxyResponse from './parse-proxy-response'; -const debug = createDebug('https-proxy-agent:agent'); +const debug = createDebug('https-proxy-agent'); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type Protocol = T extends `${infer Protocol}:${infer _}` ? Protocol : never; + +type ConnectOptsMap = { + http: Omit; + https: Omit; +} + +type ConnectOpts = { + [P in keyof ConnectOptsMap]: Protocol extends P + ? ConnectOptsMap[P] + : never; +}[keyof ConnectOptsMap]; + +export type HttpsProxyAgentOptions = ConnectOpts & { + headers?: OutgoingHttpHeaders; +}; /** * The `HttpsProxyAgent` implements an HTTP Agent subclass that connects to @@ -21,103 +38,85 @@ const debug = createDebug('https-proxy-agent:agent'); * * `https:` requests have their socket connection upgraded to TLS once * the connection to the proxy server has been established. - * - * @api public */ -export default class HttpsProxyAgent extends Agent { - private secureProxy: boolean; - private proxy: HttpsProxyAgentOptions; - - constructor(_opts: string | HttpsProxyAgentOptions) { - let opts: HttpsProxyAgentOptions; - if (typeof _opts === 'string') { - opts = url.parse(_opts); - } else { - opts = _opts; - } - if (!opts) { - throw new Error( - 'an HTTP(S) proxy server `host` and `port` must be specified!' - ); - } - debug('creating new HttpsProxyAgent instance: %o', opts); - super(opts); - - const proxy: HttpsProxyAgentOptions = { ...opts }; +export class HttpsProxyAgent extends Agent { + static protocols = ["http", "https"] as const; - // If `true`, then connect to the proxy server over TLS. - // Defaults to `false`. - this.secureProxy = opts.secureProxy || isHTTPS(proxy.protocol); + readonly proxy: URL; + proxyHeaders: OutgoingHttpHeaders; + connectOpts: net.TcpNetConnectOpts & tls.ConnectionOptions; - // Prefer `hostname` over `host`, and set the `port` if needed. - proxy.host = proxy.hostname || proxy.host; - if (typeof proxy.port === 'string') { - proxy.port = parseInt(proxy.port, 10); - } - if (!proxy.port && proxy.host) { - proxy.port = this.secureProxy ? 443 : 80; - } - - // ALPN is supported by Node.js >= v5. - // attempt to negotiate http/1.1 for proxy servers that support http/2 - if (this.secureProxy && !('ALPNProtocols' in proxy)) { - proxy.ALPNProtocols = ['http 1.1']; - } - - if (proxy.host && proxy.path) { - // If both a `host` and `path` are specified then it's most likely - // the result of a `url.parse()` call... we need to remove the - // `path` portion so that `net.connect()` doesn't attempt to open - // that as a Unix socket file. - delete proxy.path; - delete proxy.pathname; - } + get secureProxy() { + return isHTTPS(this.proxy.protocol); + } - this.proxy = proxy; + constructor(proxy: Uri | URL, opts?: HttpsProxyAgentOptions) { + super(); + this.proxy = typeof proxy === "string" ? new URL(proxy) : proxy; + this.proxyHeaders = opts?.headers ?? {}; + debug("Creating new HttpsProxyAgent instance: %o", this.proxy.href); + + // Trim off the brackets from IPv6 addresses + const host = (this.proxy.hostname || this.proxy.host).replace(/^\[|\]$/g, ''); + const port = this.proxy.port + ? parseInt(this.proxy.port, 10) + : this.secureProxy + ? 443 + : 80; + this.connectOpts = { + ...(opts ? omit(opts, "headers") : null), + host, + port, + }; } /** * Called when the node-core HTTP client library is creating a * new HTTP request. - * - * @api protected */ - async callback( - req: ClientRequest, - opts: RequestOptions + async connect( + req: http.ClientRequest, + opts: AgentConnectOpts ): Promise { const { proxy, secureProxy } = this; + if (!opts.host) { + throw new TypeError('No "host" provided'); + } + // Create a socket connection to the proxy server. let socket: net.Socket; if (secureProxy) { - debug('Creating `tls.Socket`: %o', proxy); - socket = tls.connect(proxy as tls.ConnectionOptions); + debug("Creating `tls.Socket`: %o", this.connectOpts); + socket = tls.connect(this.connectOpts); } else { - debug('Creating `net.Socket`: %o', proxy); - socket = net.connect(proxy as net.NetConnectOpts); + debug("Creating `net.Socket`: %o", this.connectOpts); + socket = net.connect(this.connectOpts); } - const headers: OutgoingHttpHeaders = { ...proxy.headers }; - const hostname = `${opts.host}:${opts.port}`; - let payload = `CONNECT ${hostname} HTTP/1.1\r\n`; + const headers: OutgoingHttpHeaders = { ...this.proxyHeaders }; + let host = net.isIPv6(opts.host) ? `[${opts.host}]` : opts.host; + let payload = `CONNECT ${host}:${opts.port} HTTP/1.1\r\n`; // Inject the `Proxy-Authorization` header if necessary. - if (proxy.auth) { - headers['Proxy-Authorization'] = `Basic ${Buffer.from( - proxy.auth - ).toString('base64')}`; + if (proxy.username || proxy.password) { + const auth = `${decodeURIComponent( + proxy.username + )}:${decodeURIComponent(proxy.password)}`; + headers["Proxy-Authorization"] = `Basic ${Buffer.from( + auth + ).toString("base64")}`; } // The `Host` header should only include the port // number when it is not the default port. - let { host, port, secureEndpoint } = opts; + const { port, secureEndpoint } = opts; if (!isDefaultPort(port, secureEndpoint)) { host += `:${port}`; } headers.Host = host; - headers.Connection = 'close'; + headers.Connection = "close"; for (const name of Object.keys(headers)) { payload += `${name}: ${headers[name]}\r\n`; } @@ -126,24 +125,26 @@ export default class HttpsProxyAgent extends Agent { socket.write(`${payload}\r\n`); - const { - statusCode, - buffered - } = await proxyResponsePromise; + const { statusCode, buffered } = await proxyResponsePromise; if (statusCode === 200) { - req.once('socket', resume); + req.once("socket", resume); if (opts.secureEndpoint) { // The proxy is connecting to a TLS server, so upgrade // this socket connection to a TLS connection. - debug('Upgrading socket connection to TLS'); + debug("Upgrading socket connection to TLS"); const servername = opts.servername || opts.host; - return tls.connect({ - ...omit(opts, 'host', 'hostname', 'path', 'port'), + const s = tls.connect({ + ...omit(opts, "host", "path", "port"), socket, - servername + servername, }); + //console.log(s); + + //s.write('GET /foo HTTP/1.1\r\n\r\n'); + //await new Promise(r => setTimeout(r, 5000)); + return s; } return socket; @@ -166,9 +167,9 @@ export default class HttpsProxyAgent extends Agent { fakeSocket.readable = true; // Need to wait for the "socket" event to re-play the "data" events. - req.once('socket', (s: net.Socket) => { - debug('replaying proxy buffer for failed request'); - assert(s.listenerCount('data') > 0); + req.once("socket", (s: net.Socket) => { + debug("Replaying proxy buffer for failed request"); + assert(s.listenerCount("data") > 0); // Replay the "buffered" Buffer onto the fake `socket`, since at // this point the HTTP module machinery has been hooked up for diff --git a/src/parse-proxy-response.ts b/packages/https-proxy-agent/src/parse-proxy-response.ts similarity index 98% rename from src/parse-proxy-response.ts rename to packages/https-proxy-agent/src/parse-proxy-response.ts index 6791775c..fc57ff6a 100644 --- a/src/parse-proxy-response.ts +++ b/packages/https-proxy-agent/src/parse-proxy-response.ts @@ -67,9 +67,10 @@ export default function parseProxyResponse( ); const statusCode = +firstLine.split(' ')[1]; debug('got proxy server response: %o', firstLine); + cleanup(); resolve({ statusCode, - buffered + buffered, }); } diff --git a/packages/https-proxy-agent/test/ssl-cert-snakeoil.key b/packages/https-proxy-agent/test/ssl-cert-snakeoil.key new file mode 100644 index 00000000..fd125012 --- /dev/null +++ b/packages/https-proxy-agent/test/ssl-cert-snakeoil.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCzURxIqzer0ACAbX/lHdsn4Gd9PLKrf7EeDYfIdV0HZKPD8WDr +bBx2/fBu0OW2sjnzv/SVZbJ0DAuPE/p0+eT0qb2qC10iz9iTD7ribd7gxhirVb8y +b3fBjXsxc8V8p4Ny1LcvNSqCjwUbJqdRogfoJeTiqPM58z5sNzuv5iq7iwIDAQAB +AoGAPMQy4olrP0UotlzlJ36bowLP70ffgHCwU+/f4NWs5fF78c3du0oSx1w820Dd +Z7E0JF8bgnlJJTxjumPZz0RUCugrEHBKJmzEz3cxF5E3+7NvteZcjKn9D67RrM5x +1/uSZ9cqKE9cYvY4fSuHx18diyZ4axR/wB1Pea2utjjDM+ECQQDb9ZbmmaWMiRpQ +5Up+loxP7BZNPsEVsm+DVJmEFbaFgGfncWBqSIqnPNjMwTwj0OigTwCAEGPkfRVW +T0pbYWCxAkEA0LK7SCTwzyDmhASUalk0x+3uCAA6ryFdwJf/wd8TRAvVOmkTEldX +uJ7ldLvfrONYO3v56uKTU/SoNdZYzKtO+wJAX2KM4ctXYy5BXztPpr2acz4qHa1N +Bh+vBAC34fOYhyQ76r3b1btHhWZ5jbFuZwm9F2erC94Ps5IaoqcX07DSwQJAPKGw +h2U0EPkd/3zVIZCJJQya+vgWFIs9EZcXVtvYXQyTBkVApTN66MhBIYjzkub5205J +bVQmOV37AKklY1DhwQJAA1wos0cYxro02edzatxd0DIR2r4qqOqLkw6BhYHhq6HJ +ZvIcQkHqdSXzdETFc01I1znDGGIrJHcnvKWgBPoEUg== +-----END RSA PRIVATE KEY----- diff --git a/packages/https-proxy-agent/test/ssl-cert-snakeoil.pem b/packages/https-proxy-agent/test/ssl-cert-snakeoil.pem new file mode 100644 index 00000000..b115a5e9 --- /dev/null +++ b/packages/https-proxy-agent/test/ssl-cert-snakeoil.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB1TCCAT4CCQDV5mPlzm9+izANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDEyQ3 +NTI3YmQ3Ny1hYjNlLTQ3NGItYWNlNy1lZWQ2MDUzOTMxZTcwHhcNMTUwNzA2MjI0 +NTA3WhcNMjUwNzAzMjI0NTA3WjAvMS0wKwYDVQQDEyQ3NTI3YmQ3Ny1hYjNlLTQ3 +NGItYWNlNy1lZWQ2MDUzOTMxZTcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +ALNRHEirN6vQAIBtf+Ud2yfgZ308sqt/sR4Nh8h1XQdko8PxYOtsHHb98G7Q5bay +OfO/9JVlsnQMC48T+nT55PSpvaoLXSLP2JMPuuJt3uDGGKtVvzJvd8GNezFzxXyn +g3LUty81KoKPBRsmp1GiB+gl5OKo8znzPmw3O6/mKruLAgMBAAEwDQYJKoZIhvcN +AQEFBQADgYEACzoHUF8UV2Z6541Q2wKEA0UFUzmUjf/E1XwBO+1P15ZZ64uw34B4 +1RwMPtAo9RY/PmICTWtNxWGxkzwb2JtDWtnxVER/lF8k2XcXPE76fxTHJF/BKk9J +QU8OTD1dd9gHCBviQB9TqntRZ5X7axjtuWjb2umY+owBYzAHZkp1HKI= +-----END CERTIFICATE----- diff --git a/packages/https-proxy-agent/test/test.ts b/packages/https-proxy-agent/test/test.ts new file mode 100644 index 00000000..08835174 --- /dev/null +++ b/packages/https-proxy-agent/test/test.ts @@ -0,0 +1,270 @@ +import fs from 'fs'; +import http from 'http'; +import https from 'https'; +import assert from 'assert'; +import { once } from 'events'; +import { listen } from 'async-listen'; +import { json, req } from 'agent-base'; +import { createProxy, ProxyServer } from 'proxy'; +import { HttpsProxyAgent } from '../src'; + +const sslOptions = { + key: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.key`), + cert: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.pem`), +}; + +const testIf = (condition: boolean, ...args: Parameters) => + condition ? test(...args) : test.skip(...args); + +describe('HttpsProxyAgent', () => { + let server: http.Server; + let serverUrl: URL; + + let sslServer: https.Server; + let sslServerUrl: URL; + + let proxy: ProxyServer; + let proxyUrl: URL; + + let sslProxy: ProxyServer; + let sslProxyUrl: URL; + + beforeAll(async () => { + // setup target HTTP server + server = http.createServer(); + serverUrl = await listen(server) as URL; + }); + + beforeAll(async () => { + // setup HTTP proxy server + proxy = createProxy(); + proxyUrl = await listen(proxy) as URL; + }); + + beforeAll(async () => { + // setup target HTTPS server + sslServer = https.createServer(sslOptions); + sslServerUrl = await listen(sslServer) as URL; + }); + + beforeAll(async () => { + // setup SSL HTTP proxy server + sslProxy = createProxy(https.createServer(sslOptions)); + sslProxyUrl = await listen(sslProxy) as URL; + }); + + // shut down the test HTTP servers + afterAll(() => { + server.close(); + proxy.close(); + sslServer.close(); + sslProxy.close(); + }); + + describe('constructor', () => { + it('should throw an Error if no "proxy" argument is given', () => { + assert.throws(() => { + new HttpsProxyAgent(''); + }); + }); + it('should accept a "string" proxy argument', () => { + const agent = new HttpsProxyAgent(proxyUrl.href); + assert.equal(proxyUrl.hostname, agent.proxy.hostname); + assert.equal(proxyUrl.port, agent.proxy.port); + }); + it('should accept a `URL` instance proxy argument', () => { + const agent = new HttpsProxyAgent(proxyUrl); + assert.equal(proxyUrl.hostname, agent.proxy.hostname); + assert.equal(proxyUrl.port, agent.proxy.port); + }); + describe('secureProxy', () => { + it('should be `false` when "http:" protocol is used', () => { + const agent = new HttpsProxyAgent( + proxyUrl + ); + assert.equal(false, agent.secureProxy); + }); + it('should be `true` when "https:" protocol is used', () => { + const agent = new HttpsProxyAgent( + sslProxyUrl + ); + assert.equal(true, agent.secureProxy); + }); + }); + }); + + describe('"http" module', () => { + beforeEach(() => { + delete proxy.authenticate; + }); + + it('should work over an HTTP proxy', async () => { + server.once('request', (req, res) => { + res.end(JSON.stringify(req.headers)); + }); + + const agent = new HttpsProxyAgent(proxyUrl); + + const res = await req(serverUrl, { agent }); + const body = await json(res); + assert.equal(serverUrl.host, body.host); + }); + + it('should work over an HTTPS proxy', async () => { + server.once('request', (req, res) => { + res.end(JSON.stringify(req.headers)); + }); + + const agent = new HttpsProxyAgent(sslProxyUrl, { + rejectUnauthorized: false, + }); + + const res = await req(serverUrl, { agent }); + const body = await json(res); + assert.equal(serverUrl.host, body.host); + }); + it('should receive the 407 authorization code on the `http.ClientResponse`', async () => { + // reject all auth requests + proxy.authenticate = () => false; + + const agent = new HttpsProxyAgent(proxyUrl); + + const res = await req('http://example.com', { agent }); + assert.equal(407, res.statusCode); + assert('proxy-authenticate' in res.headers); + }); + it('should not error if the proxy responds with 407 and the request is aborted', async () => { + // reject all auth requests + proxy.authenticate = () => false; + + const req = http.get( + { + agent: new HttpsProxyAgent(proxyUrl), + }, + (res) => { + assert.equal(407, res.statusCode); + req.abort(); + } + ); + + await once(req, 'abort'); + }); + it('should emit an "end" event on the `http.IncomingMessage` if the proxy responds with non-200 status code', async () => { + proxy.authenticate = () => false; + + const agent = new HttpsProxyAgent(proxyUrl); + + const res = await req('http://example.com', { + agent, + }); + assert.equal(407, res.statusCode); + + res.resume(); + await once(res, 'end'); + }); + + it('should emit an "error" event on the `http.ClientRequest` if the proxy does not exist', async () => { + // port 4 is a reserved, but "unassigned" port + const agent = new HttpsProxyAgent('http://localhost:4'); + + let err: NodeJS.ErrnoException | undefined; + try { + await req('http://nodejs.org', { agent }); + } catch (_err) { + err = _err as NodeJS.ErrnoException; + } + assert(err); + assert.equal('ECONNREFUSED', err.code); + }); + + it('should allow custom proxy "headers"', async () => { + const agent = new HttpsProxyAgent( + serverUrl, + { + headers: { + Foo: 'bar', + }, + } + ); + + const connectPromise = once(server, 'connect'); + + http.get({ agent }); + + const [req, socket] = await connectPromise; + assert.equal('CONNECT', req.method); + assert.equal('bar', req.headers.foo); + socket.destroy(); + }); + }); + + describe('"https" module', () => { + // Skipping these tests on older Node, since it fails with a strange error. + // Possibly related to the `proxy` testing module. + // The module does seem to work fine on an actual proxy though. + const nodeVersion = parseFloat(process.versions.node); + + testIf( + nodeVersion >= 18, + 'should work over an HTTP proxy', + async () => { + sslServer.once('request', (req, res) => { + res.end(JSON.stringify(req.headers)); + }); + + const agent = new HttpsProxyAgent(proxyUrl); + + const res = await req(sslServerUrl, { + rejectUnauthorized: false, + agent, + }); + const body = await json(res); + assert.equal(sslServerUrl.host, body.host); + } + ); + + testIf( + nodeVersion >= 18, + 'should work over an HTTPS proxy', + async () => { + sslServer.once('request', (req, res) => { + res.end(JSON.stringify(req.headers)); + }); + + const agent = new HttpsProxyAgent(sslProxyUrl, { + rejectUnauthorized: false, + }); + + const res = await req(sslServerUrl, { + agent, + rejectUnauthorized: false, + }); + const body = await json(res); + assert.equal(sslServerUrl.host, body.host); + } + ); + + testIf( + nodeVersion >= 18, + 'should not send a port number for the default port', + async () => { + sslServer.once('request', (req, res) => { + res.end(JSON.stringify(req.headers)); + }); + + const agent = new HttpsProxyAgent( + sslProxyUrl, + { rejectUnauthorized: false } + ); + agent.defaultPort = parseInt(sslServerUrl.port, 10); + + const res = await req(sslServerUrl, { + agent, + rejectUnauthorized: false, + }); + const body = await json(res); + assert.equal(sslServerUrl.hostname, body.host); + } + ); + }); +}); diff --git a/packages/https-proxy-agent/test/tsconfig.json b/packages/https-proxy-agent/test/tsconfig.json new file mode 100644 index 00000000..a79e2e63 --- /dev/null +++ b/packages/https-proxy-agent/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["test.ts"] +} diff --git a/packages/https-proxy-agent/tsconfig.json b/packages/https-proxy-agent/tsconfig.json new file mode 100644 index 00000000..24b6cf7e --- /dev/null +++ b/packages/https-proxy-agent/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/pac-proxy-agent/README.md b/packages/pac-proxy-agent/README.md new file mode 100644 index 00000000..16511ace --- /dev/null +++ b/packages/pac-proxy-agent/README.md @@ -0,0 +1,78 @@ +pac-proxy-agent +=============== +### A [PAC file][pac-wikipedia] proxy `http.Agent` implementation for HTTP and HTTPS +[![Build Status](https://github.com/TooTallNate/node-pac-proxy-agent/workflows/Node%20CI/badge.svg)](https://github.com/TooTallNate/node-pac-proxy-agent/actions?workflow=Node+CI) + +This module provides an `http.Agent` implementation that retreives the specified +[PAC proxy file][pac-wikipedia] and uses it to resolve which HTTP, HTTPS, or +SOCKS proxy, or if a direct connection should be used to connect to the +HTTP endpoint. + +It is designed to be be used with the built-in `http` and `https` modules. + + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install pac-proxy-agent +``` + + +Example +------- + +``` js +var url = require('url'); +var http = require('http'); +var PacProxyAgent = require('pac-proxy-agent'); + +// URI to a PAC proxy file to use (the "pac+" prefix is stripped) +var proxy = 'pac+https://cloudup.com/ceGH2yZ0Bjp+'; +console.log('using PAC proxy proxy file at %j', proxy); + +// HTTP endpoint for the proxy to connect to +var endpoint = 'http://nodejs.org/api/'; +console.log('attempting to GET %j', endpoint); +var opts = url.parse(endpoint); + +// create an instance of the `PacProxyAgent` class with the PAC file location +var agent = new PacProxyAgent(proxy); +opts.agent = agent; + +http.get(opts, function (res) { + console.log('"response" event!', res.headers); + res.pipe(process.stdout); +}); +``` + + +License +------- + +(The MIT License) + +Copyright (c) 2014 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +[pac-wikipedia]: http://wikipedia.org/wiki/Proxy_auto-config diff --git a/packages/pac-proxy-agent/package.json b/packages/pac-proxy-agent/package.json new file mode 100644 index 00000000..492b93b3 --- /dev/null +++ b/packages/pac-proxy-agent/package.json @@ -0,0 +1,57 @@ +{ + "name": "pac-proxy-agent", + "version": "5.0.0", + "description": "A PAC file proxy `http.Agent` implementation for HTTP", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "test": "mocha --reporter spec", + "prepublishOnly": "npm run build" + }, + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/node-pac-proxy-agent.git" + }, + "keywords": [ + "pac", + "proxy", + "agent", + "http", + "https", + "socks", + "request", + "access" + ], + "author": "Nathan Rajlich (http://n8.io/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/TooTallNate/node-pac-proxy-agent/issues" + }, + "homepage": "https://github.com/TooTallNate/node-pac-proxy-agent", + "dependencies": { + "@tootallnate/once": "^2.0.0", + "agent-base": "^6.0.2", + "debug": "^4.3.4", + "get-uri": "^5.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "pac-resolver": "^5.0.1", + "socks-proxy-agent": "^7.0.0" + }, + "devDependencies": { + "@types/debug": "^4.1.7", + "@types/node": "^14.18.43", + "mocha": "^6.2.3", + "proxy": "workspace:*", + "socksv5": "0.0.6", + "tsconfig": "workspace:*", + "typescript": "^5.0.4" + }, + "engines": { + "node": ">= 14" + } +} diff --git a/packages/pac-proxy-agent/src/index.ts b/packages/pac-proxy-agent/src/index.ts new file mode 100644 index 00000000..c6ec5aff --- /dev/null +++ b/packages/pac-proxy-agent/src/index.ts @@ -0,0 +1,288 @@ +import * as net from 'net'; +import * as tls from 'tls'; +import * as http from 'http'; +import * as crypto from 'crypto'; +import once from '@tootallnate/once'; +import createDebug from 'debug'; +import { Readable } from 'stream'; +import { format } from 'url'; +import { Agent, AgentConnectOpts, toBuffer } from 'agent-base'; +import { HttpProxyAgent, HttpProxyAgentOptions } from 'http-proxy-agent'; +import { HttpsProxyAgent, HttpsProxyAgentOptions } from 'https-proxy-agent'; +import { SocksProxyAgent, SocksProxyAgentOptions } from 'socks-proxy-agent'; +import { + getUri, + protocols as gProtocols, + ProtocolOpts as GetUriOptions, +} from "get-uri"; +import { + createPacResolver, + FindProxyForURL, + PacResolverOptions, +} from 'pac-resolver'; + +const debug = createDebug('pac-proxy-agent'); + +type Protocols = keyof typeof gProtocols; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +type Protocol = T extends `pac+${infer P}:${infer _}` + ? P + // eslint-disable-next-line @typescript-eslint/no-unused-vars + : T extends `${infer P}:${infer _}` + ? P + : never; + +export type PacProxyAgentOptions = PacResolverOptions & + GetUriOptions<`${Protocol}:`> & + HttpProxyAgentOptions<''> & + HttpsProxyAgentOptions<''> & + SocksProxyAgentOptions & { + fallbackToDirect?: boolean; + }; + +/** + * The `PacProxyAgent` class. + * + * A few different "protocol" modes are supported (supported protocols are + * backed by the `get-uri` module): + * + * - "pac+data", "data" - refers to an embedded "data:" URI + * - "pac+file", "file" - refers to a local file + * - "pac+ftp", "ftp" - refers to a file located on an FTP server + * - "pac+http", "http" - refers to an HTTP endpoint + * - "pac+https", "https" - refers to an HTTPS endpoint + */ +export class PacProxyAgent extends Agent { + static readonly protocols: `pac-${Protocols}`[] = [ + "pac-data", + "pac-file", + "pac-ftp", + "pac-http", + "pac-https", + ]; + + uri: URL; + opts: PacProxyAgentOptions; + cache?: Readable; + resolver?: FindProxyForURL; + resolverHash: string; + resolverPromise?: Promise; + + constructor(uri: Uri | URL, opts?: PacProxyAgentOptions) { + super(); + + // Strip the "pac+" prefix + const uriStr = typeof uri === "string" ? uri : uri.href; + this.uri = new URL(uriStr.replace(/^pac\+/i, "")); + + debug("Creating PacProxyAgent with URI %o", this.uri.href); + + // @ts-expect-error Not sure why TS is complaining here… + this.opts = { ...opts }; + this.cache = undefined; + this.resolver = undefined; + this.resolverHash = ""; + this.resolverPromise = undefined; + + // For `PacResolver` + if (!this.opts.filename) { + this.opts.filename = this.uri.href; + } + } + + private clearResolverPromise = (): void => { + this.resolverPromise = undefined; + }; + + /** + * Loads the PAC proxy file from the source if necessary, and returns + * a generated `FindProxyForURL()` resolver function to use. + * + * @api private + */ + private getResolver(): Promise { + if (!this.resolverPromise) { + this.resolverPromise = this.loadResolver(); + this.resolverPromise.then( + this.clearResolverPromise, + this.clearResolverPromise + ); + } + return this.resolverPromise; + } + + private async loadResolver(): Promise { + try { + // (Re)load the contents of the PAC file URI + const code = await this.loadPacFile(); + + // Create a sha1 hash of the JS code + const hash = crypto.createHash("sha1").update(code).digest("hex"); + + if (this.resolver && this.resolverHash === hash) { + debug( + "Same sha1 hash for code - contents have not changed, reusing previous proxy resolver" + ); + return this.resolver; + } + + // Cache the resolver + debug("Creating new proxy resolver instance"); + this.resolver = createPacResolver(code, this.opts); + + // Store that sha1 hash for future comparison purposes + this.resolverHash = hash; + + return this.resolver; + } catch (err: unknown) { + if ( + this.resolver && + (err as NodeJS.ErrnoException).code === "ENOTMODIFIED" + ) { + debug( + "Got ENOTMODIFIED response, reusing previous proxy resolver" + ); + return this.resolver; + } + throw err; + } + } + + /** + * Loads the contents of the PAC proxy file. + * + * @api private + */ + private async loadPacFile(): Promise { + debug("Loading PAC file: %o", this.uri); + + const rs = await getUri(this.uri, { ...this.opts, cache: this.cache }); + debug("Got `Readable` instance for URI"); + this.cache = rs; + + const buf = await toBuffer(rs); + debug("Read %o byte PAC file from URI", buf.length); + + return buf.toString("utf8"); + } + + /** + * Called when the node-core HTTP client library is creating a new HTTP request. + */ + async connect( + req: http.ClientRequest, + opts: AgentConnectOpts + ): Promise { + const { secureEndpoint } = opts; + + // First, get a generated `FindProxyForURL()` function, + // either cached or retrieved from the source + const resolver = await this.getResolver(); + + // Calculate the `url` parameter + const defaultPort = secureEndpoint ? 443 : 80; + let path = req.path; + let search: string | null = null; + const firstQuestion = path.indexOf("?"); + if (firstQuestion !== -1) { + search = path.substring(firstQuestion); + path = path.substring(0, firstQuestion); + } + + const urlOpts = { + ...opts, + protocol: secureEndpoint ? "https:" : "http:", + pathname: path, + search, + + // need to use `hostname` instead of `host` otherwise `port` is ignored + hostname: opts.host, + host: null, + href: null, + + // set `port` to null when it is the protocol default port (80 / 443) + port: defaultPort === opts.port ? null : opts.port, + }; + const url = format(urlOpts); + + debug("url: %o", url); + let result = await resolver(url); + + // Default to "DIRECT" if a falsey value was returned (or nothing) + if (!result) { + result = "DIRECT"; + } + + const proxies = String(result) + .trim() + .split(/\s*;\s*/g) + .filter(Boolean); + + if (this.opts.fallbackToDirect && !proxies.includes("DIRECT")) { + proxies.push("DIRECT"); + } + + for (const proxy of proxies) { + let agent: Agent | null = null; + let socket: net.Socket | null = null; + const [type, target] = proxy.split(/\s+/); + debug("Attempting to use proxy: %o", proxy); + + if (type === "DIRECT") { + // Direct connection to the destination endpoint + socket = secureEndpoint ? tls.connect(opts) : net.connect(opts); + } else if (type === "SOCKS" || type === "SOCKS5") { + // Use a SOCKSv5h proxy + agent = new SocksProxyAgent(`socks://${target}`, this.opts); + } else if (type === "SOCKS4") { + // Use a SOCKSv4a proxy + agent = new SocksProxyAgent(`socks4a://${target}`, this.opts); + } else if ( + type === "PROXY" || + type === "HTTP" || + type === "HTTPS" + ) { + // Use an HTTP or HTTPS proxy + // http://dev.chromium.org/developers/design-documents/secure-web-proxy + const proxyURL = `${ + type === "HTTPS" ? "https" : "http" + }://${target}`; + if (secureEndpoint) { + agent = new HttpsProxyAgent(proxyURL, this.opts); + } else { + agent = new HttpProxyAgent(proxyURL, this.opts); + } + } + + try { + if (socket) { + // "DIRECT" connection, wait for connection confirmation + await once(socket, "connect"); + req.emit("proxy", { proxy, socket }); + return socket; + } + if (agent) { + const s = await agent.connect(req, opts); + if (!(s instanceof net.Socket)) { + throw new Error( + "Expected a `net.Socket` to be returned from agent" + ); + } + req.emit("proxy", { proxy, socket: s }); + return s; + } + throw new Error(`Could not determine proxy type for: ${proxy}`); + } catch (err) { + debug("Got error for proxy %o: %o", proxy, err); + req.emit("proxy", { proxy, error: err }); + } + } + + throw new Error( + `Failed to establish a socket connection to proxies: ${JSON.stringify( + proxies + )}` + ); + } +} \ No newline at end of file diff --git a/packages/pac-proxy-agent/test/ssl-cert-snakeoil.key b/packages/pac-proxy-agent/test/ssl-cert-snakeoil.key new file mode 100644 index 00000000..fd125012 --- /dev/null +++ b/packages/pac-proxy-agent/test/ssl-cert-snakeoil.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCzURxIqzer0ACAbX/lHdsn4Gd9PLKrf7EeDYfIdV0HZKPD8WDr +bBx2/fBu0OW2sjnzv/SVZbJ0DAuPE/p0+eT0qb2qC10iz9iTD7ribd7gxhirVb8y +b3fBjXsxc8V8p4Ny1LcvNSqCjwUbJqdRogfoJeTiqPM58z5sNzuv5iq7iwIDAQAB +AoGAPMQy4olrP0UotlzlJ36bowLP70ffgHCwU+/f4NWs5fF78c3du0oSx1w820Dd +Z7E0JF8bgnlJJTxjumPZz0RUCugrEHBKJmzEz3cxF5E3+7NvteZcjKn9D67RrM5x +1/uSZ9cqKE9cYvY4fSuHx18diyZ4axR/wB1Pea2utjjDM+ECQQDb9ZbmmaWMiRpQ +5Up+loxP7BZNPsEVsm+DVJmEFbaFgGfncWBqSIqnPNjMwTwj0OigTwCAEGPkfRVW +T0pbYWCxAkEA0LK7SCTwzyDmhASUalk0x+3uCAA6ryFdwJf/wd8TRAvVOmkTEldX +uJ7ldLvfrONYO3v56uKTU/SoNdZYzKtO+wJAX2KM4ctXYy5BXztPpr2acz4qHa1N +Bh+vBAC34fOYhyQ76r3b1btHhWZ5jbFuZwm9F2erC94Ps5IaoqcX07DSwQJAPKGw +h2U0EPkd/3zVIZCJJQya+vgWFIs9EZcXVtvYXQyTBkVApTN66MhBIYjzkub5205J +bVQmOV37AKklY1DhwQJAA1wos0cYxro02edzatxd0DIR2r4qqOqLkw6BhYHhq6HJ +ZvIcQkHqdSXzdETFc01I1znDGGIrJHcnvKWgBPoEUg== +-----END RSA PRIVATE KEY----- diff --git a/packages/pac-proxy-agent/test/ssl-cert-snakeoil.pem b/packages/pac-proxy-agent/test/ssl-cert-snakeoil.pem new file mode 100644 index 00000000..b115a5e9 --- /dev/null +++ b/packages/pac-proxy-agent/test/ssl-cert-snakeoil.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB1TCCAT4CCQDV5mPlzm9+izANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDEyQ3 +NTI3YmQ3Ny1hYjNlLTQ3NGItYWNlNy1lZWQ2MDUzOTMxZTcwHhcNMTUwNzA2MjI0 +NTA3WhcNMjUwNzAzMjI0NTA3WjAvMS0wKwYDVQQDEyQ3NTI3YmQ3Ny1hYjNlLTQ3 +NGItYWNlNy1lZWQ2MDUzOTMxZTcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +ALNRHEirN6vQAIBtf+Ud2yfgZ308sqt/sR4Nh8h1XQdko8PxYOtsHHb98G7Q5bay +OfO/9JVlsnQMC48T+nT55PSpvaoLXSLP2JMPuuJt3uDGGKtVvzJvd8GNezFzxXyn +g3LUty81KoKPBRsmp1GiB+gl5OKo8znzPmw3O6/mKruLAgMBAAEwDQYJKoZIhvcN +AQEFBQADgYEACzoHUF8UV2Z6541Q2wKEA0UFUzmUjf/E1XwBO+1P15ZZ64uw34B4 +1RwMPtAo9RY/PmICTWtNxWGxkzwb2JtDWtnxVER/lF8k2XcXPE76fxTHJF/BKk9J +QU8OTD1dd9gHCBviQB9TqntRZ5X7axjtuWjb2umY+owBYzAHZkp1HKI= +-----END CERTIFICATE----- diff --git a/packages/pac-proxy-agent/test/test.js b/packages/pac-proxy-agent/test/test.js new file mode 100644 index 00000000..005deab3 --- /dev/null +++ b/packages/pac-proxy-agent/test/test.js @@ -0,0 +1,464 @@ +/** + * Module dependencies. + */ + +let fs = require('fs'); +let url = require('url'); +let http = require('http'); +let https = require('https'); +let assert = require('assert'); +let socks = require('socksv5'); +let { createProxy } = require('proxy'); +let { toBuffer } = require('agent-base'); +let { PacProxyAgent } = require('../'); + +describe('PacProxyAgent', function () { + // target servers + let httpServer; + let httpPort; + let httpsServer; + let httpsPort; + + // proxy servers + let socksServer; + let socksPort; + let proxyServer; + let proxyPort; + let proxyHttpsServer; + let proxyHttpsPort; + + before(function (done) { + // setup target HTTP server + httpServer = http.createServer(); + httpServer.listen(function () { + httpPort = httpServer.address().port; + done(); + }); + }); + + before(function (done) { + // setup target SSL HTTPS server + let options = { + key: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.key`), + cert: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.pem`), + }; + httpsServer = https.createServer(options); + httpsServer.listen(function () { + httpsPort = httpsServer.address().port; + done(); + }); + }); + + before(function (done) { + // setup SOCKS proxy server + socksServer = socks.createServer(function (_info, accept) { + accept(); + }); + socksServer.listen(function () { + socksPort = socksServer.address().port; + done(); + }); + socksServer.useAuth(socks.auth.None()); + }); + + before(function (done) { + // setup HTTP proxy server + proxyServer = createProxy(); + proxyServer.listen(function () { + proxyPort = proxyServer.address().port; + done(); + }); + }); + + before(function (done) { + // setup SSL HTTPS proxy server + let options = { + key: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.key`), + cert: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.pem`), + }; + proxyHttpsServer = createProxy(https.createServer(options)); + proxyHttpsServer.listen(function () { + proxyHttpsPort = proxyHttpsServer.address().port; + done(); + }); + }); + + after(function (done) { + // socksServer.once('close', function () { done(); }); + socksServer.close(); + done(); + }); + + after(function (done) { + // httpServer.once('close', function () { done(); }); + httpServer.close(); + done(); + }); + + after(function (done) { + // httpsServer.once('close', function () { done(); }); + httpsServer.close(); + done(); + }); + + after(function (done) { + // proxyServer.once('close', function () { done(); }); + proxyServer.close(); + done(); + }); + + after(function (done) { + // proxyHttpsServer.once('close', function () { done(); }); + proxyHttpsServer.close(); + done(); + }); + + it('should allow a `sandbox` to be passed in', function (done) { + this.slow(1000); + + function FindProxyForURL() { + throw new Error(foo() + bar()); + } + + function foo() { + return 'hi'; + } + + function asyncBar() { + return new Promise((r) => r('fooooo')); + } + asyncBar.async = true; + + let uri = `data:,${encodeURIComponent(FindProxyForURL.toString())}`; + let agent = new PacProxyAgent(uri, { + sandbox: { + foo, + bar: asyncBar, + }, + }); + + let opts = url.parse(`http://localhost:${httpPort}/test`); + opts.agent = agent; + + let req = http.get(opts); + req.once('error', function (err) { + assert.equal(err.message, 'hifooooo'); + done(); + }); + }); + + describe('constructor', function () { + it('should throw an Error if no "proxy" argument is given', function () { + assert.throws(() => { + new PacProxyAgent(); + }); + }); + it('should accept a "string" proxy argument', function () { + let agent = new PacProxyAgent('pac+ftp://example.com/proxy.pac'); + assert.equal('ftp://example.com/proxy.pac', agent.uri.href); + }); + it('should accept a `URL` instance proxy argument', function () { + let agent = new PacProxyAgent( + new URL('pac+ftp://example.com/proxy.pac') + ); + assert.equal('ftp://example.com/proxy.pac', agent.uri.href); + }); + }); + + describe('"http" module', function () { + it('should work over an HTTP proxy', function (done) { + httpServer.once('request', function (req, res) { + res.end(JSON.stringify(req.headers)); + }); + + function FindProxyForURL(url, host) { + return 'PROXY localhost:PORT;'; + } + + let uri = `data:,${encodeURIComponent( + FindProxyForURL.toString().replace('PORT', proxyPort) + )}`; + let agent = new PacProxyAgent(uri); + + let opts = url.parse(`http://localhost:${httpPort}/test`); + opts.agent = agent; + + let req = http.get(opts, function (res) { + toBuffer(res).then((buf) => { + let data = JSON.parse(buf.toString()); + assert.equal(`localhost:${httpPort}`, data.host); + assert('via' in data); + done(); + }); + }); + req.once('error', done); + }); + + it('should work over an HTTPS proxy', function (done) { + httpServer.once('request', function (req, res) { + res.end(JSON.stringify(req.headers)); + }); + + function FindProxyForURL(url, host) { + return 'HTTPS localhost:PORT;'; + } + + let uri = `data:,${encodeURIComponent( + FindProxyForURL.toString().replace('PORT', proxyHttpsPort) + )}`; + let agent = new PacProxyAgent(uri, { rejectUnauthorized: false }); + + let opts = url.parse(`http://localhost:${httpPort}/test`); + opts.agent = agent; + + let req = http.get(opts, function (res) { + toBuffer(res).then((buf) => { + let data = JSON.parse(buf.toString()); + assert.equal(`localhost:${httpPort}`, data.host); + assert('via' in data); + done(); + }); + }); + req.once('error', done); + }); + + it('should work over a SOCKS proxy', function (done) { + httpServer.once('request', function (req, res) { + res.end(JSON.stringify(req.headers)); + }); + + function FindProxyForURL() { + return 'SOCKS localhost:PORT;'; + } + + let uri = `data:,${encodeURIComponent( + FindProxyForURL.toString().replace('PORT', socksPort) + )}`; + let agent = new PacProxyAgent(uri); + + let opts = url.parse(`http://localhost:${httpPort}/test`); + opts.agent = agent; + + let req = http.get(opts, function (res) { + toBuffer(res).then((buf) => { + let data = JSON.parse(buf.toString()); + assert.equal(`localhost:${httpPort}`, data.host); + done(); + }); + }); + req.once('error', done); + }); + + it('should fall back to the next proxy after one fails', function (done) { + // This test is slow on Windows :/ + this.timeout(10000); + + let gotReq = false; + httpServer.once('request', function (req, res) { + res.end(JSON.stringify(req.headers)); + gotReq = true; + }); + + function FindProxyForURL(url, host) { + return 'SOCKS bad-domain:8080; HTTP bad-domain:8080; HTTPS bad-domain:8080; DIRECT;'; + } + + let uri = `data:,${encodeURIComponent(String(FindProxyForURL))}`; + let agent = new PacProxyAgent(uri); + + let opts = url.parse(`http://localhost:${httpPort}/test`); + opts.agent = agent; + + let req = http.get(opts, function (res) { + toBuffer(res).then((buf) => { + let data = JSON.parse(buf.toString()); + assert.equal(`localhost:${httpPort}`, data.host); + assert.equal(proxyCount, 4); + assert(gotReq); + done(); + }); + }); + + let proxyCount = 0; + req.on('proxy', function ({ proxy, error, socket }) { + proxyCount++; + if (proxy === 'DIRECT') { + assert(socket); + } else { + assert(error); + } + }); + + req.once('error', done); + }); + + it('should support `fallbackToDirect` option', function (done) { + // This test is slow on Windows :/ + this.timeout(10000); + + let gotReq = false; + httpServer.once('request', function (req, res) { + res.end(JSON.stringify(req.headers)); + gotReq = true; + }); + + function FindProxyForURL(url, host) { + return 'SOCKS 127.0.0.1:4'; + } + + const uri = `data:,${encodeURIComponent(String(FindProxyForURL))}`; + const agent = new PacProxyAgent(uri, { fallbackToDirect: true }); + + const opts = url.parse(`http://localhost:${httpPort}/test`); + opts.agent = agent; + + const req = http.get(opts, function (res) { + toBuffer(res).then((buf) => { + let data = JSON.parse(buf.toString()); + assert.equal(`localhost:${httpPort}`, data.host); + assert(gotReq); + done(); + }); + }); + req.once('error', done); + }); + }); + + describe('"https" module', function () { + it('should work over an HTTP proxy', function (done) { + httpsServer.once('request', function (req, res) { + res.end(JSON.stringify(req.headers)); + }); + + function FindProxyForURL(url, host) { + return 'PROXY localhost:PORT;'; + } + + let uri = `data:,${encodeURIComponent( + FindProxyForURL.toString().replace('PORT', proxyPort) + )}`; + let agent = new PacProxyAgent(uri); + + let opts = url.parse(`https://localhost:${httpsPort}/test`); + opts.agent = agent; + opts.rejectUnauthorized = false; + + let req = https.get(opts, function (res) { + toBuffer(res).then((buf) => { + let data = JSON.parse(buf.toString()); + assert.equal(`localhost:${httpsPort}`, data.host); + done(); + }); + }); + req.once('error', done); + }); + + it('should work over an HTTPS proxy', function (done) { + let gotReq = false; + httpsServer.once('request', function (req, res) { + gotReq = true; + res.end(JSON.stringify(req.headers)); + }); + + function FindProxyForURL(url, host) { + return 'HTTPS localhost:PORT;'; + } + + let uri = `data:,${encodeURIComponent( + FindProxyForURL.toString().replace('PORT', proxyHttpsPort) + )}`; + let agent = new PacProxyAgent(uri, { + rejectUnauthorized: false, + }); + + let opts = url.parse(`https://localhost:${httpsPort}/test`); + opts.agent = agent; + opts.rejectUnauthorized = false; + + let req = https.get(opts, function (res) { + toBuffer(res).then((buf) => { + let data = JSON.parse(buf.toString()); + assert.equal(`localhost:${httpsPort}`, data.host); + assert(gotReq); + done(); + }); + }); + req.once('error', done); + }); + + it('should work over a SOCKS proxy', function (done) { + let gotReq = false; + httpsServer.once('request', function (req, res) { + gotReq = true; + res.end(JSON.stringify(req.headers)); + }); + + function FindProxyForURL(url, host) { + return 'SOCKS localhost:PORT;'; + } + + let uri = `data:,${encodeURIComponent( + FindProxyForURL.toString().replace('PORT', socksPort) + )}`; + let agent = new PacProxyAgent(uri); + + let opts = url.parse(`https://localhost:${httpsPort}/test`); + opts.agent = agent; + opts.rejectUnauthorized = false; + + let req = https.get(opts, function (res) { + toBuffer(res).then((buf) => { + let data = JSON.parse(buf.toString()); + assert.equal(`localhost:${httpsPort}`, data.host); + assert(gotReq); + done(); + }); + }); + req.once('error', done); + }); + + it('should fall back to the next proxy after one fails', function (done) { + // This test is slow on Windows :/ + this.timeout(10000); + + let gotReq = false; + httpsServer.once('request', function (req, res) { + gotReq = true; + res.end(JSON.stringify(req.headers)); + }); + + function FindProxyForURL(url, host) { + return 'SOCKS bad-domain:8080; HTTP bad-domain:8080; HTTPS bad-domain:8080; DIRECT;'; + } + + let uri = `data:,${encodeURIComponent(String(FindProxyForURL))}`; + let agent = new PacProxyAgent(uri); + + let opts = url.parse(`https://localhost:${httpsPort}/test`); + opts.agent = agent; + opts.rejectUnauthorized = false; + + let req = https.get(opts, function (res) { + toBuffer(res).then((buf) => { + let data = JSON.parse(buf.toString()); + assert.equal(`localhost:${httpsPort}`, data.host); + assert.equal(proxyCount, 4); + assert(gotReq); + done(); + }); + }); + + let proxyCount = 0; + req.on('proxy', function ({ proxy, error, socket }) { + proxyCount++; + if (proxy === 'DIRECT') { + assert(socket); + } else { + assert(error); + } + }); + + req.once('error', done); + }); + }); +}); diff --git a/packages/pac-proxy-agent/tsconfig.json b/packages/pac-proxy-agent/tsconfig.json new file mode 100644 index 00000000..24b6cf7e --- /dev/null +++ b/packages/pac-proxy-agent/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/pac-resolver/.eslintignore b/packages/pac-resolver/.eslintignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/packages/pac-resolver/.eslintignore @@ -0,0 +1 @@ +dist diff --git a/packages/pac-resolver/README.md b/packages/pac-resolver/README.md new file mode 100644 index 00000000..5cf551ab --- /dev/null +++ b/packages/pac-resolver/README.md @@ -0,0 +1,97 @@ +pac-resolver +============ +### Generates an asynchronous resolver function from a [PAC file][pac-wikipedia] +[![Build Status](https://github.com/TooTallNate/node-pac-resolver/workflows/Node%20CI/badge.svg)](https://github.com/TooTallNate/node-pac-resolver/actions?workflow=Node+CI) + + +This module accepts a JavaScript String of code, which is meant to be a +[PAC proxy file][pac-wikipedia], and returns a generated asynchronous +`FindProxyForURL()` function. + + +Installation +------------ + +Install with `npm`: + +```bash +$ npm install pac-resolver +``` + + +Example +------- + +Given the PAC proxy file named `proxy.pac`: + +```js +function FindProxyForURL(url, host) { + if (isInNet(myIpAddress(), "10.1.10.0", "255.255.255.0")) { + return "PROXY 1.2.3.4:8080"; + } else { + return "DIRECT"; + } +} +``` + +You can consume this PAC file with `pac-resolver` like so: + +```js +var fs = require('fs'); +var pac = require('pac-resolver'); + +var FindProxyForURL = pac(fs.readFileSync('proxy.pac')); + +FindProxyForURL('http://foo.com/').then((res) => { + console.log(res); + // "DIRECT" +}); +``` + + +API +--- + +### pac(String pacFileContents[, Object options]) → Function + +Returns an asynchronous `FindProxyForURL()` function based off of the given JS +string `pacFileContents` PAC proxy file. An optional `options` object may be +passed in which respects the following options: + + * `filename` - String - the filename to use in error stack traces. Defaults to `proxy.pac`. + * `sandbox` - Object - a map of functions to include in the sandbox of the + JavaScript environment where the JS code will be executed. i.e. if you wanted to + include the common `alert` function you could pass `alert: console.log`. For + async functions, you must set the `async = true` property on the function + instance, and the JS code will be able to invoke the function as if it were + synchronous. + + +License +------- + +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +[pac-file-docs]: https://web.archive.org/web/20070602031929/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html +[pac-wikipedia]: http://wikipedia.org/wiki/Proxy_auto-config diff --git a/packages/pac-resolver/package.json b/packages/pac-resolver/package.json new file mode 100644 index 00000000..db2574d5 --- /dev/null +++ b/packages/pac-resolver/package.json @@ -0,0 +1,48 @@ +{ + "name": "pac-resolver", + "version": "5.0.1", + "description": "Generates an asynchronous resolver function from a PAC file", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "dependencies": { + "degenerator": "^3.0.2", + "ip": "^1.1.5", + "netmask": "^2.0.2" + }, + "devDependencies": { + "@types/ip": "^1.1.0", + "@types/netmask": "^1.0.30", + "@types/node": "^14.18.43", + "mocha": "^9.2.1", + "tsconfig": "workspace:*", + "typescript": "^5.0.4" + }, + "scripts": { + "build": "tsc", + "test": "mocha --reporter spec", + "lint": "eslint . --ext .ts", + "prepublishOnly": "npm run build" + }, + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/node-pac-resolver.git" + }, + "engines": { + "node": ">= 14" + }, + "keywords": [ + "pac", + "file", + "proxy", + "resolve", + "dns" + ], + "author": "Nathan Rajlich (http://n8.io/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/TooTallNate/node-pac-resolver/issues" + } +} diff --git a/packages/pac-resolver/src/dateRange.ts b/packages/pac-resolver/src/dateRange.ts new file mode 100644 index 00000000..7ef09588 --- /dev/null +++ b/packages/pac-resolver/src/dateRange.ts @@ -0,0 +1,70 @@ +/** + * If only a single value is specified (from each category: day, month, year), the + * function returns a true value only on days that match that specification. If + * both values are specified, the result is true between those times, including + * bounds. + * + * Even though the examples don't show, the "GMT" parameter can be specified + * in any of the 9 different call profiles, always as the last parameter. + * + * Examples: + * + * ``` js + * dateRange(1) + * true on the first day of each month, local timezone. + * + * dateRange(1, "GMT") + * true on the first day of each month, GMT timezone. + * + * dateRange(1, 15) + * true on the first half of each month. + * + * dateRange(24, "DEC") + * true on 24th of December each year. + * + * dateRange(24, "DEC", 1995) + * true on 24th of December, 1995. + * + * dateRange("JAN", "MAR") + * true on the first quarter of the year. + * + * dateRange(1, "JUN", 15, "AUG") + * true from June 1st until August 15th, each year (including June 1st and August + * 15th). + * + * dateRange(1, "JUN", 15, 1995, "AUG", 1995) + * true from June 1st, 1995, until August 15th, same year. + * + * dateRange("OCT", 1995, "MAR", 1996) + * true from October 1995 until March 1996 (including the entire month of October + * 1995 and March 1996). + * + * dateRange(1995) + * true during the entire year 1995. + * + * dateRange(1995, 1997) + * true from beginning of year 1995 until the end of year 1997. + * ``` + * + * dateRange(day) + * dateRange(day1, day2) + * dateRange(mon) + * dateRange(month1, month2) + * dateRange(year) + * dateRange(year1, year2) + * dateRange(day1, month1, day2, month2) + * dateRange(month1, year1, month2, year2) + * dateRange(day1, month1, year1, day2, month2, year2) + * dateRange(day1, month1, year1, day2, month2, year2, gmt) + * + * @param {String} day is the day of month between 1 and 31 (as an integer). + * @param {String} month is one of the month strings: JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC + * @param {String} year is the full year number, for example 1995 (but not 95). Integer. + * @param {String} gmt is either the string "GMT", which makes time comparison occur in GMT timezone; if left unspecified, times are taken to be in the local timezone. + * @return {Boolean} + */ + +export default function dateRange(): boolean { + // TODO: implement me! + return false; +} diff --git a/packages/pac-resolver/src/dnsDomainIs.ts b/packages/pac-resolver/src/dnsDomainIs.ts new file mode 100644 index 00000000..ff6b9bf7 --- /dev/null +++ b/packages/pac-resolver/src/dnsDomainIs.ts @@ -0,0 +1,26 @@ +/** + * Returns true iff the domain of hostname matches. + * + * Examples: + * + * ``` js + * dnsDomainIs("www.netscape.com", ".netscape.com") + * // is true. + * + * dnsDomainIs("www", ".netscape.com") + * // is false. + * + * dnsDomainIs("www.mcom.com", ".netscape.com") + * // is false. + * ``` + * + * + * @param {String} host is the hostname from the URL. + * @param {String} domain is the domain name to test the hostname against. + * @return {Boolean} true iff the domain of the hostname matches. + */ +export default function dnsDomainIs(host: string, domain: string): boolean { + host = String(host); + domain = String(domain); + return host.substr(domain.length * -1) === domain; +} diff --git a/packages/pac-resolver/src/dnsDomainLevels.ts b/packages/pac-resolver/src/dnsDomainLevels.ts new file mode 100644 index 00000000..7bc953f5 --- /dev/null +++ b/packages/pac-resolver/src/dnsDomainLevels.ts @@ -0,0 +1,24 @@ +/** + * Returns the number (integer) of DNS domain levels (number of dots) in the + * hostname. + * + * Examples: + * + * ``` js + * dnsDomainLevels("www") + * // returns 0. + * dnsDomainLevels("www.netscape.com") + * // returns 2. + * ``` + * + * @param {String} host is the hostname from the URL. + * @return {Number} number of domain levels + */ +export default function dnsDomainLevels(host: string): number { + const match = String(host).match(/\./g); + let levels = 0; + if (match) { + levels = match.length; + } + return levels; +} diff --git a/packages/pac-resolver/src/dnsResolve.ts b/packages/pac-resolver/src/dnsResolve.ts new file mode 100644 index 00000000..0ec077d1 --- /dev/null +++ b/packages/pac-resolver/src/dnsResolve.ts @@ -0,0 +1,29 @@ +import { dnsLookup } from './util'; + +/** + * Resolves the given DNS hostname into an IP address, and returns it in the dot + * separated format as a string. + * + * Example: + * + * ``` js + * dnsResolve("home.netscape.com") + * // returns the string "198.95.249.79". + * ``` + * + * @param {String} host hostname to resolve + * @return {String} resolved IP address + */ + +export default async function dnsResolve(host: string): Promise { + const family = 4; + try { + const r = await dnsLookup(host, { family }); + if (typeof r === 'string') { + return r; + } + } catch (err) { + // @ignore + } + return null; +} diff --git a/packages/pac-resolver/src/index.ts b/packages/pac-resolver/src/index.ts new file mode 100644 index 00000000..fab57f6b --- /dev/null +++ b/packages/pac-resolver/src/index.ts @@ -0,0 +1,220 @@ +import { parse } from 'url'; +import { Context } from 'vm'; +import { CompileOptions, compile } from 'degenerator'; + +/** + * Built-in PAC functions. + */ +import dateRange from './dateRange'; +import dnsDomainIs from './dnsDomainIs'; +import dnsDomainLevels from './dnsDomainLevels'; +import dnsResolve from './dnsResolve'; +import isInNet from './isInNet'; +import isPlainHostName from './isPlainHostName'; +import isResolvable from './isResolvable'; +import localHostOrDomainIs from './localHostOrDomainIs'; +import myIpAddress from './myIpAddress'; +import shExpMatch from './shExpMatch'; +import timeRange from './timeRange'; +import weekdayRange from './weekdayRange'; + +/** + * Returns an asynchronous `FindProxyForURL()` function + * from the given JS string (from a PAC file). + */ +export function createPacResolver( + _str: string | Buffer, + _opts: PacResolverOptions = {} +) { + const str = Buffer.isBuffer(_str) ? _str.toString('utf8') : _str; + + // The sandbox to use for the `vm` context. + const context: Context = { + ...sandbox, + ..._opts.sandbox, + }; + + const opts: PacResolverOptions = { + filename: 'proxy.pac', + ..._opts, + sandbox: context, + }; + + // Construct the array of async function names to add `await` calls to. + const names = Object.keys(context).filter((k) => + isAsyncFunction(context[k]) + ); + + // Compile the JS `FindProxyForURL()` function into an async function. + const resolver = compile( + str, + 'FindProxyForURL', + names, + opts + ); + + /* eslint-disable @typescript-eslint/naming-convention, no-redeclare */ + function FindProxyForURL(url: string, host?: string): Promise; + function FindProxyForURL( + url: string, + callback: FindProxyForURLCallback + ): void; + function FindProxyForURL( + url: string, + host: string, + callback: FindProxyForURLCallback + ): void; + function FindProxyForURL( + url: string, + _host?: string | FindProxyForURLCallback, + _callback?: FindProxyForURLCallback + ): Promise | void { + let host: string | null = null; + let callback: FindProxyForURLCallback | null = null; + + if (typeof _callback === 'function') { + callback = _callback; + } + + if (typeof _host === 'string') { + host = _host; + } else if (typeof _host === 'function') { + callback = _host; + } + + if (!host) { + host = parse(url).hostname; + } + + if (!host) { + throw new TypeError('Could not determine `host`'); + } + + const promise = resolver(url, host); + + if (typeof callback === 'function') { + toCallback(promise, callback); + } else { + return promise; + } + } + + Object.defineProperty(FindProxyForURL, 'toString', { + value: () => resolver.toString(), + enumerable: false, + }); + + return FindProxyForURL; +} + +export type GMT = 'GMT'; +export type Hour = + | 0 + | 1 + | 2 + | 3 + | 4 + | 5 + | 6 + | 7 + | 8 + | 9 + | 10 + | 11 + | 12 + | 13 + | 14 + | 15 + | 16 + | 17 + | 18 + | 19 + | 20 + | 21 + | 22 + | 23; +export type Day = + | 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 + | 26 + | 27 + | 28 + | 29 + | 30 + | 31; +export type Weekday = 'SUN' | 'MON' | 'TUE' | 'WED' | 'THU' | 'FRI' | 'SAT'; +export type Month = + | 'JAN' + | 'FEB' + | 'MAR' + | 'APR' + | 'MAY' + | 'JUN' + | 'JUL' + | 'AUG' + | 'SEP' + | 'OCT' + | 'NOV' + | 'DEC'; +export type PacResolverOptions = CompileOptions; +export interface FindProxyForURLCallback { + (err?: Error | null, result?: string): void; +} +export type FindProxyForURL = ReturnType; + +export const sandbox = Object.freeze({ + alert: (message = '') => console.log('%s', message), + dateRange, + dnsDomainIs, + dnsDomainLevels, + dnsResolve, + isInNet, + isPlainHostName, + isResolvable, + localHostOrDomainIs, + myIpAddress, + shExpMatch, + timeRange, + weekdayRange, +}); + +function toCallback( + promise: Promise, + callback: (err: Error | null, result?: T) => void +): void { + promise.then((rtn) => callback(null, rtn), callback); +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function isAsyncFunction(v: any): boolean { + if (typeof v !== 'function') return false; + // Native `AsyncFunction` + if (v.constructor.name === 'AsyncFunction') return true; + // TypeScript compiled + if (String(v).indexOf('__awaiter(') !== -1) return true; + // Legacy behavior - set `async` property on the function + return Boolean(v.async); +} diff --git a/packages/pac-resolver/src/isInNet.ts b/packages/pac-resolver/src/isInNet.ts new file mode 100644 index 00000000..50a54873 --- /dev/null +++ b/packages/pac-resolver/src/isInNet.ts @@ -0,0 +1,47 @@ +/** + * Module dependencies. + */ + +import { Netmask } from 'netmask'; +import { dnsLookup } from './util'; + +/** + * True iff the IP address of the host matches the specified IP address pattern. + * + * Pattern and mask specification is done the same way as for SOCKS configuration. + * + * Examples: + * + * ``` js + * isInNet(host, "198.95.249.79", "255.255.255.255") + * // is true iff the IP address of host matches exactly 198.95.249.79. + * + * isInNet(host, "198.95.0.0", "255.255.0.0") + * // is true iff the IP address of the host matches 198.95.*.*. + * ``` + * + * @param {String} host a DNS hostname, or IP address. If a hostname is passed, + * it will be resoved into an IP address by this function. + * @param {String} pattern an IP address pattern in the dot-separated format mask. + * @param {String} mask for the IP address pattern informing which parts of the + * IP address should be matched against. 0 means ignore, 255 means match. + * @return {Boolean} + */ + +export default async function isInNet( + host: string, + pattern: string, + mask: string +): Promise { + const family = 4; + try { + const ip = await dnsLookup(host, { family }); + if (typeof ip === 'string') { + const netmask = new Netmask(pattern, mask); + return netmask.contains(ip); + } + } catch (err) { + // ignore + } + return false; +} diff --git a/packages/pac-resolver/src/isPlainHostName.ts b/packages/pac-resolver/src/isPlainHostName.ts new file mode 100644 index 00000000..00df26f6 --- /dev/null +++ b/packages/pac-resolver/src/isPlainHostName.ts @@ -0,0 +1,20 @@ +/** + * True iff there is no domain name in the hostname (no dots). + * + * Examples: + * + * ``` js + * isPlainHostName("www") + * // is true. + * + * isPlainHostName("www.netscape.com") + * // is false. + * ``` + * + * @param {String} host The hostname from the URL (excluding port number). + * @return {Boolean} + */ + +export default function isPlainHostName(host: string): boolean { + return !/\./.test(host); +} diff --git a/packages/pac-resolver/src/isResolvable.ts b/packages/pac-resolver/src/isResolvable.ts new file mode 100644 index 00000000..d109566d --- /dev/null +++ b/packages/pac-resolver/src/isResolvable.ts @@ -0,0 +1,20 @@ +import { dnsLookup } from './util'; + +/** + * Tries to resolve the hostname. Returns true if succeeds. + * + * @param {String} host is the hostname from the URL. + * @return {Boolean} + */ + +export default async function isResolvable(host: string): Promise { + const family = 4; + try { + if (await dnsLookup(host, { family })) { + return true; + } + } catch (err) { + // ignore + } + return false; +} diff --git a/packages/pac-resolver/src/localHostOrDomainIs.ts b/packages/pac-resolver/src/localHostOrDomainIs.ts new file mode 100644 index 00000000..7efc8606 --- /dev/null +++ b/packages/pac-resolver/src/localHostOrDomainIs.ts @@ -0,0 +1,41 @@ +/** + * Is true if the hostname matches exactly the specified hostname, or if there is + * no domain name part in the hostname, but the unqualified hostname matches. + * + * Examples: + * + * ``` js + * localHostOrDomainIs("www.netscape.com", "www.netscape.com") + * // is true (exact match). + * + * localHostOrDomainIs("www", "www.netscape.com") + * // is true (hostname match, domain not specified). + * + * localHostOrDomainIs("www.mcom.com", "www.netscape.com") + * // is false (domain name mismatch). + * + * localHostOrDomainIs("home.netscape.com", "www.netscape.com") + * // is false (hostname mismatch). + * ``` + * + * @param {String} host the hostname from the URL. + * @param {String} hostdom fully qualified hostname to match against. + * @return {Boolean} + */ +export default function localHostOrDomainIs( + host: string, + hostdom: string +): boolean { + const parts = host.split('.'); + const domparts = hostdom.split('.'); + let matches = true; + + for (let i = 0; i < parts.length; i++) { + if (parts[i] !== domparts[i]) { + matches = false; + break; + } + } + + return matches; +} diff --git a/packages/pac-resolver/src/myIpAddress.ts b/packages/pac-resolver/src/myIpAddress.ts new file mode 100644 index 00000000..9ec9ff5a --- /dev/null +++ b/packages/pac-resolver/src/myIpAddress.ts @@ -0,0 +1,42 @@ +import ip from 'ip'; +import net, { AddressInfo } from 'net'; + +/** + * Returns the IP address of the host that the Navigator is running on, as + * a string in the dot-separated integer format. + * + * Example: + * + * ``` js + * myIpAddress() + * // would return the string "198.95.249.79" if you were running the + * // Navigator on that host. + * ``` + * + * @return {String} external IP address + */ +export default async function myIpAddress(): Promise { + return new Promise((resolve, reject) => { + // 8.8.8.8:53 is "Google Public DNS": + // https://developers.google.com/speed/public-dns/ + const socket = net.connect({ host: '8.8.8.8', port: 53 }); + const onError = () => { + // if we fail to access Google DNS (as in firewall blocks access), + // fallback to querying IP locally + resolve(ip.address()); + }; + socket.once('error', onError); + socket.once('connect', () => { + socket.removeListener('error', onError); + const addr = socket.address(); + socket.destroy(); + if (typeof addr === 'string') { + resolve(addr); + } else if ((addr as AddressInfo).address) { + resolve((addr as AddressInfo).address); + } else { + reject(new Error('Expected a `string`')); + } + }); + }); +} diff --git a/packages/pac-resolver/src/shExpMatch.ts b/packages/pac-resolver/src/shExpMatch.ts new file mode 100644 index 00000000..19424009 --- /dev/null +++ b/packages/pac-resolver/src/shExpMatch.ts @@ -0,0 +1,40 @@ +/** + * Returns true if the string matches the specified shell + * expression. + * + * Actually, currently the patterns are shell expressions, + * not regular expressions. + * + * Examples: + * + * ``` js + * shExpMatch("http://home.netscape.com/people/ari/index.html", "*\/ari/*") + * // is true. + * + * shExpMatch("http://home.netscape.com/people/montulli/index.html", "*\/ari/*") + * // is false. + * ``` + * + * @param {String} str is any string to compare (e.g. the URL, or the hostname). + * @param {String} shexp is a shell expression to compare against. + * @return {Boolean} true if the string matches the shell expression. + */ + +export default function shExpMatch(str: string, shexp: string): boolean { + const re = toRegExp(shexp); + return re.test(str); +} + +/** + * Converts a "shell expression" to a JavaScript RegExp. + * + * @api private + */ + +function toRegExp(str: string): RegExp { + str = String(str) + .replace(/\./g, '\\.') + .replace(/\?/g, '.') + .replace(/\*/g, '.*'); + return new RegExp(`^${str}$`); +} diff --git a/packages/pac-resolver/src/timeRange.ts b/packages/pac-resolver/src/timeRange.ts new file mode 100644 index 00000000..18f8327a --- /dev/null +++ b/packages/pac-resolver/src/timeRange.ts @@ -0,0 +1,114 @@ +/** + * True during (or between) the specified time(s). + * + * Even though the examples don't show it, this parameter may be present in + * each of the different parameter profiles, always as the last parameter. + * + * + * Examples: + * + * ``` js + * timerange(12) + * true from noon to 1pm. + * + * timerange(12, 13) + * same as above. + * + * timerange(12, "GMT") + * true from noon to 1pm, in GMT timezone. + * + * timerange(9, 17) + * true from 9am to 5pm. + * + * timerange(8, 30, 17, 00) + * true from 8:30am to 5:00pm. + * + * timerange(0, 0, 0, 0, 0, 30) + * true between midnight and 30 seconds past midnight. + * ``` + * + * timeRange(hour) + * timeRange(hour1, hour2) + * timeRange(hour1, min1, hour2, min2) + * timeRange(hour1, min1, sec1, hour2, min2, sec2) + * timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt) + * + * @param {String} hour is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.) + * @param {String} min minutes from 0 to 59. + * @param {String} sec seconds from 0 to 59. + * @param {String} gmt either the string "GMT" for GMT timezone, or not specified, for local timezone. + * @return {Boolean} + */ + +export default function timeRange(): boolean { + // eslint-disable-next-line prefer-rest-params + const args = Array.prototype.slice.call(arguments); + const lastArg = args.pop(); + const useGMTzone = lastArg === 'GMT'; + const currentDate = new Date(); + + if (!useGMTzone) { + args.push(lastArg); + } + + let result = false; + const noOfArgs = args.length; + const numericArgs = args.map((n) => parseInt(n, 10)); + + // timeRange(hour) + if (noOfArgs === 1) { + result = getCurrentHour(useGMTzone, currentDate) === numericArgs[0]; + + // timeRange(hour1, hour2) + } else if (noOfArgs === 2) { + const currentHour = getCurrentHour(useGMTzone, currentDate); + result = numericArgs[0] <= currentHour && currentHour < numericArgs[1]; + + // timeRange(hour1, min1, hour2, min2) + } else if (noOfArgs === 4) { + result = valueInRange( + secondsElapsedToday(numericArgs[0], numericArgs[1], 0), + secondsElapsedToday( + getCurrentHour(useGMTzone, currentDate), + getCurrentMinute(useGMTzone, currentDate), + 0 + ), + secondsElapsedToday(numericArgs[2], numericArgs[3], 59) + ); + + // timeRange(hour1, min1, sec1, hour2, min2, sec2) + } else if (noOfArgs === 6) { + result = valueInRange( + secondsElapsedToday(numericArgs[0], numericArgs[1], numericArgs[2]), + secondsElapsedToday( + getCurrentHour(useGMTzone, currentDate), + getCurrentMinute(useGMTzone, currentDate), + getCurrentSecond(useGMTzone, currentDate) + ), + secondsElapsedToday(numericArgs[3], numericArgs[4], numericArgs[5]) + ); + } + + return result; +} + +function secondsElapsedToday(hh: number, mm: number, ss: number): number { + return hh * 3600 + mm * 60 + ss; +} + +function getCurrentHour(gmt: boolean, currentDate: Date): number { + return gmt ? currentDate.getUTCHours() : currentDate.getHours(); +} + +function getCurrentMinute(gmt: boolean, currentDate: Date): number { + return gmt ? currentDate.getUTCMinutes() : currentDate.getMinutes(); +} + +function getCurrentSecond(gmt: boolean, currentDate: Date): number { + return gmt ? currentDate.getUTCSeconds() : currentDate.getSeconds(); +} + +// start <= value <= finish +function valueInRange(start: number, value: number, finish: number): boolean { + return start <= value && value <= finish; +} diff --git a/packages/pac-resolver/src/util.ts b/packages/pac-resolver/src/util.ts new file mode 100644 index 00000000..3ff31bd4 --- /dev/null +++ b/packages/pac-resolver/src/util.ts @@ -0,0 +1,21 @@ +import { LookupAddress, LookupOptions, lookup } from 'dns'; +import { GMT } from './index'; + +export function dnsLookup( + host: string, + opts: LookupOptions +): Promise { + return new Promise((resolve, reject) => { + lookup(host, opts, (err, res) => { + if (err) { + reject(err); + } else { + resolve(res); + } + }); + }); +} + +export function isGMT(v?: string): v is GMT { + return v === 'GMT'; +} diff --git a/packages/pac-resolver/src/weekdayRange.ts b/packages/pac-resolver/src/weekdayRange.ts new file mode 100644 index 00000000..7e4b033f --- /dev/null +++ b/packages/pac-resolver/src/weekdayRange.ts @@ -0,0 +1,100 @@ +import { isGMT } from './util'; +import { GMT, Weekday } from './index'; + +const weekdays: Weekday[] = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT']; + +/** + * Only the first parameter is mandatory. Either the second, the third, or both + * may be left out. + * + * If only one parameter is present, the function yeilds a true value on the + * weekday that the parameter represents. If the string "GMT" is specified as + * a second parameter, times are taken to be in GMT, otherwise in local timezone. + * + * If both wd1 and wd1 are defined, the condition is true if the current weekday + * is in between those two weekdays. Bounds are inclusive. If the "GMT" parameter + * is specified, times are taken to be in GMT, otherwise the local timezone is + * used. + * + * Valid "weekday strings" are: + * + * SUN MON TUE WED THU FRI SAT + * + * Examples: + * + * ``` js + * weekdayRange("MON", "FRI") + * true Monday trhough Friday (local timezone). + * + * weekdayRange("MON", "FRI", "GMT") + * same as above, but GMT timezone. + * + * weekdayRange("SAT") + * true on Saturdays local time. + * + * weekdayRange("SAT", "GMT") + * true on Saturdays GMT time. + * + * weekdayRange("FRI", "MON") + * true Friday through Monday (note, order does matter!). + * ``` + * + * + * @param {String} wd1 one of the weekday strings. + * @param {String} wd2 one of the weekday strings. + * @param {String} gmt is either the string: GMT or is left out. + * @return {Boolean} + */ + +export default function weekdayRange( + wd1: Weekday, + wd2?: Weekday | GMT, + gmt?: GMT +): boolean { + let useGMTzone = false; + let wd1Index = -1; + let wd2Index = -1; + let wd2IsGmt = false; + + if (isGMT(gmt)) { + useGMTzone = true; + } else if (isGMT(wd2)) { + useGMTzone = true; + wd2IsGmt = true; + } + + wd1Index = weekdays.indexOf(wd1); + + if (!wd2IsGmt && isWeekday(wd2)) { + wd2Index = weekdays.indexOf(wd2); + } + + const todaysDay = getTodaysDay(useGMTzone); + let result: boolean; + + if (wd2Index < 0) { + result = todaysDay === wd1Index; + } else if (wd1Index <= wd2Index) { + result = valueInRange(wd1Index, todaysDay, wd2Index); + } else { + result = + valueInRange(wd1Index, todaysDay, 6) || + valueInRange(0, todaysDay, wd2Index); + } + + return result; +} + +function getTodaysDay(gmt: boolean): number { + return gmt ? new Date().getUTCDay() : new Date().getDay(); +} + +// start <= value <= finish +function valueInRange(start: number, value: number, finish: number): boolean { + return start <= value && value <= finish; +} + +function isWeekday(v?: string): v is Weekday { + if (!v) return false; + return (weekdays as string[]).includes(v); +} diff --git a/packages/pac-resolver/test/dnsDomainIs.js b/packages/pac-resolver/test/dnsDomainIs.js new file mode 100644 index 00000000..928bef54 --- /dev/null +++ b/packages/pac-resolver/test/dnsDomainIs.js @@ -0,0 +1,24 @@ +/** + * Module dependencies. + */ + +var assert = require('assert'); +const { dnsDomainIs } = require('../').sandbox; + +describe('dnsDomainIs(host, domain)', function () { + var tests = [ + ['www.netscape.com', '.netscape.com', true], + ['www', '.netscape.com', false], + ['www.mcom.com', '.netscape.com', false], + ]; + + tests.forEach(function (test) { + var expected = test.pop(); + it( + 'should return `' + expected + '` for "' + test.join('", "') + '"', + function () { + assert.equal(expected, dnsDomainIs(test[0], test[1])); + } + ); + }); +}); diff --git a/packages/pac-resolver/test/dnsDomainLevels.js b/packages/pac-resolver/test/dnsDomainLevels.js new file mode 100644 index 00000000..7f503b86 --- /dev/null +++ b/packages/pac-resolver/test/dnsDomainLevels.js @@ -0,0 +1,24 @@ +/** + * Module dependencies. + */ + +var assert = require('assert'); +var { dnsDomainLevels } = require('../').sandbox; + +describe('dnsDomainLevels(host)', function () { + var tests = [ + ['www', 0], + ['www.netscape', 1], + ['www.netscape.com', 2], + ]; + + tests.forEach(function (test) { + var expected = test.pop(); + it( + 'should return `' + expected + '` for "' + test.join('", "') + '"', + function () { + assert.equal(expected, dnsDomainLevels(test[0])); + } + ); + }); +}); diff --git a/packages/pac-resolver/test/dnsResolve.js b/packages/pac-resolver/test/dnsResolve.js new file mode 100644 index 00000000..d05157dd --- /dev/null +++ b/packages/pac-resolver/test/dnsResolve.js @@ -0,0 +1,44 @@ +/** + * Module dependencies. + */ + +var isIP = require('net').isIP; +var assert = require('assert'); +var { dnsResolve } = require('../').sandbox; + +describe('dnsResolve(host)', function () { + var tests = [ + ['www.netscape.com', true], + ['bogus.domain.foobar', false], + ]; + + tests.forEach(function (test) { + var expected = test.pop(); + if (expected) { + it( + 'should resolve an IPv4 address for "' + + test.join('", "') + + '"', + function (done) { + dnsResolve(test[0]).then((res) => { + assert.equal('string', typeof res); + assert.equal(4, isIP(res)); + done(); + }, done); + } + ); + } else { + it( + 'should return null for if can\'t be resolved "' + + test.join('", "') + + '"', + function (done) { + dnsResolve(test[0]).then((res) => { + assert.equal(null, res); + done(); + }, done); + } + ); + } + }); +}); diff --git a/packages/pac-resolver/test/fixtures/gfw_whitelist.pac b/packages/pac-resolver/test/fixtures/gfw_whitelist.pac new file mode 100644 index 00000000..fe840043 --- /dev/null +++ b/packages/pac-resolver/test/fixtures/gfw_whitelist.pac @@ -0,0 +1,14342 @@ +var wall_proxy = "SOCKS5 127.0.0.1:1080;"; +var nowall_proxy = "DIRECT;"; +var direct = "DIRECT;"; +var auto_proxy = wall_proxy; // if you have something like COW proxy +var ip_proxy = nowall_proxy; + +/* + * Copyright (C) 2014 breakwa11 + * https://github.com/breakwa11/gfw_whitelist + */ + +var white_domains = {"am":{ +"126":1, +"51":1 +},"biz":{ +"7daysinn":1, +"baozhuang":1, +"bengfa":1, +"changan":1, +"chinafastener":1, +"chongchuang":1, +"dahuoji":1, +"diandongche":1, +"diaoding":1, +"fishings":1, +"hanjie":1, +"intil":1, +"kuangchan":1, +"menchuang":1, +"qianyan":1, +"rohlan":1, +"runhuayou":1, +"shiyongjun":1, +"shoutao":1, +"tongye":1, +"yuanyi":1, +"zhaoming":1 +},"cc":{ +"0316":1, +"0738":1, +"163k":1, +"1news":1, +"21cp":1, +"2che":1, +"2ic":1, +"3456":1, +"365bh":1, +"509":1, +"55":1, +"55g":1, +"5648":1, +"592wg":1, +"6wang":1, +"7190":1, +"77wan":1, +"7mo":1, +"8682":1, +"9844":1, +"ahtc":1, +"ahyx":1, +"air":1, +"anhui":1, +"anqiu":1, +"aoyou":1, +"atax":1, +"baise":1, +"bamaol":1, +"bczx":1, +"bendiw":1, +"bjjf":1, +"byr":1, +"cccity":1, +"cctw":1, +"chengche":1, +"chinatimes":1, +"chuban":1, +"civilaviation":1, +"comac":1, +"cqnc":1, +"d17":1, +"denglu":1, +"dker":1, +"donglingying":1, +"dqccc":1, +"dyj":1, +"ejiaju":1, +"en":1, +"eqz":1, +"ewt":1, +"fengfeng":1, +"fm93":1, +"gczx":1, +"gf":1, +"globalbuy":1, +"gmtv":1, +"gqw":1, +"haitou":1, +"hao315":1, +"hb114":1, +"hefei":1, +"heyang":1, +"heze":1, +"hezerencai":1, +"hongmen":1, +"hty":1, +"huaer":1, +"huoshan":1, +"huxi":1, +"icoat":1, +"jiangxia":1, +"jinnong":1, +"jinxian":1, +"jinxun":1, +"jz":1, +"kjw":1, +"kuge":1, +"lbx":1, +"ldz":1, +"liaoba":1, +"liulin":1, +"longyu":1, +"loyo":1, +"lrd":1, +"lyrc":1, +"miit":1, +"moko":1, +"mzsky":1, +"n21":1, +"nandagang":1, +"pingyin":1, +"pinjie":1, +"pp":1, +"pp6":1, +"pubone":1, +"pyedu":1, +"qcr":1, +"qqzl":1, +"qxw":1, +"rc":1, +"ruixing":1, +"seeyoo":1, +"sg8":1, +"sgnet":1, +"sh021":1, +"shanhe":1, +"shiyan":1, +"shuichan":1, +"sjhl":1, +"sjz":1, +"souge":1, +"souyi":1, +"starbaby":1, +"suzhou":1, +"suzo":1, +"tcnews":1, +"tcxw":1, +"teambuy":1, +"tkw":1, +"tuku":1, +"tyn":1, +"ujian":1, +"utt":1, +"uu":1, +"uyan":1, +"vfe":1, +"wandu":1, +"webportal":1, +"weishan":1, +"wuqing":1, +"wutongxiang":1, +"wzcn":1, +"xgz":1, +"xialingying":1, +"xidian":1, +"xinzheng":1, +"xszp":1, +"yahui":1, +"yc":1, +"yl114":1, +"ytrc":1, +"ytx":1, +"yutian":1, +"yxi":1, +"yzw":1, +"zg5":1, +"zhibo8":1, +"zhuwang":1, +"zmsc":1, +"zyqc":1 +},"cm":{ +"4":1, +"60":1, +"bearing":1, +"hebei":1, +"yinshua":1 +},"co":{ +"425300":1, +"banzhu":1, +"hongfeng":1, +"huas":1, +"lixin":1, +"xiaomayi":1, +"xiapu":1, +"ychdzx":1 +},"com":{ +"0-6":1, +"0001688":1, +"001cndc":1, +"001en":1, +"001jm":1, +"001job":1, +"001pp":1, +"001sxy":1, +"001uk":1, +"001xin":1, +"007swz":1, +"00817":1, +"0086gd":1, +"01-123":1, +"010lf":1, +"01dai":1, +"01dianzi":1, +"01hr":1, +"01w":1, +"01wed":1, +"020":1, +"020h":1, +"020job":1, +"0214":1, +"022china":1, +"022net":1, +"022s":1, +"022v":1, +"0231230":1, +"023zp":1, +"024zxw":1, +"025ct":1, +"025syedu":1, +"025xl":1, +"025zp":1, +"027art":1, +"029k":1, +"0312mc":1, +"0352fang":1, +"0356f":1, +"0357hz":1, +"0371gaokao":1, +"0371job":1, +"0377auto":1, +"0379city":1, +"0384":1, +"0396e":1, +"03th":1, +"0411hd":1, +"04168":1, +"0418fc":1, +"0427":1, +"0427qcw":1, +"0430":1, +"0437":1, +"0460":1, +"0470a":1, +"0471fcw":1, +"0510syedu":1, +"0512zp":1, +"0514":1, +"0514rj":1, +"051591":1, +"0517cw":1, +"0517w":1, +"051jk":1, +"0523zp":1, +"05273":1, +"0531":1, +"0533":1, +"0535-0411":1, +"0537tt":1, +"0537zp":1, +"0538fc":1, +"0543hr":1, +"0546fdc":1, +"0550":1, +"055178":1, +"0551fangchan":1, +"0552jie":1, +"0556zx":1, +"0558":1, +"0558t":1, +"0559fc":1, +"0561house":1, +"0563job":1, +"0564luan":1, +"0566fc":1, +"0566job":1, +"0567":1, +"0570fc":1, +"057191":1, +"0571car":1, +"0573ren":1, +"0575360":1, +"0575bbs":1, +"0575life":1, +"057650":1, +"0577cnw":1, +"0577hr":1, +"0577job":1, +"0578rencai":1, +"0578zhaopin":1, +"0579com":1, +"0591job":1, +"05927":1, +"0594":1, +"0595job":1, +"0595rc":1, +"0596fc":1, +"0598job":1, +"0598rc":1, +"059iu":1, +"05info":1, +"05sun":1, +"060s":1, +"0634":1, +"0663job":1, +"06abc":1, +"07073":1, +"07073sy":1, +"0710go":1, +"0712fang":1, +"0715rc":1, +"0715zp":1, +"07177":1, +"0719house":1, +"0722fc":1, +"0728f":1, +"0730188":1, +"0730news":1, +"0731fdc":1, +"0731jiaju":1, +"0731job":1, +"0734":1, +"0734zpw":1, +"0735":1, +"07358":1, +"0735jz":1, +"0735zx":1, +"0736fdc":1, +"0739tt":1, +"073yx":1, +"0743063":1, +"07430743":1, +"0746news":1, +"0750rc":1, +"0752qc":1, +"0755888":1, +"0755caibao":1, +"0755car":1, +"0755rc":1, +"0756home":1, +"0756tong":1, +"0757fc":1, +"0757rc":1, +"0760":1, +"0760rc":1, +"076299":1, +"076650":1, +"0769che":1, +"0771rc":1, +"0771td":1, +"0772fang":1, +"0772job":1, +"0775fcw":1, +"0775jzw":1, +"0791look":1, +"0791quanquan":1, +"0797rs":1, +"07jm":1, +"07ka":1, +"08115":1, +"0818work":1, +"0831che":1, +"0832mh":1, +"0835":1, +"0838":1, +"0838live":1, +"0852diaoyu":1, +"0852job":1, +"0853rc":1, +"0854job":1, +"0855job":1, +"0856job":1, +"0857job":1, +"0859job":1, +"0890":1, +"0891zp":1, +"0898100":1, +"0898dichan":1, +"08cms":1, +"08px":1, +"0902rc":1, +"0914cn":1, +"0917":1, +"0919123":1, +"0921":1, +"0931lanzhou":1, +"0935":1, +"09451":1, +"0951job":1, +"09635":1, +"0991dj":1, +"099sky":1, +"09jz":1, +"100":1, +"100-tong":1, +"10000fang":1, +"10000job":1, +"10000link":1, +"10000tc":1, +"1000tuan":1, +"10010":1, +"100580":1, +"100afrc":1, +"100ayrc":1, +"100bt":1, +"100cyrc":1, +"100dnrc":1, +"100dyrc":1, +"100gcrc":1, +"100gxrc":1, +"100hcrc":1, +"100jgsrc":1, +"100jsrc":1, +"100lcrc":1, +"100lnrc":1, +"100ndrc":1, +"100njz":1, +"100nkrc":1, +"100ppi":1, +"100qnrc":1, +"100rjrc":1, +"100scrc":1, +"100t":1, +"100thrc":1, +"100warc":1, +"100xfrc":1, +"100xgrc":1, +"100xiao":1, +"100xuexi":1, +"100xwrc":1, +"100ydrc":1, +"100ye":1, +"100yfrc":1, +"100yiyao":1, +"100yxrc":1, +"100zhuang":1, +"101":1, +"1010jz":1, +"10155":1, +"101jiajiao":1, +"1024sj":1, +"10339":1, +"1039soft":1, +"1065m":1, +"108sq":1, +"10fang":1, +"10huan":1, +"10s1":1, +"10yan":1, +"110":1, +"1111":1, +"111jz":1, +"111ttt":1, +"11315":1, +"114160":1, +"11464":1, +"11467":1, +"114best":1, +"114chn":1, +"114hrb":1, +"114huoche":1, +"114ic":1, +"114jc":1, +"114jcw":1, +"114la":1, +"114max":1, +"114piaowu":1, +"114px":1, +"114qy":1, +"114study":1, +"115":1, +"115800":1, +"115img":1, +"115kf":1, +"1166":1, +"11773":1, +"117800":1, +"1188":1, +"118rc":1, +"11919":1, +"119g":1, +"11chuangye":1, +"11jk":1, +"120-job":1, +"120ask":1, +"120askimages":1, +"12114job":1, +"12114rc":1, +"121314":1, +"122park":1, +"12333sb":1, +"1234wu":1, +"12365auto":1, +"123cha":1, +"123fj":1, +"123lvxing":1, +"123xun":1, +"123youhuo":1, +"12530":1, +"125job":1, +"126":1, +"128qd":1, +"128uu":1, +"12999":1, +"12yao":1, +"131cc":1, +"133jz":1, +"135edu":1, +"1360":1, +"136hr":1, +"1377":1, +"137home":1, +"138edu":1, +"138jm":1, +"138job":1, +"138mr":1, +"139":1, +"139life":1, +"139shop":1, +"13ejob":1, +"13pr":1, +"148-law":1, +"1488":1, +"15153":1, +"1518":1, +"1545ts":1, +"155":1, +"156580":1, +"15666":1, +"15880":1, +"159":1, +"15hr":1, +"15sjw":1, +"15w":1, +"160":1, +"161gg":1, +"163":1, +"163disk":1, +"163k":1, +"163yu":1, +"164580":1, +"1666":1, +"1688":1, +"16886000":1, +"16888":1, +"1688wan":1, +"168dc":1, +"168hm":1, +"168hs":1, +"168job":1, +"168rc":1, +"168tea":1, +"168tex":1, +"168xiezi":1, +"16999":1, +"16njl":1, +"16sucai":1, +"16tz":1, +"17":1, +"17173":1, +"17173cdn":1, +"1717518":1, +"1717kf":1, +"1717pk":1, +"1718001":1, +"1718china":1, +"1718world":1, +"172xiaoyuan":1, +"173":1, +"1732":1, +"173daxue":1, +"173eg":1, +"173py":1, +"173zy":1, +"175game":1, +"175kh":1, +"176":1, +"17611":1, +"17673":1, +"178":1, +"17888":1, +"178good":1, +"178zmy":1, +"179179":1, +"1797wan":1, +"17baba":1, +"17dm":1, +"17game":1, +"17heli":1, +"17house":1, +"17k":1, +"17liansuo":1, +"17oh":1, +"17ok":1, +"17pr":1, +"17sucai":1, +"17u":1, +"17ugo":1, +"17yy":1, +"17zwd":1, +"18183":1, +"1818hm":1, +"188":1, +"188cyw":1, +"189house":1, +"189rc":1, +"189store":1, +"18dao":1, +"18ph":1, +"18qiang":1, +"18touch":1, +"18yl":1, +"1905":1, +"197c":1, +"198526":1, +"198game":1, +"199it":1, +"19lou":1, +"19yxw":1, +"19zhan":1, +"1dufish":1, +"1dusou":1, +"1dutm":1, +"1gbru":1, +"1kejian":1, +"1m3d":1, +"1mall":1, +"1mishu":1, +"1mit":1, +"1n11":1, +"1nongjing":1, +"1nsou":1, +"1peixun":1, +"1qwe3r":1, +"1stjc":1, +"1t1t":1, +"1techan":1, +"1textile":1, +"1ting":1, +"1v1offcn":1, +"1wandian":1, +"1y2y":1, +"1youxi":1, +"1zhanok":1, +"1zjob":1, +"1zw":1, +"2000888":1, +"2008red":1, +"200tc":1, +"2011n":1, +"21-cmjob":1, +"21-rent":1, +"21-sun":1, +"21-used":1, +"2100book":1, +"210go":1, +"211600":1, +"211lx":1, +"212300":1, +"2125":1, +"2197079":1, +"21caas":1, +"21cbh":1, +"21ccnn":1, +"21cn":1, +"21cnhr":1, +"21cnimg":1, +"21cnjy":1, +"21cp":1, +"21dagong":1, +"21dianyuan":1, +"21dnn":1, +"21edu8":1, +"21food":1, +"21hospital":1, +"21hubei":1, +"21ic":1, +"21js":1, +"21momo":1, +"21our":1, +"21part":1, +"21pw":1, +"21rcw":1, +"21rv":1, +"21so":1, +"21tb":1, +"21tyn":1, +"21wecan":1, +"21wenju":1, +"21xc":1, +"2200book":1, +"221400job":1, +"221700":1, +"224700":1, +"226500":1, +"226y":1, +"2298":1, +"22edu":1, +"233":1, +"233000":1, +"233863":1, +"2344":1, +"2345":1, +"2366":1, +"238200":1, +"23ks":1, +"246xf":1, +"24jz":1, +"24k99":1, +"2500sz":1, +"253u":1, +"258":1, +"258en":1, +"25nc":1, +"25pp":1, +"25yz":1, +"263":1, +"263xmail":1, +"264g":1, +"265":1, +"26595":1, +"265g":1, +"2688":1, +"26abc":1, +"28":1, +"2881":1, +"28hotel":1, +"28sn":1, +"28yj":1, +"2cto":1, +"2ge8":1, +"2hua":1, +"2m2j":1, +"2mjob":1, +"2mould":1, +"2pjob":1, +"2shequ":1, +"2shoujie":1, +"2smtc":1, +"300p":1, +"301688":1, +"30556":1, +"308308":1, +"30edu":1, +"310win":1, +"311100":1, +"312168":1, +"312green":1, +"313":1, +"3145":1, +"3155":1, +"3158":1, +"315che":1, +"315hyw":1, +"315online":1, +"315weishi":1, +"31alu":1, +"31bear":1, +"31bxg":1, +"31byq":1, +"31bzjx":1, +"31expo":1, +"31food":1, +"31gcjx":1, +"31gear":1, +"31huiyi":1, +"31jc":1, +"31jgj":1, +"31jiaju":1, +"31jmw":1, +"31jxw":1, +"31mada":1, +"31mold":1, +"31pump":1, +"31rzp":1, +"31seal":1, +"31spjx":1, +"31taoci":1, +"31wj":1, +"31xjd":1, +"31yj":1, +"31zscl":1, +"320106":1, +"320921":1, +"321200":1, +"321cy":1, +"3234":1, +"323g":1, +"32800":1, +"32wan":1, +"3310":1, +"332527":1, +"333job":1, +"333ku":1, +"33519":1, +"3366":1, +"337y":1, +"33lc":1, +"33ly":1, +"33map":1, +"33or":1, +"3454":1, +"34job":1, +"35":1, +"350ban":1, +"352":1, +"352200":1, +"3533":1, +"3566t":1, +"35941":1, +"35nic":1, +"35q":1, +"35rc":1, +"35tool":1, +"3603":1, +"3608":1, +"36099":1, +"360aiyi":1, +"360buy":1, +"360buyimg":1, +"360che":1, +"360chuguo":1, +"360doc":1, +"360doo":1, +"360eol":1, +"360hun":1, +"360hx":1, +"360hy":1, +"360kad":1, +"360kan":1, +"360kxr":1, +"360safe":1, +"360top":1, +"360wbl":1, +"360wyw":1, +"360xh":1, +"3618med":1, +"361games":1, +"364000":1, +"365128":1, +"36578":1, +"36588zs":1, +"365a8":1, +"365ajw":1, +"365anfang":1, +"365art":1, +"365auto":1, +"365azw":1, +"365bj":1, +"365cgw":1, +"365exam":1, +"365heart":1, +"365jilin":1, +"365mo":1, +"365rili":1, +"365sji":1, +"365tex":1, +"365webcall":1, +"365zhaosheng":1, +"366x24":1, +"368tea":1, +"36hjob":1, +"36kr":1, +"36mc":1, +"36nz":1, +"37":1, +"371":1, +"37168":1, +"371house":1, +"3737":1, +"3737k":1, +"373f":1, +"373house":1, +"37937":1, +"37cs":1, +"37kfb":1, +"37nixi":1, +"37wan":1, +"37wanimg":1, +"3817":1, +"3839":1, +"387a":1, +"3937":1, +"3987":1, +"39yss":1, +"39yst":1, +"3conline":1, +"3d66":1, +"3dfc":1, +"3dkezhan":1, +"3dmgame":1, +"3dwwwgame":1, +"3ghuashang":1, +"3kfw":1, +"3kk":1, +"3lian":1, +"3n110":1, +"3qhouse":1, +"3r66":1, +"3s001":1, +"3see":1, +"3xgd":1, +"400516":1, +"4006666688":1, +"4008000000":1, +"4008885166":1, +"400jz":1, +"40279":1, +"404000":1, +"40407":1, +"405400":1, +"411au":1, +"42144":1, +"425300":1, +"435200":1, +"4355":1, +"4399":1, +"4399dmw":1, +"4399j":1, +"4399sy":1, +"45575":1, +"458hospital":1, +"45fan":1, +"45rc":1, +"45win":1, +"46518":1, +"47365":1, +"4738":1, +"4765":1, +"4779":1, +"488u":1, +"49you":1, +"4gfy":1, +"4yang":1, +"500":1, +"50018":1, +"500wan":1, +"5054399":1, +"5068":1, +"508job":1, +"51":1, +"51-cf":1, +"510560":1, +"5117":1, +"5120":1, +"51240":1, +"51511":1, +"5156edu":1, +"5156rcw":1, +"5173":1, +"5173cdn":1, +"51766":1, +"5179":1, +"517best":1, +"517huizhou":1, +"517tez":1, +"5184":1, +"518ad":1, +"519d":1, +"519dian":1, +"51aimei":1, +"51auto":1, +"51bafu":1, +"51bi":1, +"51buy":1, +"51bxg":1, +"51chudui":1, +"51chuli":1, +"51cnhr":1, +"51comp":1, +"51credit":1, +"51cto":1, +"51daifu":1, +"51ditu":1, +"51dzrc":1, +"51dzw":1, +"51edu":1, +"51etong":1, +"51ey":1, +"51fanli":1, +"51fdc":1, +"51flrc":1, +"51g3":1, +"51gaifang":1, +"51haojob":1, +"51hcw":1, +"51hejia":1, +"51iec":1, +"51ielts":1, +"51img1":1, +"51jam":1, +"51jiameng":1, +"51jiaxiao":1, +"51jiemeng":1, +"51jingke":1, +"51jishu":1, +"51jiuhuo":1, +"51job":1, +"51jobcdn":1, +"51jyrc":1, +"51kids":1, +"51kqn":1, +"51langtu":1, +"51liucheng":1, +"51lunwen":1, +"51lyrc":1, +"51meishu":1, +"51mingche":1, +"51mobilejob":1, +"51mole":1, +"51mp3ring":1, +"51nuoqi":1, +"51offer":1, +"51oscar":1, +"51pla":1, +"51qc":1, +"51qingjiao":1, +"51rc":1, +"51rencai":1, +"51report":1, +"51seer":1, +"51sheyuan":1, +"51sole":1, +"51t":1, +"51talk":1, +"51talkenglish":1, +"51taonan":1, +"51taoshi":1, +"51tie":1, +"51touch":1, +"51ttyy":1, +"51tz":1, +"51valves":1, +"51wan":1, +"51wf":1, +"51wj":1, +"51wjrc":1, +"51wyrc":1, +"51xxr":1, +"51yala":1, +"51yes":1, +"51yey":1, +"51you":1, +"51youcai":1, +"51yougo":1, +"51ysrc":1, +"51yunli":1, +"51zhantai":1, +"51zhucai":1, +"51zjxm":1, +"51zsjc":1, +"51ztzj":1, +"51zuoche":1, +"51zupu":1, +"51zx":1, +"51zyrc":1, +"520":1, +"520520520520520":1, +"520apk":1, +"520bn":1, +"520e3e4":1, +"520love520":1, +"520wawa":1, +"521che":1, +"521g":1, +"5234444":1, +"52372":1, +"5253":1, +"526wan":1, +"527pk":1, +"52as":1, +"52bar":1, +"52bendi":1, +"52bus":1, +"52che":1, +"52da":1, +"52design":1, +"52djq":1, +"52fangzi":1, +"52fuqing":1, +"52guixi":1, +"52hardware":1, +"52hxw":1, +"52jscn":1, +"52njl":1, +"52pk":1, +"52solution":1, +"52ykjob":1, +"52yuanm":1, +"52z":1, +"52zhushan":1, +"531city":1, +"533":1, +"5336":1, +"538538":1, +"5399":1, +"53kf":1, +"54086":1, +"54114":1, +"5433":1, +"54heb":1, +"54jj":1, +"54job":1, +"55":1, +"550400":1, +"55188":1, +"55bbs":1, +"55tuan":1, +"55tuanimg":1, +"55weixiu":1, +"55you":1, +"56":1, +"56156":1, +"5617":1, +"56360":1, +"5652":1, +"5654":1, +"566855":1, +"5669":1, +"566job":1, +"56china":1, +"56en":1, +"56img":1, +"56ml":1, +"56mp":1, +"56qss":1, +"57023":1, +"5741886":1, +"576":1, +"57616":1, +"576tv":1, +"5778":1, +"577fang":1, +"57821":1, +"57go":1, +"57info":1, +"57qy":1, +"57tibet":1, +"57tuan":1, +"58":1, +"580k":1, +"582hr":1, +"5858":1, +"586jz":1, +"5874":1, +"587766":1, +"58dm":1, +"58fenlei":1, +"58food":1, +"58game":1, +"58guakao":1, +"58house":1, +"58pic":1, +"58sing":1, +"59120":1, +"59178":1, +"591hx":1, +"591wed":1, +"591wy":1, +"5925car":1, +"596fc":1, +"597":1, +"59706":1, +"597rcw":1, +"598rc":1, +"59kankan":1, +"5ajob":1, +"5d6d":1, +"5est":1, +"5etv":1, +"5fen":1, +"5g":1, +"5huu":1, +"5i5j":1, +"5i9u":1, +"5ikfc":1, +"5ipatent":1, +"5its":1, +"5iucn":1, +"5iyq":1, +"5jzp":1, +"5khouse":1, +"5lejob":1, +"5read":1, +"5sai":1, +"5sw":1, +"5u588":1, +"5w":1, +"6-china":1, +"60malaysia":1, +"61":1, +"6103":1, +"61166":1, +"612345":1, +"6164":1, +"6168511":1, +"6188":1, +"618hr":1, +"61baobao":1, +"61bbw":1, +"61ertong":1, +"61flash":1, +"61mami":1, +"628":1, +"632news":1, +"64365":1, +"646000":1, +"64ma":1, +"65":1, +"651700":1, +"6528":1, +"65singapore":1, +"65wan":1, +"66163":1, +"6637":1, +"6665":1, +"66667676":1, +"666ccc":1, +"6676":1, +"6677000":1, +"66880":1, +"668city":1, +"668map":1, +"6695":1, +"66diqiu":1, +"66dt":1, +"66house":1, +"66liu":1, +"66qhd":1, +"66ruian":1, +"66u":1, +"66wc":1, +"66wz":1, +"66xue":1, +"66yj":1, +"66you":1, +"66zhuang":1, +"67":1, +"6711":1, +"678114":1, +"6789uu":1, +"678py":1, +"680":1, +"68211":1, +"685":1, +"688glass":1, +"688n":1, +"68hr":1, +"68zhuan":1, +"6949":1, +"69hr":1, +"69kan":1, +"6eat":1, +"6m":1, +"6tennis":1, +"6v68":1, +"6zrc":1, +"70":1, +"703804":1, +"70e":1, +"70yx":1, +"711g":1, +"7120":1, +"712100":1, +"7192":1, +"71lady":1, +"71lm":1, +"71study":1, +"71zs":1, +"72177":1, +"7230":1, +"7273":1, +"72ce":1, +"72g":1, +"72xuan":1, +"731c":1, +"737":1, +"7377":1, +"7399":1, +"73994":1, +"74cms":1, +"762rc":1, +"7651":1, +"766":1, +"769car":1, +"76jie":1, +"7711":1, +"77313":1, +"774g":1, +"7755":1, +"777zp":1, +"7788":1, +"7789":1, +"7799520":1, +"77hunjia":1, +"77l":1, +"77vcd":1, +"77zxw":1, +"78187":1, +"7878":1, +"78793":1, +"789gg":1, +"78fz":1, +"78hr":1, +"78zph":1, +"79":1, +"7937":1, +"7940":1, +"7979la":1, +"798edu":1, +"799job":1, +"79cha":1, +"79w":1, +"7ahr":1, +"7c":1, +"7caiyun":1, +"7dapei":1, +"7edown":1, +"7fgame":1, +"7hcn":1, +"7hon":1, +"7jia2":1, +"7k35":1, +"7k7k":1, +"7mgame":1, +"7po":1, +"7stk":1, +"7su":1, +"7wsh":1, +"7xz":1, +"7y7":1, +"7yueji":1, +"800020308":1, +"800hr":1, +"800pai":1, +"8014":1, +"80881":1, +"8090yxs":1, +"80halta":1, +"80tian":1, +"81629":1, +"818":1, +"818222":1, +"81tech":1, +"81yy":1, +"8211":1, +"82222919":1, +"82341":1, +"826":1, +"8264":1, +"8265":1, +"828g":1, +"832200":1, +"83480900":1, +"83838":1, +"8384cs":1, +"8385":1, +"84519":1, +"84ktv":1, +"860527":1, +"860598":1, +"862sc":1, +"86516":1, +"8684":1, +"86933":1, +"86anjie":1, +"86djw":1, +"86jobs":1, +"86jzjob":1, +"86kx":1, +"86kyjob":1, +"86lawyer":1, +"86mdo":1, +"86nb":1, +"86office":1, +"86pla":1, +"86qc":1, +"86wan":1, +"86wind":1, +"86zsw":1, +"87188718":1, +"8783":1, +"87pk":1, +"88":1, +"88152":1, +"8844":1, +"88680":1, +"88845678":1, +"88999":1, +"88h3":1, +"88lan":1, +"88mf":1, +"88tc":1, +"88yz":1, +"89178":1, +"895cn":1, +"898tc":1, +"8bo":1, +"8btc":1, +"8dn":1, +"8fkd":1, +"8le8le":1, +"8tennis":1, +"8uka":1, +"8uuzg":1, +"90576":1, +"90tiyu":1, +"90vs":1, +"91":1, +"911cha":1, +"911xs":1, +"913u":1, +"9158":1, +"917":1, +"917rcw":1, +"9188":1, +"9191mr":1, +"9191px":1, +"91985":1, +"91b2b":1, +"91canyin":1, +"91cps":1, +"91danji":1, +"91ddcc":1, +"91huayi":1, +"91jf":1, +"91jm":1, +"91jmw":1, +"91job":1, +"91jsj":1, +"91open":1, +"91px":1, +"91rb":1, +"91student":1, +"91town":1, +"91wan":1, +"91yao":1, +"92gzc":1, +"92wudao":1, +"92wy":1, +"92you":1, +"934dsw":1, +"9355":1, +"9377":1, +"93pk":1, +"93tyy":1, +"94176":1, +"941jy":1, +"9453job":1, +"94i5":1, +"95060":1, +"95081":1, +"95191":1, +"9553":1, +"9564":1, +"9588":1, +"958shop":1, +"95px":1, +"960law":1, +"960rc":1, +"96211":1, +"9637":1, +"96520":1, +"9666sr":1, +"9669":1, +"96963":1, +"969g":1, +"96hq":1, +"96pk":1, +"96u":1, +"9724":1, +"973":1, +"9787":1, +"97973":1, +"97go":1, +"98523":1, +"988001":1, +"988yx":1, +"98player":1, +"98znz":1, +"99":1, +"99114":1, +"99166":1, +"9939":1, +"9949":1, +"996":1, +"9966333":1, +"997788":1, +"998":1, +"999":1, +"9991":1, +"9996270":1, +"999ask":1, +"999brain":1, +"99bill":1, +"99cfw":1, +"99fund":1, +"99huizhou":1, +"99ielts":1, +"99inf":1, +"99jianzhu":1, +"99nahuo":1, +"99pet":1, +"99qh":1, +"99sushe":1, +"99wed":1, +"99xr":1, +"99ys":1, +"99zuowen":1, +"9che":1, +"9chew":1, +"9chun":1, +"9first":1, +"9ht":1, +"9i5c":1, +"9ijr":1, +"9juren":1, +"9k9k":1, +"9ku":1, +"9laodi":1, +"9qc":1, +"9sky":1, +"9to":1, +"9u":1, +"9u8u":1, +"9v8v":1, +"9wee":1, +"9ye":1, +"9yjobtm":1, +"9you":1, +"9zjob":1, +"a0598":1, +"a1166":1, +"a22":1, +"a67":1, +"a688888":1, +"a8":1, +"a9188":1, +"a963":1, +"abab":1, +"abang":1, +"abchina":1, +"ablesky":1, +"accgame":1, +"aci-wh":1, +"acs86":1, +"acshoes":1, +"adaicom":1, +"addpv":1, +"adiic":1, +"admaimai":1, +"admin5":1, +"admin6":1, +"adnxs":1, +"adroll":1, +"adsage":1, +"adsame":1, +"adsonar":1, +"adtechus":1, +"adyun":1, +"aeenets":1, +"af360":1, +"afjk":1, +"afjob88":1, +"aft888":1, +"afzhan":1, +"ag365":1, +"age06":1, +"agrodt":1, +"agrofairs":1, +"agrosg":1, +"ahauto":1, +"ahbys":1, +"ahchanyi":1, +"ahgame":1, +"ahglj":1, +"ahgzdz":1, +"ahjgxy":1, +"ahjtxx":1, +"ahjzw":1, +"ahlags":1, +"ahlib":1, +"ahlife":1, +"ahljnews":1, +"ahmmhg":1, +"ahqmdq":1, +"ahssnews":1, +"ahsyj":1, +"ai96":1, +"aibang":1, +"aibo123":1, +"aicai":1, +"aichao521":1, +"aicunfu":1, +"aideschool":1, +"aidiao":1, +"aidigong":1, +"aidr968":1, +"aifang":1, +"aifangke":1, +"aifcdn":1, +"aifengjie":1, +"aigou":1, +"aihami":1, +"aihandu":1, +"aihuigo":1, +"aija":1, +"aiju":1, +"aiketour":1, +"aili":1, +"ailinzhou":1, +"ailiuxue":1, +"aimuju":1, +"aipai":1, +"airmb":1, +"airtofly":1, +"aituan":1, +"aiyiqu":1, +"aiyouxi":1, +"aizhan":1, +"aizhe58":1, +"aizongyi":1, +"ajkcdn":1, +"ajkimg":1, +"akangdi":1, +"akdanji":1, +"aksxw":1, +"alacun":1, +"aliapp":1, +"alibaba":1, +"alibado":1, +"alibole":1, +"alicdn":1, +"aliexpress":1, +"alifabu":1, +"alihuahua":1, +"aliimg":1, +"aliloan":1, +"alimama":1, +"alipay":1, +"alipayobjects":1, +"aliresearch":1, +"alisoft":1, +"alitrip":1, +"aliunicorn":1, +"alivv":1, +"alixixi":1, +"aliyiyao":1, +"aliyun":1, +"aliyuncs":1, +"alkuyi":1, +"allbrightlaw":1, +"allfang":1, +"allyes":1, +"altxw":1, +"alu1886":1, +"alyisheng":1, +"am89":1, +"amap":1, +"ambow":1, +"ampcn":1, +"anatuprak":1, +"anchi-china":1, +"andaike":1, +"anfan":1, +"anfensi":1, +"angeeks":1, +"angelcrunch":1, +"angelyeast":1, +"anhuaedu":1, +"anhuihr":1, +"anhuijrw":1, +"anhuinews":1, +"animalchina":1, +"anjian":1, +"anjuke":1, +"anjukestatic":1, +"anqingonline":1, +"anqu":1, +"anquanbao":1, +"anruan":1, +"ansteelgroup":1, +"antpedia":1, +"anxiangren":1, +"anxin":1, +"anxjm":1, +"any123":1, +"any2000":1, +"anzhi":1, +"anzow":1, +"aojiyouxue":1, +"aojiyuke":1, +"aojoo":1, +"aoomoo":1, +"aoshu":1, +"aosoo":1, +"aotutuan":1, +"aoye":1, +"aoyou":1, +"ap88":1, +"apabi":1, +"apandim":1, +"apclc":1, +"apkyx":1, +"app111":1, +"app17":1, +"apparelsos":1, +"appchina":1, +"appgame":1, +"apple":1, +"apple2003":1, +"appvv":1, +"appying":1, +"aptchina":1, +"apusic":1, +"aqapk":1, +"aqioo":1, +"aqjfsy":1, +"aqjob":1, +"aqzpw":1, +"aqzyzx":1, +"archina":1, +"arpun":1, +"art456":1, +"artebuy":1, +"artgoin":1, +"arting365":1, +"artokok":1, +"artrade":1, +"artxun":1, +"as2sc":1, +"aseantradecenter":1, +"asiabt":1, +"asiae":1, +"askci":1, +"aslzw":1, +"asp168":1, +"aspcms":1, +"astropulsion":1, +"at918":1, +"ataozx":1, +"atdmt":1, +"atobo":1, +"atpanel":1, +"auak":1, +"austargroup":1, +"austarstudy":1, +"auto024":1, +"auto18":1, +"auto318":1, +"auto328":1, +"autobaidu":1, +"autochina360":1, +"autosup":1, +"auyou":1, +"av001":1, +"aw99":1, +"axmro":1, +"ayrbs":1, +"ayxxz":1, +"b-fairy":1, +"b-tea":1, +"b2b110":1, +"b2b168":1, +"b2bic":1, +"b2bkk":1, +"b2bvip":1, +"b2cedu":1, +"b5m":1, +"bababian":1, +"babidou":1, +"babymob":1, +"babytree":1, +"babytreeimg":1, +"bafangwang":1, +"bagb2b":1, +"baicai":1, +"baicheng":1, +"baicmotor":1, +"baidajob":1, +"baidu":1, +"baidustatic":1, +"baiduyy":1, +"baietu":1, +"baifendian":1, +"baifubao":1, +"baihe":1, +"baike":1, +"baikemy":1, +"bailitop":1, +"bailvwang":1, +"baimao":1, +"baimei":1, +"baimin":1, +"baina":1, +"baipaopao":1, +"baipu365":1, +"baiqi008":1, +"baisha2004":1, +"baishan":1, +"baishuiapple":1, +"baishunet":1, +"baiwanzhan":1, +"baixing":1, +"baixinger":1, +"baixingjd":1, +"baiye5":1, +"baiyou100":1, +"bamaiwo":1, +"bamaol":1, +"bamboo18":1, +"bamudi":1, +"bamuyu":1, +"banbijiang":1, +"banggo":1, +"banjiajia":1, +"bank-of-china":1, +"bankcomm":1, +"bankhr":1, +"bankofshanghai":1, +"banksteel":1, +"banma":1, +"banwojia":1, +"baobao88":1, +"baobaolong":1, +"baobaomm":1, +"baobei360":1, +"baobeihuijia":1, +"baobeita":1, +"baobidai":1, +"baofeng":1, +"baofoo":1, +"baoji168":1, +"baojidaily":1, +"baojijob":1, +"baojinews":1, +"baojk":1, +"baomihua":1, +"baoming":1, +"baoming88":1, +"baoruan":1, +"baoshanjie":1, +"baosteel":1, +"baotoufxh":1, +"baoyuntong":1, +"baozang":1, +"baozifa":1, +"baozoumanhua":1, +"barmap":1, +"batiaoyu":1, +"batterydir":1, +"batterykey":1, +"baxue":1, +"bayuche":1, +"bazhibo":1, +"bbbaaa":1, +"bbhun":1, +"bbioo":1, +"bbs029":1, +"bbsheji":1, +"bbtcaster":1, +"bbwfish":1, +"bcactc":1, +"bcjy123":1, +"bcpcn":1, +"bdall":1, +"bdchina":1, +"bdglj":1, +"bdgycx":1, +"bdimg":1, +"bdmryj":1, +"bdstatic":1, +"becod":1, +"beelink":1, +"beianbeian":1, +"beibaotu":1, +"beibei":1, +"beifabook":1, +"beifangfoshifen":1, +"beiguorc":1, +"beihai365":1, +"beihaidc":1, +"beijinghuashen":1, +"beijingrc":1, +"beimeihongfeng":1, +"beisen":1, +"beitaichufang":1, +"beiwaionline":1, +"beiww":1, +"benber":1, +"bendibao":1, +"bengbeng":1, +"bengou":1, +"benseshijue":1, +"benshouji":1, +"benxi0414":1, +"berqin":1, +"berui":1, +"best73":1, +"bestb2b":1, +"besttrav":1, +"betrad":1, +"beva":1, +"bf35":1, +"bfedu":1, +"bfjjw":1, +"bfyx":1, +"bgl88":1, +"bgrimm":1, +"bh-eye":1, +"bh111":1, +"bhxww":1, +"biancui":1, +"bianfeng":1, +"bianmincn":1, +"bianzhirensheng":1, +"bibitie":1, +"bidchance":1, +"bieshu":1, +"big-bit":1, +"bijirim":1, +"biketo":1, +"bilibili":1, +"biligame":1, +"bilinstar":1, +"bincailiuxue":1, +"bingchengwang":1, +"binzhuang":1, +"bio1000":1, +"bioon":1, +"bisenet":1, +"bitauto":1, +"bitautoimg":1, +"bitautotech":1, +"bitscn":1, +"biyangwang":1, +"biyong007":1, +"biz178":1, +"biz72":1, +"bizcn":1, +"bj597":1, +"bjbghc":1, +"bjbus":1, +"bjcankao":1, +"bjccedu":1, +"bjcec":1, +"bjchild":1, +"bjclio":1, +"bjfair":1, +"bjfisc":1, +"bjlmfq":1, +"bjmama":1, +"bjmanyuan":1, +"bjmti":1, +"bjp321":1, +"bjrc":1, +"bjrcb":1, +"bjsjwl":1, +"bjsly":1, +"bjsoyo":1, +"bjspw":1, +"bjsqgy":1, +"bjsyqw":1, +"bjtopli":1, +"bjwmys":1, +"bjxatq":1, +"bjximei":1, +"bjzqw":1, +"bjzs114":1, +"bkill":1, +"blctwed":1, +"bleju":1, +"blemall":1, +"bliao":1, +"blimage":1, +"blogbus":1, +"blogchina":1, +"blogcn":1, +"bloves":1, +"blqx":1, +"blqy":1, +"blshe":1, +"bluehn":1, +"blueidea":1, +"bluekai":1, +"blyol":1, +"blyun":1, +"bmlink":1, +"bnagri":1, +"bndaily":1, +"bnjyks":1, +"bnncn":1, +"bnqgsl":1, +"bnwin":1, +"bo-yi":1, +"boai":1, +"boairc":1, +"bokecc":1, +"bokee":1, +"bokerb":1, +"bolijob":1, +"booeoo":1, +"bookschina":1, +"boosj":1, +"bootcss":1, +"boqii":1, +"bosscdn":1, +"bossgoo":1, +"bosshr":1, +"bosslink":1, +"boxuu":1, +"bozhong":1, +"bqqm":1, +"brandcn":1, +"breadtrip":1, +"brightdairy":1, +"bs265":1, +"bsjhhzs":1, +"bsrczpw":1, +"bssmm":1, +"bsyjrb":1, +"btc114":1, +"btc123":1, +"btcha":1, +"btd":1, +"btophr":1, +"btrcsc":1, +"btsteel":1, +"bufan":1, +"buildhr":1, +"bundpic":1, +"burberry":1, +"bus84":1, +"busytrade":1, +"buycarcn":1, +"buyiju":1, +"bww6bdd":1, +"bx58":1, +"bxd365":1, +"bxdyt":1, +"bxgtd":1, +"bxjyw":1, +"bxxy":1, +"bxycw":1, +"bxynzz":1, +"bxzxw":1, +"bycmw":1, +"byecity":1, +"byf":1, +"byshr":1, +"bytsylmr":1, +"bzgd":1, +"bzjw":1, +"bzshw":1, +"bzw315":1, +"c-119":1, +"c-bm":1, +"c-c":1, +"c-ctrip":1, +"c-yl":1, +"c029":1, +"c969":1, +"ca001":1, +"ca168":1, +"ca800":1, +"caayee":1, +"cabhr":1, +"cabinetbuy":1, +"cableabc":1, +"caexpo":1, +"cago365":1, +"cai188":1, +"caidianqu":1, +"caigou2003":1, +"caiguu":1, +"caihao":1, +"caijixia":1, +"cailele":1, +"cailiao":1, +"caing":1, +"caipiao365":1, +"caipopo":1, +"caixin":1, +"caiyun":1, +"cake400":1, +"cali-light":1, +"candou":1, +"cang":1, +"cangnan5":1, +"cankaoxiaoxi":1, +"canyin88":1, +"caomeipai":1, +"car0575":1, +"car2100":1, +"carcav":1, +"cardbaobao":1, +"carnoc":1, +"carschina":1, +"carxoo":1, +"casemeet":1, +"casvino":1, +"casvm":1, +"cat898":1, +"catwaji":1, +"cb12580":1, +"cbe21":1, +"cbigame":1, +"cbinews":1, +"cbminfo":1, +"cboil":1, +"cbrx":1, +"cbskc":1, +"cbsrc":1, +"cc148":1, +"cc961":1, +"ccb":1, +"cccwww":1, +"ccdby":1, +"ccedip":1, +"ccedisp":1, +"ccedpw":1, +"ccement":1, +"ccgoufang":1, +"ccic":1, +"ccidcom":1, +"ccidnet":1, +"ccjoy":1, +"cclcn":1, +"ccm-1":1, +"ccn360":1, +"ccnpic":1, +"ccoalnews":1, +"ccost":1, +"ccpc360":1, +"ccr100":1, +"cct114":1, +"cctcct":1, +"cctiedu":1, +"cctime":1, +"cctv":1, +"cctvcj":1, +"cctvmall":1, +"cctvpic":1, +"ccutu":1, +"ccwcw":1, +"ccwushu":1, +"cd-feiyue":1, +"cdaidu":1, +"cdbcw":1, +"cddiaosu":1, +"cdedu":1, +"cdjsjx":1, +"cdnet110":1, +"cdqss":1, +"cdrfjc":1, +"cdrtvu":1, +"cdscjd":1, +"cdsme":1, +"cdsydc":1, +"cdyee":1, +"cdygdq":1, +"cdyipin":1, +"cdyzg":1, +"cdzgh":1, +"ce-air":1, +"ceair":1, +"cebbank":1, +"cecb2b":1, +"ceccen":1, +"cehome":1, +"cehui8":1, +"ceiea":1, +"cement365":1, +"cementren":1, +"centanet":1, +"centuryholding":1, +"cenwor":1, +"ceoeo":1, +"ceotx":1, +"ceowan":1, +"ceramicschina":1, +"cernet":1, +"cersp":1, +"ceunion":1, +"cf668":1, +"cfcyb":1, +"cfd163":1, +"cfhot":1, +"cfmmc":1, +"cguardian":1, +"cguwan":1, +"ch":1, +"ch999":1, +"chaduo":1, +"chaej":1, +"chahaoba":1, +"chahaotai":1, +"chaic":1, +"chajie":1, +"chamei":1, +"champconsult":1, +"chanel":1, +"changfon":1, +"changhongnet":1, +"changjiangtimes":1, +"changyou":1, +"changzhinews":1, +"chaoren":1, +"chaoxing":1, +"chaozhoudaily":1, +"chasfz":1, +"chashebao":1, +"chazidian":1, +"chazuo":1, +"chcpmc":1, +"che-piao":1, +"che12":1, +"che168":1, +"che2":1, +"cheaa":1, +"cheari":1, +"checkoo":1, +"cheduo":1, +"cheduoshao":1, +"chefans":1, +"chelink":1, +"chem17":1, +"chem960":1, +"chem99":1, +"chemayi":1, +"chemdrug":1, +"chemishu":1, +"chemmade":1, +"chemnet":1, +"chemrc":1, +"chemsb":1, +"chengan5":1, +"chengdechina":1, +"chengdujjw":1, +"chengguw":1, +"chengkao365":1, +"chengshiw":1, +"chengshizg":1, +"chengw":1, +"chengyangnews":1, +"chenhr":1, +"chepin88":1, +"chepinnet":1, +"cheshi":1, +"cheshi-img":1, +"chetx":1, +"chetxia":1, +"chewen":1, +"chexiu":1, +"chexun":1, +"cheyian":1, +"cheyisou":1, +"cheyou123":1, +"cheyun":1, +"chgcw":1, +"chhkjob":1, +"china":1, +"china-3":1, +"china-315":1, +"china-ah":1, +"china-asahi":1, +"china-b":1, +"china-cdt":1, +"china-changlin":1, +"china-channel":1, +"china-chuwei":1, +"china-d":1, +"china-designer":1, +"china-edai":1, +"china-ef":1, +"china-eia":1, +"china-erzhong":1, +"china-fire":1, +"china-flower":1, +"china-holiday":1, +"china-huaxue":1, +"china-insurance":1, +"china-lushan":1, +"china-nengyuan":1, +"china-pub":1, +"china-seeq":1, +"china-shufajia":1, +"china-slate":1, +"china-sss":1, +"china-up":1, +"china-waste":1, +"china-zibo":1, +"china001":1, +"china114net":1, +"china12365":1, +"china1f":1, +"china2car":1, +"china3-d":1, +"chinaacc":1, +"chinaamc":1, +"chinaavl":1, +"chinabathware":1, +"chinabdh":1, +"chinabdt":1, +"chinabgao":1, +"chinabidding":1, +"chinabmi":1, +"chinabreed":1, +"chinabuses":1, +"chinabx":1, +"chinabyte":1, +"chinabzp":1, +"chinacarbide":1, +"chinacars":1, +"chinacbe":1, +"chinaccm":1, +"chinaccnet":1, +"chinaceot":1, +"chinachemnet":1, +"chinachugui":1, +"chinacnr":1, +"chinacoal":1, +"chinacoop":1, +"chinacpx":1, +"chinacqsb":1, +"chinacreator":1, +"chinacxgd":1, +"chinadance":1, +"chinadrtv":1, +"chinadyt":1, +"chinaedu":1, +"chinaedunet":1, +"chinaenvironment":1, +"chinaeye":1, +"chinafix":1, +"chinaforklift":1, +"chinafudaoban":1, +"chinagiftsfair":1, +"chinagoldgroup":1, +"chinagzn":1, +"chinahaoan":1, +"chinahazelnut":1, +"chinahbnet":1, +"chinahightech":1, +"chinahighway":1, +"chinahr":1, +"chinahrt":1, +"chinahvacr":1, +"chinaiiss":1, +"chinaiol":1, +"chinairn":1, +"chinaitlab":1, +"chinajnhb":1, +"chinajob":1, +"chinajsxx":1, +"chinajxship":1, +"chinajzw":1, +"chinakaoyan":1, +"chinakingo":1, +"chinalao":1, +"chinalawedu":1, +"chinalawinfo":1, +"chinaluxus":1, +"chinalxnet":1, +"chinameirongspa":1, +"chinamendu":1, +"chinamenwang":1, +"chinamining":1, +"chinamobile":1, +"chinamsr":1, +"chinamypp":1, +"chinanetsun":1, +"chinaneweast":1, +"chinanews":1, +"chinaningbo":1, +"chinaoct":1, +"chinaok":1, +"chinaore":1, +"chinapay":1, +"chinapet":1, +"chinapnr":1, +"chinaports":1, +"chinapp":1, +"chinaqw":1, +"chinaren":1, +"chinasexq":1, +"chinaso":1, +"chinasq":1, +"chinasspp":1, +"chinaswitch":1, +"chinasws":1, +"chinatarena":1, +"chinatat":1, +"chinatibetnews":1, +"chinatietong":1, +"chinatour-net":1, +"chinatsi":1, +"chinauma":1, +"chinaunicom":1, +"chinaunionpay":1, +"chinaups":1, +"chinavegan":1, +"chinavisual":1, +"chinavnet":1, +"chinavoa":1, +"chinawatchnet":1, +"chinawbsyxh":1, +"chinawch":1, +"chinaweiyu":1, +"chinawestagr":1, +"chinawjol":1, +"chinawoodnet":1, +"chinawudang":1, +"chinawutong":1, +"chinawuyuan":1, +"chinaxiaokang":1, +"chinaxinge":1, +"chinaxq":1, +"chinaxwcb":1, +"chinayigui":1, +"chinayouji":1, +"chinayyjx":1, +"chinaz":1, +"chinazichan":1, +"chinazikao":1, +"chinazjy":1, +"chinesecio":1, +"chinesejy":1, +"chizhoujob":1, +"chizhouren":1, +"chiznews":1, +"chnart":1, +"chnpac":1, +"chnsuv":1, +"chnvc":1, +"chnweiyu":1, +"chofn":1, +"chtf":1, +"chtgc":1, +"chuandong":1, +"chuangye":1, +"chuangyemeng":1, +"chuanke":1, +"chuanmeicn":1, +"chufw":1, +"chuguo78":1, +"chuguohome":1, +"chunqiuwang":1, +"chushan":1, +"ci123":1, +"ciceme":1, +"ciif-expo":1, +"ciku5":1, +"cioage":1, +"cisco":1, +"cisregister":1, +"citic":1, +"citsnj":1, +"city8":1, +"citygf":1, +"citysbs":1, +"cityy":1, +"civilness":1, +"ciweek":1, +"ciwong":1, +"cjol":1, +"cjrcsc":1, +"cjyyw":1, +"ckplayer":1, +"cl0438":1, +"cl597":1, +"class01":1, +"classic023":1, +"clcmw":1, +"cljmmm123":1, +"clotheshr":1, +"clothr":1, +"clssn":1, +"cmbchina":1, +"cmejob":1, +"cmfu":1, +"cmge":1, +"cmhk":1, +"cmitsd":1, +"cmol":1, +"cmstop":1, +"cmt178":1, +"cmxrcw":1, +"cn-122":1, +"cn-diaoyu":1, +"cn-office":1, +"cn-roofexpert":1, +"cn-truck":1, +"cn010w":1, +"cn0434":1, +"cn0556":1, +"cn0851":1, +"cn0917":1, +"cn12333":1, +"cn21edu":1, +"cn2che":1, +"cn357":1, +"cn5135":1, +"cn539":1, +"cn716":1, +"cnad":1, +"cnadtop":1, +"cnagri":1, +"cnaho":1, +"cnair":1, +"cnal":1, +"cnautonews":1, +"cnbeta":1, +"cnblogs":1, +"cnbzxw":1, +"cnchainnet":1, +"cnchu":1, +"cncn":1, +"cncookernet":1, +"cncopter":1, +"cncotton":1, +"cncproduct":1, +"cncraftinfo":1, +"cncrk":1, +"cnd8":1, +"cndae":1, +"cndesign":1, +"cndingxi":1, +"cndns":1, +"cndoornet":1, +"cndoors":1, +"cndrsq":1, +"cndrynet":1, +"cndsi":1, +"cndzys":1, +"cnelc":1, +"cnena":1, +"cnep001":1, +"cnepaper":1, +"cnfeol":1, +"cnffi":1, +"cnfilternet":1, +"cnfla":1, +"cnfol":1, +"cnfolimg":1, +"cnforex":1, +"cnfq":1, +"cnfruit":1, +"cnfzflw":1, +"cngaosu":1, +"cngba":1, +"cngoto":1, +"cngrain":1, +"cnhaio":1, +"cnhan":1, +"cnhandan":1, +"cnhangyun":1, +"cnhmsq":1, +"cnhnb":1, +"cnhubei":1, +"cnhvacrnet":1, +"cnhzpjy":1, +"cnipai":1, +"cnipr":1, +"cnjiaju":1, +"cnjidan":1, +"cnjj":1, +"cnjnsb":1, +"cnjob":1, +"cnjsqw":1, +"cnjxol":1, +"cnjywl":1, +"cnjzjj":1, +"cnkang":1, +"cnkjtf":1, +"cnkjz":1, +"cnledw":1, +"cnlightnet":1, +"cnluqiao":1, +"cnluye":1, +"cnmill":1, +"cnmmhh":1, +"cnmo":1, +"cnnaihuo":1, +"cnnb":1, +"cnoee":1, +"cnokcn":1, +"cnoutdoor":1, +"cnpatent":1, +"cnpickups":1, +"cnpkm":1, +"cnpowdernet":1, +"cnpubg":1, +"cnpv":1, +"cnqc":1, +"cnqjc":1, +"cnqjw":1, +"cnradio":1, +"cnrdn":1, +"cnree":1, +"cnrencai":1, +"cnrepair":1, +"cnrepark":1, +"cnseeq":1, +"cnshipnet":1, +"cnshipping":1, +"cnsikao":1, +"cnsimin":1, +"cnsnpj":1, +"cnsphoto":1, +"cnsqzx":1, +"cnstock":1, +"cnsuning":1, +"cnsuv":1, +"cnsyyx":1, +"cnta":1, +"cntaijiquan":1, +"cntaiping":1, +"cntc":1, +"cntexjob":1, +"cntheory":1, +"cntour2":1, +"cntrades":1, +"cntronics":1, +"cnv168":1, +"cnwaternews":1, +"cnwav":1, +"cnwdjj":1, +"cnwear":1, +"cnwest":1, +"cnwest88":1, +"cnwinenews":1, +"cnwpem":1, +"cnwuyun":1, +"cnxds":1, +"cnxiantao":1, +"cnxianzai":1, +"cnyigui":1, +"cnys":1, +"cnyu":1, +"cnyxs":1, +"cnzao":1, +"cnzhantuan":1, +"cnzhengmu":1, +"cnzjqi":1, +"cnzsyz":1, +"cnzz":1, +"co188":1, +"coal-link":1, +"coalcn":1, +"coaoo":1, +"coatingol":1, +"coco90":1, +"cocoachina":1, +"cofco":1, +"cofeed":1, +"cofool":1, +"cogonline":1, +"comicyu":1, +"compete":1, +"comsenz":1, +"conshow":1, +"coo8":1, +"coodir":1, +"cool-de":1, +"coolxap":1, +"coolzan":1, +"coop100":1, +"coopb2b":1, +"cosco":1, +"cozyee":1, +"cp2y":1, +"cpdyj":1, +"cpooo":1, +"cpp114":1, +"cppfoto":1, +"cps1688":1, +"cps800":1, +"cptjob":1, +"cpv6":1, +"cpvjob":1, +"cq3a":1, +"cq6":1, +"cqbnda":1, +"cqbnedu":1, +"cqbnkx":1, +"cqbnly":1, +"cqbnrc":1, +"cqbntv":1, +"cqbys":1, +"cqcb":1, +"cqcoal":1, +"cqcsrc":1, +"cqdent":1, +"cqduomi":1, +"cqfire":1, +"cqhaokou":1, +"cqjiaz":1, +"cqjjnet":1, +"cqjob":1, +"cqjsxx":1, +"cqjy":1, +"cqkx":1, +"cqlozz":1, +"cqmmgo":1, +"cqncnews":1, +"cqqiyi":1, +"cqshenou":1, +"cqskl":1, +"cqtea":1, +"cqtransit":1, +"cquae":1, +"cqvip":1, +"cqwin":1, +"cqxh120":1, +"cqxiehe":1, +"cqxsss":1, +"cqzls":1, +"cr173":1, +"cr18g":1, +"crabchina":1, +"craftcontact":1, +"crec4":1, +"crecg":1, +"cric":1, +"crm1001":1, +"crmgz":1, +"crosswaycn":1, +"crsky":1, +"crystaledu":1, +"cs-air":1, +"cs090":1, +"cs2sc":1, +"cs53":1, +"cs6s":1, +"csadec":1, +"csair":1, +"csbew":1, +"csc86":1, +"cscec":1, +"cscoal":1, +"cscsf":1, +"cscyw":1, +"cseyzx":1, +"csgc365":1, +"csgm168":1, +"csrcsc":1, +"cssmoban":1, +"cssyzxx":1, +"csvw":1, +"csxnews":1, +"csxww":1, +"csytv":1, +"cszx":1, +"ct10000":1, +"ct108":1, +"ct597":1, +"ctaoci":1, +"cteaw":1, +"cthnet":1, +"cthy":1, +"ctiforum":1, +"ctqcp":1, +"ctrip":1, +"ctsho":1, +"cttsd":1, +"ctule":1, +"ctxyw":1, +"cubead":1, +"cuctv":1, +"cug2313":1, +"cunan":1, +"cuncun8":1, +"cuplayer":1, +"custeel":1, +"cut35":1, +"cutv":1, +"cwan":1, +"cwddd":1, +"cwestc":1, +"cwiif":1, +"cwmining":1, +"cwroom":1, +"cxhr":1, +"cxwl":1, +"cy":1, +"cy580":1, +"cy887":1, +"cycnet":1, +"cycoo":1, +"cyol":1, +"cz2sc":1, +"cz365":1, +"czbanbantong":1, +"czbtv":1, +"czepb":1, +"czfcw":1, +"czgjj":1, +"czjpw":1, +"czrrw":1, +"czrxw":1, +"czsrc":1, +"cztour":1, +"cztv":1, +"czvv":1, +"czwlgy":1, +"czxiu":1, +"d1cm":1, +"d1net":1, +"d8wed":1, +"d9soft":1, +"daba":1, +"dachanet":1, +"dachenglaw":1, +"dachengnet":1, +"dadipedia":1, +"dadiwang":1, +"dadou":1, +"dadunet":1, +"dafengso":1, +"dagancn":1, +"dagangcheng":1, +"daguantao":1, +"dahainan":1, +"dahangzhou":1, +"dahecc":1, +"dahei":1, +"daheshui":1, +"dahoutao":1, +"dahua8":1, +"dahuawang":1, +"dai911":1, +"daibini":1, +"daikuane":1, +"dailyqd":1, +"daisonghua":1, +"daixiaomi":1, +"daiyanbao":1, +"dajiabao":1, +"dajianet":1, +"dajiazhao":1, +"dajie":1, +"damuzzz":1, +"dance365":1, +"dance888":1, +"dang3":1, +"dangdang":1, +"dangjian":1, +"danzhaowang":1, +"daodao":1, +"daogoubang":1, +"daoguo":1, +"daoxila":1, +"dapu":1, +"daqi":1, +"daqsoft":1, +"darczpw":1, +"darongcheng":1, +"darryring":1, +"davinfo":1, +"daxiangrc":1, +"dayoo":1, +"dayou123":1, +"dazhaiwang":1, +"dazhe5":1, +"dazhenzimiao":1, +"dazhongemiao":1, +"dazhonghr":1, +"dazhoushan":1, +"dazibo":1, +"dazpin":1, +"db-nw":1, +"db2car":1, +"dbank":1, +"dbgtzx":1, +"dbtc888":1, +"dc-cn":1, +"dcqedu":1, +"dcxnews":1, +"ddcsh":1, +"ddmap":1, +"ddmapimg":1, +"ddooo":1, +"ddvip":1, +"decwhy":1, +"dedecms":1, +"demage":1, +"demaxiya":1, +"denghuo":1, +"deppon":1, +"derenbs":1, +"desktx":1, +"destoon":1, +"deyang5":1, +"deyi":1, +"deyiso":1, +"dezhi":1, +"dezhoudaily":1, +"dfcfw":1, +"dfdaily":1, +"dfedu":1, +"dfhdw":1, +"dfhon":1, +"dflgnc":1, +"dfshw":1, +"dfsrcw":1, +"dg114":1, +"dg121":1, +"dgchangan":1, +"dginfo":1, +"dgjyw":1, +"dglongmei":1, +"dgqjj":1, +"dgtx888":1, +"dgyuanyi":1, +"dgzzm":1, +"dhgate":1, +"dhifi":1, +"diandian":1, +"diandong":1, +"dianji007":1, +"dianli114":1, +"dianpifa":1, +"dianping":1, +"dianpingba":1, +"dianyuan":1, +"diaochapai":1, +"diaoyanbao":1, +"diaoyu":1, +"diaoyu123":1, +"diaoyu520":1, +"diaoyula":1, +"diaoyuwang":1, +"diaoyuweng":1, +"dichan":1, +"dili360":1, +"dinghuaren":1, +"dingsite":1, +"dingxinhui":1, +"dingyx":1, +"dionly":1, +"diqiuw":1, +"dir001":1, +"discoverhongkong":1, +"discoversources":1, +"discuz":1, +"ditan360":1, +"ditan369":1, +"diyicai":1, +"diyifanwen":1, +"diyishijian":1, +"diyiyou":1, +"diypda":1, +"diytrade":1, +"djcb71":1, +"djjw":1, +"djkk":1, +"djob":1, +"djxww":1, +"djye":1, +"dld":1, +"dldcdn":1, +"dledu":1, +"dlflavor":1, +"dlgaoji":1, +"dllake":1, +"dlmonita":1, +"dlxww":1, +"dlysgh":1, +"dm-rc":1, +"dm0571":1, +"dm456":1, +"dmansg":1, +"dmcbd":1, +"dmzj":1, +"dn1234":1, +"dnwx":1, +"doc88":1, +"docer":1, +"docin":1, +"dodonew":1, +"dog126":1, +"doido":1, +"dolcn":1, +"donews":1, +"dongao":1, +"dongbeiol":1, +"dongeedu":1, +"dongfang":1, +"dongfang8":1, +"dongfangnews":1, +"dongnanshan":1, +"dongtangad":1, +"dooland":1, +"doomii":1, +"doorhr":1, +"dospy":1, +"dostor":1, +"douban":1, +"douguo":1, +"douluodalu123":1, +"douxie":1, +"dowater":1, +"downhot":1, +"downxia":1, +"dpfile":1, +"dpm360":1, +"dq247":1, +"dqccc":1, +"dqcccc":1, +"dqdaily":1, +"dqguo":1, +"dqiong":1, +"dqjob88":1, +"dqzc":1, +"dreams-travel":1, +"drivergenius":1, +"driversdown":1, +"ds123456":1, +"ds599":1, +"dsdod":1, +"dsfdc":1, +"dshigao":1, +"dshmama":1, +"dshrc":1, +"dsrlzy":1, +"dtcoalmine":1, +"dtxmw":1, +"duapp":1, +"duba":1, +"duitang":1, +"dukuai":1, +"duojiaochong":1, +"duokan":1, +"duomai":1, +"duomeiren":1, +"duomi":1, +"duoshitong":1, +"duoshuo":1, +"duote":1, +"duouoo":1, +"duowan":1, +"duoyewu":1, +"duoyi":1, +"dushicn":1, +"dushifang":1, +"duwenzhang":1, +"duxiu":1, +"dv37":1, +"dwstatic":1, +"dx-job":1, +"dxda":1, +"dxddcx":1, +"dxszxy":1, +"dxy":1, +"dxycdn":1, +"dxzx":1, +"dycars":1, +"dyfcw":1, +"dyhjw":1, +"dyplk":1, +"dyqc":1, +"dz-z":1, +"dz1982":1, +"dz666":1, +"dzcnc":1, +"dzmjw":1, +"dzqiche":1, +"dzrbs":1, +"dzsc":1, +"dzsm":1, +"dzsrcw":1, +"dzszb":1, +"dzwindows":1, +"dzwww":1, +"dzxss":1, +"dzyysb":1, +"e-baojian":1, +"e-chinalife":1, +"e-jjj":1, +"e-tiller":1, +"e0514":1, +"e0575":1, +"e0734":1, +"e118114":1, +"e21cn":1, +"e2say":1, +"e521":1, +"e8online":1, +"ea3w":1, +"ea56":1, +"eabax":1, +"eachnet":1, +"eahui":1, +"earthedu":1, +"easdo":1, +"easiu":1, +"eastday":1, +"easthome":1, +"eastmoney":1, +"eastsilver":1, +"eastsoo":1, +"easyreadtech":1, +"easysofthome":1, +"eayuan":1, +"eb80":1, +"ebay":1, +"ebdoor":1, +"ebigear":1, +"ebioe":1, +"ebnew":1, +"ebrun":1, +"ebscn":1, +"ebseek":1, +"ec51":1, +"ec818":1, +"ecaidian":1, +"ecaihr":1, +"eccn":1, +"ecduo":1, +"ecgoods":1, +"echiele":1, +"echinagov":1, +"ecitic":1, +"ecjyj":1, +"ecp888":1, +"ecppn":1, +"ecqun":1, +"ecshop":1, +"edai":1, +"edancheng":1, +"edaocha":1, +"ede35":1, +"edu-hb":1, +"edu-js":1, +"edu03":1, +"edu24ol":1, +"edu5a":1, +"edu80":1, +"edu84":1, +"edu88":1, +"eduease":1, +"eduego":1, +"eduglobal":1, +"edushi":1, +"edutt":1, +"eduu":1, +"eduuu":1, +"eduwo":1, +"eduwsw":1, +"eduyf":1, +"edzx":1, +"eechina":1, +"eefocus":1, +"eehu":1, +"eeju":1, +"eelly":1, +"eeyy":1, +"ef-school":1, +"ef360":1, +"efwang":1, +"egou":1, +"ehaier":1, +"ehomeday":1, +"ehometu":1, +"ehr99":1, +"ehvacr":1, +"eicbs":1, +"eiiq":1, +"eis100":1, +"eit0571":1, +"ejee":1, +"ejiacn":1, +"ejier":1, +"eju":1, +"ek6":1, +"ekaidian":1, +"elanw":1, +"eldawa":1, +"ele001":1, +"elecfans":1, +"elecinfo":1, +"elexcon":1, +"eliushi":1, +"ellechina":1, +"ellll":1, +"elong":1, +"els001":1, +"emarbox":1, +"eminiye":1, +"ems517":1, +"emtx":1, +"en51":1, +"ename":1, +"enbowang":1, +"enchantshow":1, +"enetedu":1, +"eng24":1, +"enguo":1, +"enkj":1, +"enshijob":1, +"enterdesk":1, +"eoaoo":1, +"eoeandroid":1, +"eoemarket":1, +"eoffcn":1, +"eooioo":1, +"eooqoo":1, +"eoouoo":1, +"eoozoo":1, +"eoriver":1, +"epanshi":1, +"epday":1, +"epjob88":1, +"epjyw":1, +"epweike":1, +"epzhaopin":1, +"eq-hl":1, +"eqyn":1, +"erdsrsrc":1, +"erongtu":1, +"ersoso":1, +"erya100":1, +"escdn":1, +"esfimg":1, +"eshow365":1, +"eshufa":1, +"esljt":1, +"esnai":1, +"esongyuan":1, +"esun88":1, +"etao":1, +"etest8":1, +"ethainan":1, +"etiantian":1, +"etlong":1, +"etoubao":1, +"etpass":1, +"etuan":1, +"ev123":1, +"evergrande":1, +"everychina":1, +"ewmzs":1, +"ewoka":1, +"eworldship":1, +"ewsos":1, +"ewteacher":1, +"exam8":1, +"examda":1, +"examw":1, +"exbulk":1, +"exchangecn":1, +"expo-china":1, +"expoon":1, +"expoooo":1, +"expotu":1, +"expowindow":1, +"ey99":1, +"eyaobei":1, +"eyejoyful":1, +"eyizhang":1, +"eyou":1, +"eyuyao":1, +"eywedu":1, +"ezhiol":1, +"eztxw":1, +"f139":1, +"f1688":1, +"f537":1, +"fa-today":1, +"fa597":1, +"fabang":1, +"fabao365":1, +"fabu114":1, +"fad123":1, +"fafaku":1, +"faidns":1, +"faisco":1, +"fala114":1, +"faloo":1, +"famen88":1, +"famens":1, +"famensi":1, +"fancai":1, +"fandian":1, +"fang":1, +"fang33":1, +"fang99":1, +"fangbx":1, +"fangchan":1, +"fangdd":1, +"fangdr":1, +"fanging":1, +"fangjia":1, +"fangjiadp":1, +"fanglimei":1, +"fangtan007":1, +"fangte":1, +"fangtoo":1, +"fangxiaoer":1, +"fangyi":1, +"fangyou":1, +"fangyuan365":1, +"fangzhanhui":1, +"fangzhur":1, +"fanhuan":1, +"fanlihe":1, +"fanqieleyuan":1, +"fanxuefei":1, +"fanyizhijia":1, +"far2000":1, +"fastcdn":1, +"favolist":1, +"faw-mazda":1, +"faw-vw":1, +"fawan":1, +"fblife":1, +"fc0633":1, +"fc571":1, +"fccs":1, +"fcg360":1, +"fcgsnews":1, +"fcjjr":1, +"fcjob88":1, +"fcrc114":1, +"fcw6":1, +"fcyhw":1, +"fd597":1, +"fdc0760":1, +"fdgzw":1, +"fdjzu":1, +"fdkjgz":1, +"feedsky":1, +"feelcars":1, +"feeyo":1, +"fei580":1, +"feicuiwuyu":1, +"feiku":1, +"feiliu":1, +"feipin":1, +"fenfen":1, +"feng":1, +"fengbao":1, +"fengj":1, +"fengjing":1, +"fengniao":1, +"fengone":1, +"fengsung":1, +"fengyunzhibo":1, +"fenlei168":1, +"fenlei265":1, +"fenleidao":1, +"fensui168":1, +"ffpic":1, +"fhlczy":1, +"fhlyou":1, +"fillseo":1, +"fimmu":1, +"financeun":1, +"findlawimg":1, +"findzd":1, +"fj-hitech":1, +"fj007":1, +"fj987":1, +"fjber":1, +"fjcha":1, +"fjcns":1, +"fjcoop":1, +"fjdaily":1, +"fjdh":1, +"fjhrss":1, +"fjhun":1, +"fjii":1, +"fjjcjy":1, +"fjjdrcw":1, +"fjlh":1, +"fjmwjx":1, +"fjnacc":1, +"fjnet":1, +"fjpta":1, +"fjsen":1, +"fjsjs":1, +"fjta":1, +"fjtn":1, +"fjzlym":1, +"fjzol":1, +"fl001":1, +"fl78":1, +"flash1890":1, +"flashget":1, +"floorb2b":1, +"flower188":1, +"flssw":1, +"fltrp":1, +"flyxg":1, +"fnkq":1, +"fnrcw":1, +"fobshanghai":1, +"focuschina":1, +"folkw":1, +"foloda":1, +"foodjx":1, +"foods1":1, +"foodszs":1, +"foojoo":1, +"for68":1, +"forbeschina":1, +"fotile":1, +"fpdisplay":1, +"fpgadev":1, +"fpwap":1, +"fq597":1, +"fractal-technology":1, +"freehead":1, +"frkq":1, +"fs121":1, +"fs31":1, +"fsclzs":1, +"fsdew":1, +"fsjoy":1, +"fslsg":1, +"fstcb":1, +"ft22":1, +"ftchinese":1, +"ftuan":1, +"fujianec":1, +"fujianrc":1, +"fuliao":1, +"fuling":1, +"fuliyuwang":1, +"fumubang":1, +"fumuhui":1, +"funfungolf":1, +"funshion":1, +"funxoo":1, +"funxun":1, +"fupiaopifa":1, +"fuqiangw":1, +"fuqing2006":1, +"furniturebbs":1, +"furnituremedia":1, +"fututa":1, +"fuwuce":1, +"fuyoubank":1, +"fwzjia":1, +"fx168":1, +"fx678":1, +"fxxz":1, +"fybxw":1, +"fysns":1, +"fytcw":1, +"fz2sc":1, +"fz597":1, +"fzbm":1, +"fzengine":1, +"fzfzjx":1, +"fzg360":1, +"fzpig":1, +"fzsjob":1, +"g1080":1, +"g12e":1, +"g207":1, +"g2b2b":1, +"g7430":1, +"gacmotor":1, +"gai001":1, +"gaibar":1, +"gaitu":1, +"gaizhui":1, +"game080":1, +"gamecomb":1, +"gamersky":1, +"gamesville":1, +"gametea":1, +"gamfe":1, +"ganhuoche":1, +"ganji":1, +"ganjiangrc":1, +"ganjistatic1":1, +"ganniu":1, +"ganwan":1, +"ganxianw":1, +"ganzhe":1, +"gao7":1, +"gao8dou":1, +"gaodun":1, +"gaofen":1, +"gaokao":1, +"gaokao789":1, +"gaoqingren":1, +"gaosiedu":1, +"gaosubao":1, +"gaoxiaola":1, +"gaozhouba":1, +"garply":1, +"gasgoo":1, +"gashr":1, +"gasshow":1, +"gather-sc":1, +"gcchina":1, +"gd-china":1, +"gd-fishmarket":1, +"gdcct":1, +"gdcoop":1, +"gdcrj":1, +"gdedu123":1, +"gdhbsh":1, +"gdmm":1, +"gdrc":1, +"gdswine":1, +"gdsxxw":1, +"gdttc":1, +"gdtuoling":1, +"gdwca":1, +"gdwest":1, +"gdzj8":1, +"geely":1, +"geihui":1, +"gemdale":1, +"geo-show":1, +"gesep":1, +"gexing":1, +"gezila":1, +"gfan":1, +"gfsns":1, +"gg-art":1, +"gg-led":1, +"ggcj":1, +"ghjie":1, +"gift12345":1, +"gitom":1, +"gk-z":1, +"gk99":1, +"gkao":1, +"gkong":1, +"gkstk":1, +"gkxx":1, +"gkzhan":1, +"glasseasy":1, +"glasshr":1, +"gldjc":1, +"glinfo":1, +"global-trade-center":1, +"globalchemmade":1, +"globalhardwares":1, +"globalmarket":1, +"globeimmi":1, +"globrand":1, +"glofang":1, +"glrcw":1, +"glyrc":1, +"glzhuang":1, +"gm86":1, +"gmseb":1, +"gndaily":1, +"go007":1, +"go2map":1, +"go823":1, +"go9939":1, +"godsignal":1, +"goepe":1, +"gogocn":1, +"gojiaju":1, +"gold600":1, +"gold678":1, +"goldenholiday":1, +"goldin168":1, +"gong123":1, +"gongcai":1, +"gongchang":1, +"gongjiao":1, +"gongjiaomi":1, +"gongkaocn":1, +"gongkong":1, +"gongyishibao":1, +"gongzhou":1, +"gonren":1, +"goo2sc":1, +"goocun":1, +"goodbaby":1, +"goodkejian":1, +"google-analytics":1, +"googleadservices":1, +"googlesyndication":1, +"googletagmanager":1, +"googvv":1, +"gooioo":1, +"gooniu":1, +"gooooal":1, +"goootech":1, +"gopinyin":1, +"gopuu":1, +"gotohz":1, +"gotoip1":1, +"gotoip2":1, +"gotoip3":1, +"gotoip4":1, +"gotoip55":1, +"gotoningbo":1, +"gouchezixun":1, +"goufang":1, +"goufw":1, +"gougou":1, +"goulew":1, +"goumin":1, +"goupuzi":1, +"goushe":1, +"gouwuke":1, +"gowulong":1, +"gqren":1, +"gqsoso":1, +"grchina":1, +"greatwuyi":1, +"greentimes":1, +"greenxf":1, +"grfyw":1, +"gridsources":1, +"gridsumdissector":1, +"grinm":1, +"gsdpw":1, +"gsftw":1, +"gsheimeiren":1, +"gsjb":1, +"gsrcw":1, +"gsstgs":1, +"gstarcad":1, +"gsxpz":1, +"gtgqw":1, +"gtimg":1, +"gtja":1, +"gtobal":1, +"gtuu":1, +"gtxh":1, +"gtzyb":1, +"guahao":1, +"guan5":1, +"guanchengrc":1, +"guang":1, +"guangdongrc":1, +"guangguiyin":1, +"guangshuishi":1, +"guangxirc":1, +"guanjiatu":1, +"guanjoy":1, +"guanyun520":1, +"guanzhongrc":1, +"gucheng":1, +"gucn":1, +"guhantai":1, +"guidaye":1, +"guidechem":1, +"guiguanrc":1, +"guijirc":1, +"guijob":1, +"guilincits":1, +"guilinhd":1, +"guilinlife":1, +"guimi":1, +"guiqianrc":1, +"gulove":1, +"guocar":1, +"guolairen":1, +"guolv":1, +"guolvol":1, +"guoshi":1, +"guoxue":1, +"gushiw":1, +"gusuwang":1, +"gutx":1, +"guuoo":1, +"guwanw":1, +"guzhiwang":1, +"gwyoo":1, +"gwyou":1, +"gwypxw":1, +"gx123":1, +"gx12301":1, +"gx211":1, +"gxbys":1, +"gxcity":1, +"gxfdcw":1, +"gxfpw":1, +"gxgymsxx":1, +"gxhc365":1, +"gxhouse":1, +"gxhzxw":1, +"gxjtaq":1, +"gxnongmu":1, +"gxorg":1, +"gxqcw":1, +"gxqxj":1, +"gxrc":1, +"gxrcw":1, +"gxsky":1, +"gxwj315":1, +"gxybw":1, +"gxylnews":1, +"gy365":1, +"gyxnews":1, +"gyxuan":1, +"gz91":1, +"gzbsnc":1, +"gzccps":1, +"gzcofc":1, +"gzcol":1, +"gzcpc":1, +"gzdfxw":1, +"gzdsw":1, +"gzedu":1, +"gzh":1, +"gzjuncheng":1, +"gzlight":1, +"gzmama":1, +"gzmanny":1, +"gzmtr":1, +"gznet":1, +"gzqnzyz":1, +"gzred":1, +"gzrencai":1, +"gzrisingsteel":1, +"gzsedu":1, +"gzstv":1, +"gzszk":1, +"gztcdj":1, +"gztv":1, +"gzucard":1, +"gzxuelun":1, +"gzymj":1, +"gzza":1, +"h0591":1, +"h2o-china":1, +"habctv":1, +"hackhome":1, +"hackp":1, +"hahaertong":1, +"haibao":1, +"haichaninfo":1, +"haier":1, +"haihuahr":1, +"haihui1688":1, +"haimanfeisi":1, +"hainanfz":1, +"haining":1, +"hanbinwang":1, +"hancheng":1, +"handu":1, +"hangye8":1, +"hanweb":1, +"hanxingtv":1, +"hao0469":1, +"hao123":1, +"hao123img":1, +"hao224":1, +"hao315":1, +"haob2b":1, +"haobangni":1, +"haochi123":1, +"haochimei":1, +"haodai":1, +"haodf":1, +"haodingdan":1, +"haodou":1, +"haofjj":1, +"haofung":1, +"haofz":1, +"haogongzhang":1, +"haojiudaili":1, +"haoleyou":1, +"haoliv":1, +"haomzl":1, +"haopoo":1, +"haorc":1, +"haote":1, +"haotijin":1, +"haotui":1, +"haowangpu":1, +"haowm":1, +"haowu":1, +"haoyisheng":1, +"haoyiwujin":1, +"haoyun56":1, +"haozhanhui":1, +"haozhou":1, +"haozu":1, +"haozuojia":1, +"happypingpang":1, +"harbin123":1, +"harrenmedianetwork":1, +"hatcy":1, +"havehome":1, +"haxiu":1, +"hb30":1, +"hbaas":1, +"hbdjk":1, +"hbeinews":1, +"hbfxh":1, +"hbfys":1, +"hbfzb":1, +"hbgajg":1, +"hbgrain":1, +"hbgsl":1, +"hbhscpa":1, +"hbjie":1, +"hbjob88":1, +"hbksw":1, +"hbliti":1, +"hbqc":1, +"hbqnb":1, +"hbrc":1, +"hbshgzx":1, +"hbsjz110":1, +"hbsztv":1, +"hbtcw":1, +"hbtycp":1, +"hbxfmp":1, +"hbxmad":1, +"hbydsy":1, +"hbyidu":1, +"hbzhan":1, +"hbzkw":1, +"hbzkwl":1, +"hc360":1, +"hc433":1, +"hctvnet":1, +"hcxww":1, +"hd512":1, +"hdeso":1, +"hdmnw":1, +"hdslb":1, +"hdzp":1, +"hdzxw":1, +"he-nan":1, +"healthoo":1, +"healthr":1, +"hebcoop":1, +"hebdx":1, +"hebeicdc":1, +"hebeihr":1, +"hebiw":1, +"hebradio":1, +"hebtv":1, +"hefeiat":1, +"hehu":1, +"heiguang":1, +"heima8":1, +"hejun":1, +"hekouqu":1, +"hellozb":1, +"hellozx":1, +"henan100":1, +"henanci":1, +"henanedu":1, +"henankf":1, +"henanrc":1, +"henantiyu":1, +"henanweiye":1, +"hengqian":1, +"hengqijy":1, +"hengyan":1, +"hepan":1, +"hepost":1, +"hepuwang":1, +"herostart":1, +"herschina":1, +"hersp":1, +"hetda":1, +"hexieshaanxi":1, +"hexindai":1, +"hexun":1, +"heyuan163":1, +"heze369":1, +"hezejob":1, +"hezeribao":1, +"hf2sc":1, +"hf365":1, +"hfhouse":1, +"hfk99":1, +"hftogo":1, +"hfwenshi":1, +"hg-z":1, +"hgjob":1, +"hgqzj":1, +"hgrencai":1, +"hgzrc":1, +"hh010":1, +"hhczy":1, +"hhhrs":1, +"hhhtnews":1, +"hhk365":1, +"hhkao":1, +"hhncp":1, +"hhtravel":1, +"hi0734":1, +"hi1718":1, +"hi2000":1, +"hi772":1, +"hiao":1, +"hiapk":1, +"hicct":1, +"hicdma":1, +"hichina":1, +"hilizi":1, +"himfr":1, +"himinsy":1, +"hipiao":1, +"hirede":1, +"hisense":1, +"hisupplier":1, +"hitao":1, +"hjenglish":1, +"hk515":1, +"hkcts":1, +"hkproperty":1, +"hktdc":1, +"hkxtzgl":1, +"hldbtv":1, +"hldnews":1, +"hlgnet":1, +"hljjc":1, +"hljrtvu":1, +"hljtcp":1, +"hljtv":1, +"hlpolice":1, +"hly":1, +"hlybar":1, +"hmaow":1, +"hme01":1, +"hmecw":1, +"hmlan":1, +"hmw163":1, +"hmw365":1, +"hn12333":1, +"hn987":1, +"hnair":1, +"hncdc":1, +"hnchj":1, +"hncoop":1, +"hncpu":1, +"hncsmjzs":1, +"hnditu":1, +"hnemap":1, +"hnfgdj":1, +"hnfxh":1, +"hnfzb":1, +"hngbjy":1, +"hngfjy":1, +"hnhjw":1, +"hnhw":1, +"hnhyrc":1, +"hnkejixueyuan":1, +"hnloushi":1, +"hnmama":1, +"hnnypp":1, +"hnradio":1, +"hnrc":1, +"hnrcsc":1, +"hnrczpw":1, +"hnrsks":1, +"hnsncb":1, +"hnticai":1, +"hntjtjw":1, +"hnwencheng":1, +"hnxxw666":1, +"hnxxwzz":1, +"hnxysteel":1, +"hnyxhb":1, +"hnyymd":1, +"hnzhuang":1, +"hnzqw":1, +"hnzscl":1, +"hnzssj":1, +"hnzyzx":1, +"hogesoft":1, +"holdfine":1, +"home77":1, +"home898":1, +"homeinns":1, +"homekoo":1, +"hometex114":1, +"hometexnet":1, +"homeun":1, +"hong-men":1, +"hongdamold":1, +"hongen":1, +"honghuowang":1, +"hongjingedu":1, +"hongmen":1, +"hongniang":1, +"hongshu":1, +"hongtu":1, +"hongxiu":1, +"hongzao114":1, +"hoopchina":1, +"hooshong":1, +"horsehr":1, +"hosocorp":1, +"hot178":1, +"houcar":1, +"houdao":1, +"house365":1, +"house371":1, +"house666":1, +"househr":1, +"househy":1, +"housoo":1, +"houstyle":1, +"houxue":1, +"howbuy":1, +"howjia":1, +"howzhi":1, +"hq-ielts":1, +"hq0564":1, +"hq88":1, +"hqbpc":1, +"hqcr":1, +"hqdoor":1, +"hqew":1, +"hqjhw":1, +"hql8":1, +"hqlsw":1, +"hqmianshou":1, +"hqqrc":1, +"hqsxy":1, +"hqthw":1, +"hqyj":1, +"hr006":1, +"hr025":1, +"hr0571":1, +"hr0660":1, +"hr0751":1, +"hr0752":1, +"hr0753":1, +"hr0755":1, +"hr0759":1, +"hr0766":1, +"hr0898":1, +"hr1000":1, +"hr33":1, +"hr369":1, +"hr762":1, +"hr763":1, +"hrbanlv":1, +"hrbit":1, +"hrbjsd":1, +"hrbuilds":1, +"hrhorse":1, +"hrpin":1, +"hs-cn":1, +"hsdcw":1, +"hsdhq":1, +"hshan":1, +"hsjyxx":1, +"hsmhw":1, +"hsrcw":1, +"hssanli":1, +"hsw365":1, +"hsx99":1, +"htexam":1, +"htinns":1, +"htkaoyan":1, +"htsec":1, +"httpcn":1, +"htyuqi":1, +"hua":1, +"huaban":1, +"huabian":1, +"huacolor":1, +"huadaofengye":1, +"huagu":1, +"huaian":1, +"huaibei163":1, +"huainet":1, +"huaiyangnews":1, +"huajiemba":1, +"huajx":1, +"hualady":1, +"hualongxiang":1, +"huamanche":1, +"huamu":1, +"huanancn":1, +"huangru":1, +"huangye88":1, +"huangyesoso":1, +"huanjinrong":1, +"huanq":1, +"huanqiu":1, +"huantu":1, +"huapinwang":1, +"huash":1, +"huashihongfeng":1, +"huasmaple":1, +"huatai":1, +"huatu":1, +"huaue":1, +"huawei":1, +"huaxi100":1, +"huaxia":1, +"huaxirc":1, +"huayin114":1, +"huayin520":1, +"huayuanjuye":1, +"huayuejob":1, +"huazhile":1, +"huazhu":1, +"hubai":1, +"hubeirc":1, +"hudong":1, +"hugd":1, +"hui800":1, +"huibo":1, +"huibojob":1, +"huiche":1, +"huiche100":1, +"huilaimai":1, +"huilan":1, +"huimaiche":1, +"huishangbao":1, +"huishi365":1, +"huishoushang":1, +"huisou":1, +"huitong123":1, +"huitu":1, +"huixiaodai":1, +"huizcn":1, +"huizhuang":1, +"hujiang":1, +"huluxia":1, +"hunangy":1, +"hunanpta":1, +"hunanrc":1, +"hunantv":1, +"hunbys":1, +"hunchunnet":1, +"hunlimama":1, +"hunqingren":1, +"hunt007":1, +"hunuo":1, +"huobi":1, +"huoche":1, +"huochepiao":1, +"huochepu":1, +"huodongjia":1, +"huodongxing":1, +"huohu123":1, +"huolinhe":1, +"huoshannews":1, +"huowan":1, +"huoxue":1, +"hupu":1, +"hushi114":1, +"huway":1, +"huxiu":1, +"hvbao":1, +"hwlai":1, +"hx-car":1, +"hx116":1, +"hx2car":1, +"hx95":1, +"hxen":1, +"hxfzzx":1, +"hxrc":1, +"hxsd":1, +"hxuu":1, +"hxyjw":1, +"hy123":1, +"hydcd":1, +"hyedu":1, +"hyjzw":1, +"hyqcw":1, +"hyrmw":1, +"hytechan":1, +"hywlm":1, +"hyxfhq":1, +"hyxnews":1, +"hyxww":1, +"hz321":1, +"hz66":1, +"hzagro":1, +"hzbj":1, +"hzbx":1, +"hzcnb":1, +"hzcnc":1, +"hzcskj":1, +"hzgjj":1, +"hzhr":1, +"hzhuti":1, +"hzins":1, +"hzland":1, +"hzlp":1, +"hzlw":1, +"hzmba":1, +"hznews":1, +"hznzcn":1, +"hzqx":1, +"hzrc":1, +"hzshw":1, +"hzsmesc":1, +"hztbc":1, +"hzti":1, +"hzutc":1, +"hzwmw":1, +"i-jjj":1, +"i-okla":1, +"i0456":1, +"i1515":1, +"i8i8i8":1, +"i9133":1, +"iacmall":1, +"iask":1, +"ib-china":1, +"ibamaol":1, +"ibanggo":1, +"ibangkf":1, +"ibeifeng":1, +"ibicn":1, +"ibm":1, +"ibusdb":1, +"ic10":1, +"ic37":1, +"ic98":1, +"icafe8":1, +"icaile":1, +"icbuy":1, +"ichengzi":1, +"ichtf":1, +"iciba":1, +"icis-china":1, +"icjyw":1, +"icpcw":1, +"icson":1, +"icxo":1, +"icycn":1, +"idcquan":1, +"idcspy":1, +"idongzhi":1, +"idooor":1, +"idqqimg":1, +"idting":1, +"ieche":1, +"iecity":1, +"iecworld":1, +"iefang":1, +"iezhan":1, +"ifeng":1, +"ifenghui":1, +"ifengimg":1, +"ifensi":1, +"iflying":1, +"ifsino":1, +"igeak":1, +"ihaiyan":1, +"ihanhua":1, +"ihei5":1, +"iheima":1, +"ihome99":1, +"ihongyou":1, +"ihoome":1, +"ihuatong":1, +"ii010":1, +"iianews":1, +"iiigo":1, +"iiijjj":1, +"iiyi":1, +"ijia360":1, +"ijiatv":1, +"ijie":1, +"ijinshan":1, +"ijjnews":1, +"ijxjj":1, +"ik123":1, +"ikaka":1, +"ikanchai":1, +"ikandian":1, +"ikang":1, +"ikuyy":1, +"ilangwen":1, +"ileehoo":1, +"ilongre":1, +"ilqut":1, +"im286":1, +"imanhua":1, +"imeimama":1, +"img.cctvpic":1, +"imglefeng":1, +"iminte":1, +"imlvyou":1, +"imosi":1, +"imqq":1, +"imrworldwide":1, +"in-en":1, +"in189":1, +"inabr":1, +"inanle":1, +"inchedao":1, +"indexedu":1, +"inezha":1, +"infiworks":1, +"infobidding":1, +"infohc":1, +"infzm":1, +"inhe":1, +"inlishui":1, +"inshion":1, +"intel":1, +"intertid":1, +"inuobi":1, +"investjilin":1, +"invitemedia":1, +"ioeoo":1, +"iooeoo":1, +"ioomoo":1, +"iooqoo":1, +"iooroo":1, +"ioroo":1, +"iouoo":1, +"iouter":1, +"iowoo":1, +"ip138":1, +"ip1840":1, +"ipadown":1, +"ipchina":1, +"ipetfair":1, +"ipincai":1, +"ipingshan":1, +"ipinyou":1, +"iqilu":1, +"iqingren":1, +"iqiyi":1, +"iraoping":1, +"irrichina":1, +"irs01":1, +"isanmen":1, +"ishaanxi":1, +"ishangman":1, +"ishowx":1, +"isoucai":1, +"it168":1, +"it7t":1, +"itbulo":1, +"itchaguan":1, +"itdcw":1, +"iteye":1, +"ithome":1, +"itmop":1, +"itoptrip":1, +"itravelqq":1, +"itshai":1, +"itxinwen":1, +"ivsky":1, +"iwebchoice":1, +"iwhr":1, +"ixiaoma":1, +"ixiawan":1, +"ixumu":1, +"iyaxin":1, +"iyaya":1, +"iyizhai":1, +"iyuanhong":1, +"iyxwzx":1, +"iyzx":1, +"izaojiao":1, +"izda":1, +"izdap":1, +"izdinix":1, +"izhufu":1, +"izhuti":1, +"j1":1, +"j2014":1, +"ja001":1, +"jaecdn":1, +"jaedu":1, +"jarhu":1, +"jaz581":1, +"jb1000":1, +"jbdown":1, +"jbzyw":1, +"jc114":1, +"jc35":1, +"jc68":1, +"jc85":1, +"jcbao":1, +"jcbctv":1, +"jcoal":1, +"jcqzw":1, +"jcrb":1, +"jcrcw":1, +"jcsychina":1, +"jctrans":1, +"jcwcn":1, +"jcz001":1, +"jczxb":1, +"jd":1, +"jd-cg":1, +"jd100":1, +"jdbbx":1, +"jdcjsr":1, +"jdgod":1, +"jdjob88":1, +"jdsc35":1, +"jdw001":1, +"jdypgxw":1, +"jdzj":1, +"jdzol":1, +"jewelchina":1, +"jfcaifu":1, +"jfdaily":1, +"jfgphzs":1, +"jfinfo":1, +"jgaoxiao":1, +"jgcx56":1, +"jgjob88":1, +"jgjw":1, +"jgsdaily":1, +"jgxxw":1, +"jh2sc":1, +"jh597":1, +"jhbezs":1, +"jhcxl":1, +"jhhssy":1, +"jia":1, +"jia360":1, +"jia400":1, +"jiafangyun":1, +"jiagle":1, +"jiahesj":1, +"jiahesuji":1, +"jiajiao114":1, +"jiajiao400":1, +"jiajiaoban":1, +"jiaju":1, +"jiaju001":1, +"jiajumi":1, +"jiajuol":1, +"jiakao":1, +"jiameng":1, +"jiameng001":1, +"jiameng8":1, +"jiancai":1, +"jiancaizhanhui":1, +"jiangduoduo":1, +"jiangdurencai":1, +"jianghairc":1, +"jianghuairc":1, +"jiangmin":1, +"jiangnews":1, +"jiangshi":1, +"jiangsurc":1, +"jiangxirc":1, +"jiankang":1, +"jiankangzu":1, +"jianke":1, +"jiankongbao":1, +"jianli-sky":1, +"jianshe99":1, +"jianso":1, +"jianzhi8":1, +"jianzhiabc":1, +"jiaomai":1, +"jiaoman":1, +"jiaotanqihuo":1, +"jiaoyimao":1, +"jiaoyulei":1, +"jiathis":1, +"jiatx":1, +"jiaxingsteel":1, +"jiayouqiche":1, +"jiayuan":1, +"jiazhao":1, +"jiazhuang6":1, +"jichuang360":1, +"jide123":1, +"jidi":1, +"jidongrc":1, +"jiemeng8":1, +"jiepei":1, +"jietusoft":1, +"jieyitong668":1, +"jifang360":1, +"jiiaa":1, +"jijidi":1, +"jike":1, +"jilaibao":1, +"jilaidai":1, +"jilinfc":1, +"jilinrc":1, +"jilinzhaopin":1, +"jin14":1, +"jinbifun":1, +"jincin":1, +"jinfr":1, +"jinfuzi":1, +"jingchurc":1, +"jingdianwan":1, +"jingjiawang":1, +"jingjinnews":1, +"jingjishi":1, +"jingpinke":1, +"jingwei":1, +"jingwumeishi":1, +"jingyanbus":1, +"jingzheng":1, +"jingzhengu":1, +"jinhejs":1, +"jinhongmc":1, +"jinhuatv":1, +"jinjianginns":1, +"jinkaoedu":1, +"jinku":1, +"jinlaoxi":1, +"jinmajia":1, +"jinmenrc":1, +"jinpu":1, +"jinquktv":1, +"jinshangrc":1, +"jinti":1, +"jinxiang114":1, +"jinyuan2008":1, +"jitimes":1, +"jitongtianxia":1, +"jiu6":1, +"jiudianzhaopin":1, +"jiukuaiyou":1, +"jiuxian":1, +"jiuzhouxue":1, +"jiwu":1, +"jixinet":1, +"jiyifa":1, +"jiyuantour":1, +"jj20":1, +"jj831":1, +"jjjaaa":1, +"jjjgame":1, +"jjjlll":1, +"jjlxpz":1, +"jjmmw":1, +"jjsrcw":1, +"jjtang":1, +"jjwn":1, +"jjzg365":1, +"jk51":1, +"jldledu":1, +"jlhcszx":1, +"jlit365":1, +"jljbbs":1, +"jljob88":1, +"jllnzz":1, +"jlmhk":1, +"jlonline":1, +"jlsgjt":1, +"jlshumei":1, +"jlsjswm":1, +"jlsmm":1, +"jluzh":1, +"jlxtdz":1, +"jlzkb":1, +"jm-tour":1, +"jmlr":1, +"jmrb":1, +"jmstatic":1, +"jn001":1, +"jnesc":1, +"jnflsky":1, +"jnlandao":1, +"jnlc":1, +"jnmama":1, +"jnmc":1, +"jnmte":1, +"jnnc":1, +"job-sky":1, +"job0663":1, +"job0768":1, +"job100":1, +"job10000":1, +"job1001":1, +"job120":1, +"job128":1, +"job168":1, +"job2299":1, +"job250":1, +"job256":1, +"job36":1, +"job369":1, +"job5156":1, +"job592":1, +"job616":1, +"job700":1, +"job8001":1, +"job910":1, +"job9151":1, +"job98":1, +"job9981":1, +"job9988":1, +"jobcn":1, +"jobeast":1, +"jobems":1, +"jobfn":1, +"jobgao":1, +"jobgojob":1, +"jobhb":1, +"jobpin":1, +"jobui":1, +"jobuy":1, +"jobvvv":1, +"jobyun":1, +"joeoo":1, +"joingoo":1, +"joobbe":1, +"joojcc":1, +"joojzz":1, +"joouoo":1, +"jooxoo":1, +"joyes":1, +"joyme":1, +"joyo":1, +"joyyang":1, +"jpkjz":1, +"jq-school":1, +"jqw":1, +"jr18":1, +"jrb2b":1, +"jrj":1, +"jrlpw":1, +"jrpengze":1, +"jrrsq":1, +"jrshx":1, +"jrsmw":1, +"jrw100":1, +"jrxjnet":1, +"jryghq":1, +"jrzj":1, +"jrzp":1, +"js-lottery":1, +"js0573":1, +"js118114":1, +"js119":1, +"js811":1, +"js95598":1, +"jsbc":1, +"jscj":1, +"jsenews":1, +"jsgc168":1, +"jshealth":1, +"jshyrsrc":1, +"jshze":1, +"jsjrc":1, +"jslottery":1, +"jsly001":1, +"jsnxs":1, +"jsqw":1, +"jsrc":1, +"jsrsrc":1, +"jsrxny":1, +"jssdw":1, +"jssjys":1, +"jstedu":1, +"jstour":1, +"jstv":1, +"jswmw":1, +"jsxlmed":1, +"jsxmws":1, +"jsyks":1, +"jsymyjs":1, +"jsypj":1, +"jszhaobiao":1, +"jszjsx":1, +"jt122":1, +"jtbole":1, +"jthysh":1, +"jtimg":1, +"jtlhome":1, +"jtxxol":1, +"jtyjy":1, +"ju51":1, +"juanpi":1, +"juchang":1, +"juedui100":1, +"juesheng":1, +"jufengshang":1, +"juhaof":1, +"jujiaonet":1, +"jumei":1, +"junbaike":1, +"junjiajob":1, +"junshishu":1, +"junzhe":1, +"junzhuan":1, +"junzimen":1, +"juooo":1, +"juoooo":1, +"juren":1, +"jutuw":1, +"juwai":1, +"juxia":1, +"juyouxi":1, +"juyuan":1, +"jx09":1, +"jx612345":1, +"jxazjd":1, +"jxcnt":1, +"jxcua":1, +"jxdcw":1, +"jxdiguo":1, +"jxdyf":1, +"jxedt":1, +"jxedu":1, +"jxfdc":1, +"jxfgts":1, +"jxga":1, +"jxgdw":1, +"jxgsgl":1, +"jxgydc":1, +"jxhcw":1, +"jxhi":1, +"jxjas":1, +"jxjatv":1, +"jxjjx":1, +"jxjtxy":1, +"jxjyzy":1, +"jxlcx":1, +"jxllt":1, +"jxltw":1, +"jxpaw":1, +"jxpf":1, +"jxpta":1, +"jxrcw":1, +"jxrencai":1, +"jxrsrc":1, +"jxrtv":1, +"jxsedu":1, +"jxslsd":1, +"jxsoso":1, +"jxsrr":1, +"jxt189":1, +"jxteacher":1, +"jxtourism":1, +"jxtutechan":1, +"jxwei":1, +"jxycw":1, +"jygycp":1, +"jyrcjl":1, +"jysedu":1, +"jyyuan":1, +"jz0574":1, +"jz5u":1, +"jzb":1, +"jzcool":1, +"jzjob007":1, +"jznj":1, +"jzpt":1, +"jzqyw":1, +"jzrb":1, +"jzshsc":1, +"jzvip":1, +"jzwcom":1, +"jzyx":1, +"k0410":1, +"k18":1, +"k191":1, +"k366":1, +"k369":1, +"k76":1, +"k8008":1, +"k8k9":1, +"kaba365":1, +"kadaiw":1, +"kadang":1, +"kaichewan":1, +"kaifu":1, +"kaifu7":1, +"kaiwind":1, +"kaixin001":1, +"kakaba":1, +"kameng":1, +"kanbox":1, +"kandeng":1, +"kandian":1, +"kanglu":1, +"kangq":1, +"kangzhi":1, +"kanimg":1, +"kankan":1, +"kankanews":1, +"kanshu":1, +"kanzhun":1, +"kaopu001":1, +"kaoshi110":1, +"kaoyan":1, +"kaoyanfuxi":1, +"kaoyee":1, +"kaozc":1, +"kazakhsoft":1, +"kb-medical":1, +"kds100":1, +"kdslife":1, +"kedou":1, +"keepc":1, +"kehou":1, +"kejiqi":1, +"kejixun":1, +"kekenet":1, +"kenfor":1, +"keqii":1, +"kesion":1, +"keyunzhan":1, +"kfsfu":1, +"kfw001":1, +"kimiss":1, +"kingdee":1, +"kingsoft":1, +"kisdee":1, +"kkeye":1, +"kkk5":1, +"kkkedu":1, +"kmguolv":1, +"kms88":1, +"kmxsedu":1, +"knowsky":1, +"koeoo":1, +"kongfz":1, +"kongzhong":1, +"konka":1, +"kooaoo":1, +"koofang":1, +"koolearn":1, +"koovoo":1, +"koowo":1, +"kooxoo":1, +"koubei":1, +"koudai":1, +"kq81":1, +"kq88":1, +"kqs114":1, +"kqzp":1, +"ks116":1, +"ks5u":1, +"ksbao":1, +"ksbbs":1, +"kskino":1, +"ktvcity":1, +"ktxp":1, +"ku6":1, +"ku6cdn":1, +"ku6img":1, +"ku6vms":1, +"kuai8":1, +"kuaidi100":1, +"kuaiji":1, +"kuailezu":1, +"kuailiyu":1, +"kuaiyoujia":1, +"kuakao":1, +"kuche":1, +"kufa88":1, +"kugou":1, +"kujiale":1, +"kukuplay":1, +"kunlun":1, +"kunshanrc":1, +"kuotu":1, +"kuparts":1, +"kuqyu":1, +"kutj":1, +"kutongji":1, +"kuwan8":1, +"kuyibu":1, +"kuyiso":1, +"kvov":1, +"ky-akoya":1, +"kyjia":1, +"kyp":1, +"kywmall":1, +"kzhuang":1, +"kzj365":1, +"kzjdb":1, +"l-zzz":1, +"lacpj":1, +"lady8844":1, +"lafaso":1, +"lagou":1, +"laidianduo":1, +"laidingba":1, +"laifeng":1, +"laijiuye":1, +"laiwang":1, +"lajmzs":1, +"lamabang":1, +"lamahui":1, +"lan1001":1, +"landai":1, +"landchina":1, +"landjs":1, +"landtu":1, +"lanfw":1, +"langfly":1, +"langtaojin":1, +"lanhii":1, +"lanou3g":1, +"lanqi":1, +"lanrentuku":1, +"lanrenzhaofang":1, +"lanrenzhijia":1, +"lanshandichan":1, +"lanyanwan":1, +"laohu":1, +"laoke":1, +"laomoo":1, +"laonanren":1, +"laoqianzhuang":1, +"laoren":1, +"laser-chn":1, +"laserfair":1, +"lashou":1, +"lashouimg":1, +"lavago":1, +"law-lib":1, +"law-star":1, +"law1818":1, +"lawtimeimg":1, +"lawyermr":1, +"lawyerwq":1, +"lazyren":1, +"lbgoo":1, +"lbx777":1, +"lc-news":1, +"lcfcw":1, +"lchssy":1, +"lcyx":1, +"ldrczpw":1, +"ldsshj":1, +"leatherhr":1, +"leazn":1, +"lecai":1, +"ledcac":1, +"ledcax":1, +"ledmmw":1, +"ledth":1, +"ledu":1, +"ledwn":1, +"lefeng":1, +"leglek":1, +"legolas-media":1, +"legongchang":1, +"lehecai":1, +"leho":1, +"leidian":1, +"leiphone":1, +"lejiaoyun":1, +"lejj":1, +"leju":1, +"lejuju":1, +"lengxiaohua":1, +"lenovo":1, +"lenovoqm":1, +"leqiyou":1, +"letao":1, +"letfind":1, +"letv":1, +"letvcdn":1, +"letvimg":1, +"lexun":1, +"leyoo":1, +"leyou":1, +"lezhi":1, +"leziyou":1, +"lfang":1, +"lfkt":1, +"lfxww":1, +"lg":1, +"lgmi":1, +"lgrcsc":1, +"lh36524":1, +"li63":1, +"liang360":1, +"lianjia":1, +"lianm":1, +"liansuo":1, +"liansuopinpai":1, +"liantu":1, +"liao1":1, +"liaoing":1, +"liaoshenrc":1, +"liba":1, +"lie-che":1, +"liebiao":1, +"liebo":1, +"liecheng":1, +"lieju":1, +"liepin":1, +"lierencai":1, +"lietou":1, +"lietou-static":1, +"lieyou":1, +"life139":1, +"lifeyoyo":1, +"lightget":1, +"lightingchina":1, +"lihaisheng":1, +"linekong":1, +"linezing":1, +"linfangwang":1, +"lingaoren":1, +"lingnanrc":1, +"lingshi":1, +"lingtuan":1, +"linjiang365":1, +"link-future":1, +"linkhx":1, +"linkwan":1, +"linuxdiyf":1, +"linyiren":1, +"liqucn":1, +"lishi5":1, +"lishifengyun":1, +"litianbg":1, +"liudu":1, +"liuhe":1, +"liuts":1, +"liuxilife":1, +"liuxue114":1, +"liuxue360":1, +"liuxue3600":1, +"liuxue86":1, +"liuxueca":1, +"live754":1, +"live800":1, +"livnj":1, +"liwai":1, +"liwuyou":1, +"liyezhongzhi":1, +"liyi99":1, +"lizi":1, +"lj597":1, +"ljforest":1, +"ljspjm":1, +"lkgame":1, +"lldgd":1, +"lllddd":1, +"lltqc":1, +"llvan":1, +"llzg":1, +"lm263":1, +"lmbang":1, +"lmdigi":1, +"ln-rc":1, +"ln2car":1, +"ln632":1, +"lndzz":1, +"lnfisher":1, +"lnlib":1, +"lnpaw":1, +"lnrsks":1, +"lntvu":1, +"lnzsks":1, +"locoso":1, +"lofter":1, +"logclub":1, +"logmein":1, +"loho88":1, +"lolbuku":1, +"longchengrc":1, +"longkangmiaomu":1, +"longre":1, +"longren":1, +"longshangrc":1, +"longtengcn":1, +"longwenedu":1, +"longzhouwan":1, +"looedu":1, +"loogoo":1, +"loorin":1, +"looyu":1, +"looyuoms":1, +"lotour":1, +"louoo":1, +"loupan":1, +"lousw":1, +"love169":1, +"love21cn":1, +"loveinhere":1, +"loveliao":1, +"lovingjob":1, +"lp91":1, +"lpb2b":1, +"lpsrc":1, +"lqjob88":1, +"lqzp":1, +"lrbaba":1, +"lrswl":1, +"ls666":1, +"lsdag":1, +"lsgzn":1, +"lsnetlib":1, +"lsrczpw":1, +"lssdjt":1, +"lssen":1, +"lsszj":1, +"lstcw":1, +"lsw88":1, +"lsyzzzz":1, +"lszxyey":1, +"luan163":1, +"luaninfo":1, +"luanren":1, +"lubanu":1, +"ludashi":1, +"lufax":1, +"lunwentianxia":1, +"luoanguo":1, +"luoherc":1, +"luosi":1, +"luoyuan597":1, +"lure123":1, +"lusongsong":1, +"luwenwang":1, +"luxtarget":1, +"luzhou4":1, +"lvhua":1, +"lvlian5":1, +"lvmama":1, +"lvping":1, +"lvse":1, +"lvseba":1, +"lvshou":1, +"lvtu":1, +"lvye":1, +"lvyou114":1, +"lvziyao":1, +"lwcj":1, +"lwedu":1, +"lwhfishing":1, +"lwhouse":1, +"lwinfo":1, +"lx0830":1, +"lxdns":1, +"lxtedu":1, +"ly":1, +"ly3506":1, +"lycos":1, +"lydfdz":1, +"lyfcw":1, +"lyfff":1, +"lygbole":1, +"lygczj":1, +"lygmedia":1, +"lygo":1, +"lygrc":1, +"lyhero":1, +"lyielts":1, +"lymffyjd":1, +"lyqcw":1, +"lyrcw":1, +"lywww":1, +"lywxww":1, +"lywzc":1, +"lzhuba":1, +"lzlj":1, +"lzsgaj":1, +"m148":1, +"m18":1, +"m1905":1, +"m3guo":1, +"m598":1, +"m6699":1, +"machine35":1, +"machine365":1, +"machjobs":1, +"made-in-china":1, +"magedu":1, +"mahua":1, +"maichawang":1, +"maidong100":1, +"maifang":1, +"maigoo":1, +"maijipu":1, +"maijx":1, +"mainone":1, +"maituan":1, +"maiwaiwai":1, +"maizufang":1, +"makepolo":1, +"malmam":1, +"mamacn":1, +"manggojia":1, +"mangocity":1, +"manmankan":1, +"manyou":1, +"manzuo":1, +"mao114":1, +"maomeilock":1, +"maoren8":1, +"maotiao":1, +"maoyan":1, +"maoyigu":1, +"maoyiw":1, +"maozhiwang":1, +"map456":1, +"mapabc":1, +"mapbar":1, +"marry5":1, +"marry52":1, +"marstv":1, +"masamaso":1, +"maslink":1, +"mathtag":1, +"mayi":1, +"mayiw":1, +"mayiyou":1, +"mb5u":1, +"mbachina":1, +"mbahome":1, +"mbalib":1, +"mbaobao":1, +"mbatrip":1, +"mbscss":1, +"mc-test":1, +"mc1314":1, +"mc870":1, +"mcbaidu":1, +"mcchina":1, +"mcixi":1, +"mcmqyc":1, +"mdvoo":1, +"meadin":1, +"mechr":1, +"med126":1, +"med66":1, +"medejob":1, +"mediaplex":1, +"mediav":1, +"medic360":1, +"medzpw":1, +"meet99":1, +"mei5w":1, +"meidianwang":1, +"meigui3":1, +"meihongfeng":1, +"meijing":1, +"meijw":1, +"meilele":1, +"meilishuo":1, +"meinv":1, +"meinvjz":1, +"meishanren":1, +"meishichina":1, +"meitantong":1, +"meitanwang":1, +"meitu":1, +"meituan":1, +"meiya":1, +"meiyuanchun":1, +"meizhou":1, +"meizhouchina":1, +"meizu":1, +"memall360":1, +"menkou":1, +"menmian":1, +"mepfair":1, +"mercachina":1, +"messebbs":1, +"metalchina":1, +"metaltechexpo":1, +"metroer":1, +"meyol":1, +"mf08s":1, +"mfdyy":1, +"mfw365":1, +"mgccjg":1, +"mgknives":1, +"mgshk":1, +"mgyun":1, +"mh28":1, +"mhdz":1, +"mhongfeng":1, +"mi":1, +"mianbao":1, +"miaomudi":1, +"miaotiao":1, +"miaozhen":1, +"micamika":1, +"microad-cn":1, +"microsoft":1, +"midea":1, +"miercn":1, +"miliao":1, +"mimimama":1, +"mingche":1, +"mingjian":1, +"mingluji":1, +"mingong123":1, +"mingxing":1, +"mingzhurc":1, +"mining120":1, +"mininghr":1, +"minjiangrc":1, +"mipang":1, +"mirautomation":1, +"misranim":1, +"missyuan":1, +"miui":1, +"miyun360":1, +"mizhe":1, +"mjceo":1, +"mjingpin":1, +"mjjq":1, +"mkzhan":1, +"mlbuy":1, +"mlt01":1, +"mmall":1, +"mmbang":1, +"mmfj":1, +"mmimm":1, +"mmjyw":1, +"mmrcw":1, +"mmsfw":1, +"mmstat":1, +"mmstw":1, +"mnkjxy":1, +"mnpaw":1, +"mnsfh":1, +"mnwww":1, +"mobao":1, +"moejam":1, +"mofang":1, +"mofangge":1, +"mogujie":1, +"moke8":1, +"momo35":1, +"monfr":1, +"monteamor":1, +"monternet":1, +"mookie1":1, +"moonbasa":1, +"mop":1, +"morcato":1, +"mosso":1, +"motie":1, +"motnt":1, +"motorchina":1, +"moukao":1, +"mouldu":1, +"moutaichina":1, +"moxingwang":1, +"moyiza":1, +"mozillaonline":1, +"mplife":1, +"mr91":1, +"msn":1, +"mstxx":1, +"mtime":1, +"mtnets":1, +"mtv123":1, +"mtw001":1, +"mumayi":1, +"muniao":1, +"muyee":1, +"muyingjie":1, +"muyingzhijia":1, +"muzhiwan":1, +"mw1950":1, +"mx175":1, +"mxwz":1, +"my0511":1, +"my0538":1, +"my0792":1, +"my0813":1, +"my0832":1, +"my399":1, +"my3dparts":1, +"my3w":1, +"my4399":1, +"my71":1, +"myapp":1, +"mybulkstock":1, +"mybuxiu":1, +"mybxg":1, +"mycar168":1, +"mychemjob":1, +"mycimt":1, +"mydown":1, +"mydrivers":1, +"myepjob":1, +"myhard":1, +"myjmw":1, +"myjob":1, +"mylegist":1, +"mypethome":1, +"mypharma":1, +"myrice":1, +"myrolan":1, +"myshipjob":1, +"myshuo":1, +"mysipo":1, +"mysteel":1, +"mysteelcdn":1, +"mysteelcms":1, +"mysteelweekly":1, +"mytophome":1, +"mytv365":1, +"myubbs":1, +"myyouse":1, +"myzhengxing":1, +"mzedu":1, +"mzrcw":1, +"mzstatic":1, +"mzwok":1, +"mzyfz":1, +"nahuo":1, +"naliwan":1, +"nanbuxc":1, +"nandu":1, +"nanjob":1, +"nanningjie":1, +"nanputuo":1, +"nantaihu":1, +"narkii":1, +"narutom":1, +"nb2car":1, +"nba":1, +"nbbltv":1, +"nbch2sc":1, +"nbcoop":1, +"nbgjj":1, +"nbjbsun":1, +"nbjnzx":1, +"nbqcrl":1, +"nbradio":1, +"nbwater":1, +"nbyouth":1, +"nc2sc":1, +"ncdiy":1, +"ncmgcc":1, +"ncxww":1, +"nd-medicine":1, +"nd090":1, +"nd2sc":1, +"nd597":1, +"nd999":1, +"nddaily":1, +"ndfang":1, +"nduoa":1, +"ne365":1, +"ne51":1, +"neeu":1, +"neimengrc":1, +"nengyuan":1, +"nerjob":1, +"nestcms":1, +"net114":1, +"netandtv":1, +"netbian":1, +"netease":1, +"netor":1, +"netqin":1, +"netsun":1, +"newft":1, +"newhua":1, +"newmaker":1, +"newoo":1, +"news18a":1, +"newsccn":1, +"newscnr":1, +"newshainan":1, +"newshuanan":1, +"newsxc":1, +"newsxy":1, +"newsyc":1, +"newzgc":1, +"nfcmag":1, +"nfrencai":1, +"ngjy":1, +"nh365":1, +"nhaidu":1, +"nhcsw":1, +"nhjyw":1, +"nhmgr":1, +"nhshequ":1, +"nhxxg":1, +"nhzhaopin":1, +"nhzj":1, +"nianw":1, +"night9":1, +"nihaotw":1, +"nihaowang":1, +"nike17":1, +"nimtt":1, +"ningbochina":1, +"ningboexport":1, +"ninghai":1, +"ninghaiauto":1, +"ninxun":1, +"nipic":1, +"niua":1, +"niuchengwang":1, +"niutuku":1, +"niuwan":1, +"niuwz":1, +"niwodai":1, +"niwota":1, +"nj110":1, +"njhaiwai":1, +"njltmp":1, +"njmuseum":1, +"njprice":1, +"njrsrc":1, +"njsjys":1, +"njxjyw":1, +"njycyj":1, +"nkhxl":1, +"nlxn":1, +"nmgfic":1, +"nmglabs":1, +"nmgrc":1, +"nmgzkj":1, +"nmrcw":1, +"nmsti":1, +"nnhdqm":1, +"nnjob":1, +"nnn666":1, +"nnwb":1, +"nong828":1, +"nongchengws":1, +"nongji1688":1, +"nongji360":1, +"nongjx":1, +"nongli":1, +"nongmintv":1, +"nongrisheng":1, +"nongyao001":1, +"nooeoo":1, +"noojoo":1, +"northtimes":1, +"nowec":1, +"nowscore":1, +"np5":1, +"np597":1, +"npcka":1, +"npckk":1, +"npicp":1, +"npjy":1, +"npsxjy":1, +"nrcce":1, +"nrclady":1, +"nrparking":1, +"nsjy":1, +"nsmovie":1, +"nsoso":1, +"nsw88":1, +"nsy6":1, +"ntalker":1, +"ntccjy":1, +"ntehs":1, +"ntgjj":1, +"ntjob88":1, +"ntjoy":1, +"ntlsxj":1, +"ntup":1, +"ntwenming":1, +"nubb":1, +"nuomi":1, +"nurqut":1, +"nvsheng":1, +"nxbys":1, +"nxedu":1, +"nxfang":1, +"ny1988":1, +"ny3721":1, +"ny688":1, +"nyzpw":1, +"nz165":1, +"nz86":1, +"nzczq":1, +"nzjsw":1, +"nzw-china":1, +"o571":1, +"oadz":1, +"obolee":1, +"oceanol":1, +"oecr":1, +"oeeee":1, +"oemol":1, +"oemresource":1, +"offcn":1, +"officese":1, +"offifurniture":1, +"ofuns":1, +"ofweek":1, +"ofzx":1, +"oho168":1, +"ohocn":1, +"ohqly":1, +"ohsx":1, +"oicq88":1, +"ok0559":1, +"ok619":1, +"okbuy":1, +"okhqb":1, +"oklx":1, +"okmk":1, +"okooo":1, +"okyan":1, +"ol-img":1, +"olcdn":1, +"older99":1, +"ometal":1, +"onccc":1, +"one-all":1, +"one101":1, +"onekeyrom":1, +"onlylady":1, +"oodii":1, +"oooggg":1, +"ooopic":1, +"operachina":1, +"optaim":1, +"or58":1, +"oranpage":1, +"oray":1, +"ordosggzyjy":1, +"orgcc":1, +"orsoon":1, +"ostudytour":1, +"otcms":1, +"oujiangrc":1, +"ourbloom":1, +"ourgame":1, +"ourxun":1, +"oussko":1, +"outlets365":1, +"ovodm":1, +"ozhibao":1, +"p-e-china":1, +"p2p110":1, +"p2p265":1, +"p2pchina":1, +"p2pfenhu":1, +"p2pxing":1, +"pahaoche":1, +"paidai":1, +"paipai":1, +"paipaiimg":1, +"paishoes":1, +"paljw":1, +"panduola":1, +"panguso":1, +"panjk":1, +"paochefang":1, +"paody":1, +"paopaoku":1, +"paoxue":1, +"paratong":1, +"paypal":1, +"pazx888":1, +"pc186":1, +"pc3w":1, +"pc51":1, +"pc6":1, +"pc841":1, +"pcbeta":1, +"pcpc521":1, +"pcpop":1, +"pcwl":1, +"pcxxw":1, +"pdhr":1, +"pdsxww":1, +"pe168":1, +"peixun360":1, +"peixun5":1, +"peiyou":1, +"pengchengrc":1, +"pengfu":1, +"pengke":1, +"penglaiu":1, +"pengpeng":1, +"pengrc":1, +"pengyou":1, +"pengzerencai":1, +"people258":1, +"peoplerail":1, +"pethr":1, +"petzp":1, +"pf168":1, +"pf178":1, +"pfwang":1, +"pgpop":1, +"ph66":1, +"pharmjx":1, +"phoenixtv":1, +"php100":1, +"phpchina":1, +"phpwind":1, +"piao":1, +"piaodown":1, +"piaoliang":1, +"pimei":1, +"pincai":1, +"pinfengws":1, +"pingan":1, +"pinganfang":1, +"pinganhd":1, +"pingguolv":1, +"pingshu8":1, +"pingtan597":1, +"pingtandao":1, +"pingxiaow":1, +"pinkecity":1, +"pinla":1, +"pinpai37":1, +"pinshan":1, +"pinyouc":1, +"pinzhituan":1, +"pipaw":1, +"pixlr":1, +"pizhou365":1, +"pjtime":1, +"pk38":1, +"pkzx":1, +"plxww":1, +"pm91":1, +"pnwww":1, +"pnzpw":1, +"pod100":1, +"podinns":1, +"poobuy":1, +"pop-fashion":1, +"pop136":1, +"pop800":1, +"popoho":1, +"popwan":1, +"powercdn":1, +"ppaaol":1, +"ppdai":1, +"ppkao":1, +"pplive":1, +"pppaaa":1, +"ppstream":1, +"ppsxx":1, +"pptv":1, +"ppwan":1, +"ppxmw":1, +"ppzhan":1, +"ppzhangui":1, +"ppzuche":1, +"prcedu":1, +"prnasia":1, +"psahz":1, +"psbc":1, +"pscjy":1, +"psjia":1, +"pstatp":1, +"psychcn":1, +"pszx":1, +"pt597":1, +"pt791":1, +"ptacn":1, +"ptbus":1, +"ptdao":1, +"ptjy":1, +"ptlogin2.qq":1, +"ptotour":1, +"ptpcp":1, +"puahome":1, +"pubmatic":1, +"puepu":1, +"puercn":1, +"puertea8":1, +"puerteaking":1, +"pumpbest":1, +"pupuwang":1, +"purchasingbbs":1, +"pusa123":1, +"pusa365":1, +"putaomiaomu":1, +"putclub":1, +"puworld":1, +"pvc123":1, +"px020":1, +"px0769":1, +"px101":1, +"px58":1, +"px8":1, +"pxrczpw":1, +"py168":1, +"pybxfc":1, +"pybxrc":1, +"pyinfo":1, +"pyjia":1, +"pyr6":1, +"pyspajs":1, +"pzoom":1, +"pzsh":1, +"pztuan":1, +"pzzx":1, +"q1":1, +"qa114":1, +"qbaobei":1, +"qc188":1, +"qc6":1, +"qcloud":1, +"qcoco":1, +"qcrencai":1, +"qctester":1, +"qctsw":1, +"qcw114":1, +"qcwe":1, +"qcwz8":1, +"qdbeian":1, +"qdfymr":1, +"qdmm":1, +"qdnrc":1, +"qdnrm":1, +"qdoulu":1, +"qdshuangjie":1, +"qdwenxue":1, +"qdzkb":1, +"qeecn":1, +"qeejoo":1, +"qegee":1, +"qeqeqe":1, +"qfang":1, +"qfzp":1, +"qgjxb":1, +"qhccw":1, +"qhdcc":1, +"qhdnews":1, +"qhdxw":1, +"qhgate":1, +"qhimg":1, +"qhinfo":1, +"qhjgj":1, +"qhldzb":1, +"qhlly":1, +"qhmed":1, +"qhnews":1, +"qhongfeng":1, +"qhsfgl":1, +"qhsrcw":1, +"qhswdx":1, +"qhsxnqcz":1, +"qhyedu":1, +"qhzk":1, +"qi-che":1, +"qiancheng100":1, +"qianggen":1, +"qianhuaweb":1, +"qianjia":1, +"qianjiangjob":1, +"qianlijob":1, +"qianlima":1, +"qianlong":1, +"qianlongnews":1, +"qianluntianxia":1, +"qiannianzhicheng":1, +"qianqian":1, +"qianqishou":1, +"qianqiuren":1, +"qianxs":1, +"qianyan001":1, +"qianyecao":1, +"qianzhan":1, +"qianzhengdaiban":1, +"qiaobutang":1, +"qiaopinche":1, +"qiaxing":1, +"qibosoft":1, +"qicaispace":1, +"qiche-china":1, +"qichetong":1, +"qicou":1, +"qidian":1, +"qieta":1, +"qihoo":1, +"qihuiwang":1, +"qikan":1, +"qilibali":1, +"qimbulak":1, +"qinbei":1, +"qincai":1, +"qincaitou":1, +"qingdaomedia":1, +"qingdaonews":1, +"qingdaozaixian":1, +"qinglanart":1, +"qingrenw":1, +"qiniu":1, +"qiniucdn":1, +"qiniudn":1, +"qinqiner":1, +"qiongzhourc":1, +"qipaoxian":1, +"qipei001":1, +"qipei8":1, +"qipeiren":1, +"qipeizhan":1, +"qire123":1, +"qishanwang":1, +"qiuhongfeng":1, +"qiushibaike":1, +"qiushibang":1, +"qiuxian":1, +"qiyefabu":1, +"qiyegu":1, +"qiyeku":1, +"qiyexxw":1, +"qiyi":1, +"qiyipic":1, +"qiyouwang":1, +"qiyu360":1, +"qiyue":1, +"qizuang":1, +"qjrc":1, +"qjy168":1, +"qkfang":1, +"qlgaokao":1, +"qlmlxg":1, +"qlnew":1, +"qlrc":1, +"qlswu":1, +"qlteacher":1, +"qlzpw":1, +"qm120":1, +"qm988":1, +"qmango":1, +"qmyue":1, +"qnsb":1, +"qooic":1, +"qoomoo":1, +"qopoo":1, +"qouoo":1, +"qp110":1, +"qphome":1, +"qq":1, +"qq163":1, +"qq190":1, +"qqbaobao":1, +"qqbiaoqing":1, +"qqcyl":1, +"qqdcw":1, +"qqershou":1, +"qqfangke":1, +"qqgexingqianming":1, +"qqhryz":1, +"qqkqw":1, +"qqma":1, +"qqmail":1, +"qqpifu":1, +"qqqggg":1, +"qqqooo":1, +"qqtn":1, +"qqtouxiang":1, +"qqtu8":1, +"qqwm2014":1, +"qqwwr":1, +"qqyy":1, +"qqzssl":1, +"qqzyw":1, +"qstatic":1, +"qsztc":1, +"qtour":1, +"qu-zhou":1, +"qu114":1, +"qu247":1, +"qu97":1, +"quanguoyoubian":1, +"quanji":1, +"quanjing":1, +"quanjingke":1, +"quanjinglian":1, +"quanmama":1, +"quanranchina":1, +"quantserve":1, +"quanwangtz":1, +"quanzhi":1, +"qudao":1, +"qudao168":1, +"qudong":1, +"qufeizhou":1, +"qufenlei":1, +"quhua":1, +"qulv":1, +"quna":1, +"qunar":1, +"qunarzz":1, +"qutouzi":1, +"quweiwu":1, +"quxiu":1, +"quzhouwang":1, +"qvod":1, +"qw168":1, +"qwjian":1, +"qx121":1, +"qx162":1, +"qx818":1, +"qxnzx":1, +"qxw18":1, +"qxyou":1, +"qy6":1, +"qycn":1, +"qyer":1, +"qyjpzx":1, +"qymgc":1, +"qyzyw":1, +"qz123":1, +"qz597":1, +"qz828":1, +"qzbbs":1, +"qzcbs":1, +"qzhxw":1, +"qznews360":1, +"qzone":1, +"qzrcw":1, +"qzrczpw":1, +"qzshangwu":1, +"qzwb":1, +"radiotj":1, +"rajyj":1, +"rangeretech":1, +"raorao":1, +"rayp":1, +"rc114":1, +"rc365":1, +"rc3721":1, +"rc392":1, +"rc536":1, +"rc929":1, +"rclawyer":1, +"rcwlm":1, +"rcxx":1, +"readnovel":1, +"real":1, +"recycle366":1, +"redidai":1, +"redocn":1, +"regishome":1, +"rencai5":1, +"rencaijob":1, +"rencailu":1, +"renren":1, +"renren-inc":1, +"renrentou":1, +"renrenzhe":1, +"renrzx":1, +"renwen":1, +"renzheng":1, +"rfchina":1, +"rhxj":1, +"risfond":1, +"rm0510":1, +"rmburl":1, +"roadoor":1, +"robam":1, +"roboo":1, +"robot-china":1, +"rohlan":1, +"rong360":1, +"rongjie360":1, +"rongshidai":1, +"rongshuxia":1, +"rootinhenan":1, +"roowei":1, +"rpjiaoyi":1, +"rrimg":1, +"rrs":1, +"rsqzs":1, +"ruan8":1, +"ruanmei":1, +"rubberhr":1, +"rubcn":1, +"rugao35":1, +"ruian":1, +"ruiwen":1, +"ruixinlong":1, +"runsky":1, +"rzrsrc":1, +"s-msn":1, +"s0593":1, +"s163":1, +"s1979":1, +"sa26":1, +"safe10000":1, +"safecome":1, +"saicgroup":1, +"saifutong":1, +"saige":1, +"saimogroup":1, +"saiqu":1, +"samsung":1, +"sanginn":1, +"sanguosha":1, +"sanhaostreet":1, +"sankewang":1, +"sanqan":1, +"sanqin":1, +"sanqindaily":1, +"santudi":1, +"sanyatour":1, +"sanygroup":1, +"sanyhi":1, +"sanyujixie":1, +"sasa123":1, +"sc157":1, +"sc2p":1, +"sc7":1, +"sc987":1, +"scaleb2b":1, +"scanscout":1, +"scanv":1, +"scbid":1, +"scbsm":1, +"sccnn":1, +"scdms":1, +"scgckj":1, +"scgdj":1, +"scgrain":1, +"school51":1, +"schoolmy":1, +"sci99":1, +"scnjnews":1, +"scorecardresearch":1, +"scpolicec":1, +"scpspa":1, +"scrc168":1, +"screenb2b":1, +"sctv":1, +"scxcxj":1, +"scxfxj":1, +"scxlc":1, +"scxttj":1, +"scxyes":1, +"sczfcg":1, +"sczytv":1, +"sd001":1, +"sdcheshi":1, +"sdchina":1, +"sdcoop":1, +"sddjw":1, +"sdenews":1, +"sdfdc":1, +"sdfqmm":1, +"sdg-china":1, +"sdgw":1, +"sdhltsp":1, +"sdhssy":1, +"sdjnnews":1, +"sdjob":1, +"sdjtcx":1, +"sdlib":1, +"sdnycxzy":1, +"sdo":1, +"sdongnews":1, +"sdrc315":1, +"sdticai":1, +"sdwenhua":1, +"sdxhce":1, +"sdxiaohongxing":1, +"seafang":1, +"seburi":1, +"see-say":1, +"seecq":1, +"seedit":1, +"seekic":1, +"seezy":1, +"segahome":1, +"segmentfault":1, +"sendong":1, +"senmit":1, +"sensafish":1, +"sensorshome":1, +"seowhy":1, +"serving-sys":1, +"sewworld":1, +"sf-express":1, +"sfacg":1, +"sflep":1, +"sg-rc":1, +"sg169":1, +"sg560":1, +"sgamer":1, +"sgrcw":1, +"sgzjkj":1, +"sh-telue":1, +"sh1188":1, +"sh51766":1, +"sh7":1, +"sh91":1, +"sha-steel":1, +"shache":1, +"shafa":1, +"shanchengrc":1, +"shandalu":1, +"shandongbeihai":1, +"shandongrc":1, +"shang360":1, +"shangbw":1, +"shangdianp":1, +"shangdu":1, +"shanghaigm":1, +"shanghaining":1, +"shanghairc":1, +"shangmuguoji":1, +"shangpin":1, +"shangpusou":1, +"shangxiang":1, +"shangxueba":1, +"shangyici":1, +"shanxirc":1, +"shaoyoo":1, +"shaqing":1, +"shbtob":1, +"shcaoan":1, +"shccig":1, +"shchehang":1, +"shcnws":1, +"shdazao":1, +"shduer":1, +"she120":1, +"shechipin365":1, +"shedunews":1, +"shejiben":1, +"shejiqun":1, +"shejis":1, +"shenchuang":1, +"shendu":1, +"shenghuadg":1, +"shengpay":1, +"shengpet":1, +"shengyidi":1, +"shengyuanguolv":1, +"shenzhenair":1, +"shenzhenhr":1, +"shexiannet":1, +"sheying8":1, +"shgao":1, +"shgjj":1, +"shhbm":1, +"shhkws":1, +"shholip":1, +"shibaili":1, +"shidz":1, +"shifenghk":1, +"shihuahr":1, +"shijiemil":1, +"shilehui":1, +"shilijiuxiang":1, +"shiliunet":1, +"shionry":1, +"shipin588":1, +"shipinzhuchi":1, +"shippingchina":1, +"shiwan":1, +"shiyouhr":1, +"shjmnc":1, +"shjtaq":1, +"shkss":1, +"shlicang":1, +"shlkjdyxgs":1, +"shmcws":1, +"shmds-edu":1, +"shmet":1, +"shmmw":1, +"shoeshr":1, +"shonsh":1, +"shouduhr":1, +"shoudurc":1, +"shoudurx":1, +"shouji":1, +"shouliwang":1, +"shouyao8":1, +"shouyou":1, +"shouyou520":1, +"shouyoubus":1, +"shouyoucdn":1, +"shouyoutv":1, +"show160":1, +"showhua":1, +"showji":1, +"shrongtai":1, +"shsti":1, +"shsunedu":1, +"shuame":1, +"shucai001":1, +"shufa":1, +"shuguocn":1, +"shuhai":1, +"shuichan51":1, +"shuigongye":1, +"shuiguo":1, +"shuiniaoticket":1, +"shuiw114":1, +"shunwang":1, +"shuobao":1, +"shuren100":1, +"shushi100":1, +"shuzixiaoyuan":1, +"shwsdp":1, +"shxbe":1, +"shxdx":1, +"shyywz":1, +"si114":1, +"sichuanrc":1, +"sidelianghang":1, +"sihongbbs":1, +"siilu":1, +"sijihf":1, +"siliaojixie":1, +"simwe":1, +"sina":1, +"sinaapp":1, +"sinaedge":1, +"sinaimg":1, +"sinajs":1, +"singbon":1, +"sinmv":1, +"sino-gas":1, +"sino-manager":1, +"sinocars":1, +"sinochem":1, +"sinochemoil":1, +"sinoef":1, +"sinohydro":1, +"sinolub":1, +"sinopec":1, +"sinopecgroup":1, +"sinopharm":1, +"sinopipenet":1, +"sinosig":1, +"sinosteel":1, +"sinotf":1, +"sinotrans":1, +"sinotrans-csc":1, +"sirenji":1, +"sisdown":1, +"sissiok":1, +"siyuefeng":1, +"sj998":1, +"sjapk":1, +"sjdfgl":1, +"sjfzxm":1, +"sjjob88":1, +"sjjy0738":1, +"sjtinfo":1, +"sjwg":1, +"sjwj":1, +"sjwyx":1, +"sjxyx":1, +"sjzcity":1, +"sjzland":1, +"sjzonline":1, +"sjzyxh":1, +"skinpp":1, +"sksnc":1, +"skxox":1, +"skycn":1, +"skyworth":1, +"slaili":1, +"sljob88":1, +"sljyj":1, +"slrbs":1, +"slstm":1, +"sm160":1, +"sm597":1, +"smartjx":1, +"smecq":1, +"smejs":1, +"smestar":1, +"smforestry":1, +"smhzs":1, +"smrcw":1, +"smswike":1, +"smvtc":1, +"smwrc":1, +"smzy":1, +"snail":1, +"snda":1, +"sndhr":1, +"sneac":1, +"snedu":1, +"snfox":1, +"snjt":1, +"snrtv":1, +"snsyedu":1, +"snupg":1, +"snyu":1, +"snzhao":1, +"so":1, +"soautos":1, +"socang":1, +"soche8":1, +"sodao":1, +"sofang":1, +"soft515":1, +"soft6":1, +"soft711":1, +"softbar":1, +"sogou":1, +"sogoucdn":1, +"sogua":1, +"sohu":1, +"sohusce":1, +"sojike":1, +"sojump":1, +"soku":1, +"solarbe":1, +"somenmian":1, +"sonhoo":1, +"soocang":1, +"sooker":1, +"sooopin":1, +"sooshong":1, +"sootoo":1, +"sooxue":1, +"soozhu":1, +"soperson":1, +"sortdoor":1, +"soso":1, +"sososteel":1, +"soubaoad":1, +"soucai":1, +"souchebang":1, +"soufun":1, +"soufunimg":1, +"soupei360":1, +"soupu":1, +"souqian":1, +"southcn":1, +"southmoney":1, +"soutudi":1, +"souxuexiao":1, +"sowang":1, +"soxsok":1, +"soyuli":1, +"sozhen":1, +"sp0312":1, +"spacechina":1, +"spcce":1, +"spdl":1, +"speiyou":1, +"spjixie":1, +"spjobhr":1, +"spjxcn":1, +"sportscn":1, +"springtour":1, +"spsb114":1, +"spzs":1, +"sq1996":1, +"sqzg":1, +"srcdd":1, +"srtong":1, +"srxww":1, +"srzc":1, +"ss256":1, +"sscmwl":1, +"ssfun":1, +"ssjzw":1, +"sslibrary":1, +"ssqzj":1, +"ssrcsc":1, +"ssreader":1, +"sssggg":1, +"sssjhw":1, +"sssuuu":1, +"ssswh":1, +"sswoo":1, +"st001":1, +"st5":1, +"standardcn":1, +"starbaby":1, +"starlott":1, +"staticsdo":1, +"stcn":1, +"stdaily":1, +"steelphone":1, +"stnts":1, +"stockstar":1, +"stone365":1, +"stonebuy":1, +"stonemsn":1, +"stonesm":1, +"stonexp":1, +"strong-imm":1, +"strong-study":1, +"studentboss":1, +"studydao":1, +"studyems":1, +"studyez":1, +"studyget":1, +"studyie":1, +"studyma":1, +"studynl":1, +"studysg":1, +"suaee":1, +"suanpi":1, +"subaonet":1, +"sucaitianxia":1, +"sudasuta":1, +"suizhoujie":1, +"sulyxin":1, +"sun0758":1, +"sun0769":1, +"sunily":1, +"suning":1, +"sunmen":1, +"sunnychina":1, +"sunplusedu":1, +"sunrise-china":1, +"sunsirs":1, +"sunstu":1, +"sunyeabiz":1, +"suohoo":1, +"supei":1, +"susong51":1, +"sutoo":1, +"suwurc":1, +"suxiege":1, +"suyuedu":1, +"suzhourc":1, +"svisa":1, +"swjoy":1, +"swkong":1, +"swwenshi":1, +"sx597":1, +"sxac":1, +"sxbjedu":1, +"sxcoal":1, +"sxdygbjy":1, +"sxfsw":1, +"sxfyxh":1, +"sxjzwb":1, +"sxkp":1, +"sxmtxs":1, +"sxncb":1, +"sxpmg":1, +"sxpta":1, +"sxrb":1, +"sxrtv":1, +"sxshealth":1, +"sxsme":1, +"sxsznews":1, +"sxtdrn":1, +"sxtour":1, +"sxtvs":1, +"sxvieworld":1, +"sxworker":1, +"sxxfqjyj":1, +"sxxl":1, +"sxycpc":1, +"sxycrb":1, +"sxyhq":1, +"sxykmgy":1, +"sy3688":1, +"syb365":1, +"sybang":1, +"syflowers":1, +"syingrc":1, +"syiptv":1, +"syj":1, +"syjiancai":1, +"symama":1, +"syqnr":1, +"syrcsc":1, +"syrczpw":1, +"sytlw":1, +"syxwnet":1, +"syyx":1, +"sz121":1, +"szaibite":1, +"szbaws":1, +"szcchy56":1, +"szdagao":1, +"szdfsk":1, +"szdwyy":1, +"szedu":1, +"szehs":1, +"szfa":1, +"szfcol":1, +"szffmr":1, +"szfthp":1, +"szhfdq":1, +"szhk":1, +"szhome":1, +"szhua":1, +"szhufu":1, +"szjc8":1, +"szlyw12306":1, +"szmall":1, +"szmama":1, +"szmj":1, +"szmolds":1, +"sznews":1, +"szooo":1, +"szpxb":1, +"szpxe":1, +"szqcw":1, +"szsanshang":1, +"szsmw":1, +"szsnews":1, +"sztjyy":1, +"szwudao":1, +"szyc":1, +"t0001":1, +"t0376":1, +"t139":1, +"t262":1, +"t55":1, +"t960":1, +"tahjlg":1, +"tahongyuan":1, +"taian":1, +"taihainet":1, +"taihexian":1, +"taikang":1, +"taiqiedu":1, +"taishansong":1, +"taizhou":1, +"takungpao":1, +"talyys":1, +"tangdou":1, +"tangjiu":1, +"tangongye":1, +"tantuw":1, +"tanx":1, +"tao123":1, +"taobao":1, +"taobaocdn":1, +"taoche":1, +"taoche100":1, +"taoci":1, +"taoci163":1, +"taocijob":1, +"taodake":1, +"taofang":1, +"taofen8":1, +"taohuren":1, +"taojindi":1, +"taojinji":1, +"taoke":1, +"taoktv":1, +"taolv365":1, +"taopic":1, +"taoren100":1, +"taoshouyou":1, +"taotaocar":1, +"taowo2sc":1, +"taoxiao":1, +"taoxie":1, +"taoxue":1, +"taoxuexiao":1, +"tapebuy":1, +"tarkett1872":1, +"tasjpf":1, +"taskcn":1, +"tatamr":1, +"tatazx":1, +"tatbsb":1, +"taxfls":1, +"tayffdb":1, +"tbscache":1, +"tcdai":1, +"tcgimg":1, +"tcl":1, +"tcpjw":1, +"tcrcsc":1, +"tcrczpw":1, +"tdfcw":1, +"tdimg":1, +"tdycr":1, +"tdycw":1, +"tdzyw":1, +"te6":1, +"tea160":1, +"tea846":1, +"teachercn":1, +"teauo":1, +"tech-food":1, +"techan":1, +"techanzhan":1, +"teeqee":1, +"telecomhr":1, +"tencent":1, +"tencentmind":1, +"tenpay":1, +"tentrue":1, +"tesehunan":1, +"testrust":1, +"tex68":1, +"texu1":1, +"tezgc":1, +"tezhongzhuangbei":1, +"tfol":1, +"tfysw":1, +"tgbus":1, +"tgnet":1, +"the9":1, +"thebeijingnews":1, +"thethirdmedia":1, +"thmz":1, +"tianan-cyber":1, +"tiancity":1, +"tiandaoedu":1, +"tianjihr":1, +"tianjimedia":1, +"tianjinrc":1, +"tianjinwe":1, +"tianqi":1, +"tianshannet":1, +"tianshanrc":1, +"tiantian":1, +"tianxia70":1, +"tianyablog":1, +"tianyaui":1, +"tianyuimg":1, +"tiaohao":1, +"tibet3":1, +"tibetcn":1, +"tibetcul":1, +"tibetinfor":1, +"tiekuangshi":1, +"tielingcn":1, +"tietuku":1, +"tieyou":1, +"tigtag":1, +"timedg":1, +"tingbook":1, +"tingroom":1, +"titan24":1, +"tjbhnews":1, +"tjfic":1, +"tjkx":1, +"tjmama":1, +"tjmsgk":1, +"tjxqjy":1, +"tjysyjs":1, +"tl100":1, +"tmall":1, +"tmhtour":1, +"tmjob88":1, +"tmsf":1, +"tmtpost":1, +"to8to":1, +"tobaccochina":1, +"tobosu":1, +"tochgo":1, +"todayonhistory":1, +"togoo":1, +"toioo":1, +"tom":1, +"tom365":1, +"tom61":1, +"tomdurrie":1, +"tompda":1, +"tongbai8":1, +"tongbu":1, +"tongde":1, +"tongleilive":1, +"tongliaowang":1, +"tonglingjob":1, +"tonglukuaijian":1, +"tongrenshi":1, +"tongxue8":1, +"tongyu800":1, +"tongzhuo100":1, +"tonong":1, +"toobiao":1, +"toobm":1, +"toocle":1, +"toofloor":1, +"toojj":1, +"toolsky":1, +"tooopen":1, +"tootour":1, +"topfo":1, +"topnews9":1, +"topsage":1, +"topu":1, +"topzj":1, +"tordax":1, +"totob":1, +"totocn":1, +"touclick":1, +"tourunion":1, +"tourzj":1, +"toutiao":1, +"touzhijia":1, +"toybaba":1, +"toysbaba":1, +"toysgu":1, +"tozao":1, +"tpooo":1, +"tprtc":1, +"tpzj":1, +"tqedu":1, +"tqkaoyan":1, +"tqmba":1, +"tqmpacc":1, +"tqznedu":1, +"trade2cn":1, +"tranbbs":1, +"travelsky":1, +"tremormedia":1, +"trip8080":1, +"tripbaba":1, +"trjcn":1, +"trmhw":1, +"trustexporter":1, +"tryine":1, +"trylist":1, +"tsingming":1, +"tsqyxxw":1, +"tsrcw":1, +"tswybjgs":1, +"tt-ly":1, +"tt0760":1, +"tt268":1, +"tteb":1, +"tthonghuo":1, +"ttkdex":1, +"ttmam":1, +"ttmeishi":1, +"ttmn":1, +"ttpet":1, +"ttplayer":1, +"ttpod":1, +"ttteee":1, +"ttyl5":1, +"ttys5":1, +"ttzcw":1, +"tu50":1, +"tuan800":1, +"tuanche":1, +"tuanimg":1, +"tuanjiebao":1, +"tuanweihui":1, +"tuboshu":1, +"tuchw":1, +"tudinet":1, +"tudou":1, +"tudouui":1, +"tugao":1, +"tui18":1, +"tuicool":1, +"tuitui99":1, +"tujia":1, +"tuku":1, +"tulaoshi":1, +"tuliu":1, +"tulvzu":1, +"tumanduo":1, +"tuniu":1, +"tuniucdn":1, +"tunliu588":1, +"tuofubao":1, +"tuohuangzu":1, +"tupianzj":1, +"tutechanw":1, +"tutu66":1, +"tuwan":1, +"tv189":1, +"tvhf":1, +"tvhuan":1, +"tvscn":1, +"tvsou":1, +"twwtn":1, +"tx96345":1, +"txdai":1, +"txjly":1, +"txooo":1, +"txssw":1, +"txtbbs":1, +"txwb":1, +"txwm":1, +"ty360":1, +"tybaba":1, +"tyhr":1, +"tylvyouw":1, +"tz0852":1, +"tz121":1, +"tz1288":1, +"tz2car":1, +"tz911":1, +"tz94":1, +"tzcz":1, +"tzfdc":1, +"tzhr":1, +"tzjob":1, +"tzkd":1, +"tzrc":1, +"tzrl":1, +"tzzfcg":1, +"tzzp":1, +"u009":1, +"u0762":1, +"u17":1, +"u69cn":1, +"u88":1, +"u8k8":1, +"u9time":1, +"u9yoyo":1, +"uc108":1, +"uc129":1, +"uctrac":1, +"uehtml":1, +"uemap":1, +"ufangw":1, +"uggd":1, +"uhaidao":1, +"uhenan":1, +"uimaker":1, +"uisdc":1, +"ujob138":1, +"ujob360":1, +"ukecy":1, +"ule":1, +"ulinix":1, +"ulunix":1, +"umiwi":1, +"un188":1, +"unibao":1, +"uninf":1, +"unionli":1, +"unionpay":1, +"university-hr":1, +"unjs":1, +"unpcn":1, +"uoeoo":1, +"uoko":1, +"uooqoo":1, +"uouojk":1, +"up360":1, +"upaidui":1, +"upaiyun":1, +"ups":1, +"upyun":1, +"usbseries":1, +"useso":1, +"usportnews":1, +"usteel":1, +"utourworld":1, +"uu2car":1, +"uu38":1, +"uu7c":1, +"uueasy":1, +"uusee":1, +"uutuu":1, +"uuu9":1, +"uuufun":1, +"uuxoo":1, +"uuyoyo":1, +"uuzhufu":1, +"uuzu":1, +"uwan":1, +"uycar":1, +"uycnr":1, +"uydj":1, +"uzai":1, +"uzaicdn":1, +"uzzf":1, +"v2ex":1, +"v312":1, +"v3gp":1, +"value500":1, +"vancl":1, +"vanclimg":1, +"vanke":1, +"vchangyi":1, +"vcooline":1, +"vdolady":1, +"veerchina":1, +"veg-china":1, +"verycd":1, +"verydz":1, +"vhostgo":1, +"vhuaqiao":1, +"vibmro":1, +"vicovico":1, +"vikecn":1, +"vip":1, +"vip800":1, +"vipcn":1, +"vipshop":1, +"viptijian":1, +"vipyl":1, +"visionunion":1, +"visitgd":1, +"visitsz":1, +"vista123":1, +"vivijk":1, +"vizu":1, +"vjia":1, +"vlongbiz":1, +"vmall":1, +"vmovier":1, +"vobao":1, +"vodjk":1, +"vooec":1, +"vooroo":1, +"vrbeing":1, +"vsharing":1, +"vsuch":1, +"vvjob":1, +"vvvdj":1, +"vx":1, +"vxinyou":1, +"w7000":1, +"w707":1, +"wa8688":1, +"waaku":1, +"wabuw":1, +"wadongxi":1, +"wahawang":1, +"waheaven":1, +"waihuo":1, +"waiwaitu":1, +"waiyu8":1, +"wallstreetcn":1, +"wan":1, +"wandodo":1, +"wandoujia":1, +"wangdai3":1, +"wangdaibaike":1, +"wangdaibang":1, +"wangdaibangshou":1, +"wangdaicaifu":1, +"wangdaidp":1, +"wangdaiguancha":1, +"wangdaiqianyan":1, +"wangdaisky":1, +"wangdaitiandi":1, +"wangdaiwuyou":1, +"wangdaizhijia":1, +"wangdaizhinan":1, +"wanggou":1, +"wanggou86":1, +"wangjiags":1, +"wangjiajg":1, +"wangjinlou":1, +"wangjiu":1, +"wangpiao":1, +"wangqi":1, +"wangtu":1, +"wanguan":1, +"wangye":1, +"wangyouhu":1, +"wanjidao":1, +"wanjingchina":1, +"wanku":1, +"wanlitong":1, +"wanmei":1, +"wanmeimr":1, +"wansecheng":1, +"wanxf":1, +"wanxia":1, +"wanyouxi7":1, +"wanyx":1, +"wanzhoujob":1, +"wapurl":1, +"warchina":1, +"warcraftchina":1, +"watchstor":1, +"waterchina":1, +"wbshsc":1, +"wbtt315":1, +"wcnimg":1, +"wcsrcw":1, +"wcwang":1, +"wdj168":1, +"wdji":1, +"wdjyzx":1, +"wdsjz":1, +"wdzx":1, +"wealink":1, +"weamax":1, +"weathercn":1, +"web3389":1, +"web4008":1, +"webjx":1, +"webpowerchina":1, +"webterren":1, +"webuuo":1, +"webxgame":1, +"wechatnet":1, +"wed139":1, +"wedba":1, +"wedsoso":1, +"wehefei":1, +"weibo":1, +"weicaifu":1, +"weilanhaian":1, +"weilanliuxue":1, +"weimob":1, +"weiningnews":1, +"weinisi":1, +"weiphone":1, +"weixiaodian":1, +"weizhangjilu":1, +"weizhangwang":1, +"weizhuangxiu":1, +"weke":1, +"weleve":1, +"wemvp":1, +"wendu":1, +"wenjingjiaoyu":1, +"wenjuan":1, +"wenlingrc":1, +"wenshizhai":1, +"wenwuchina":1, +"wenxue24":1, +"wenyoutai":1, +"wenzhouglasses":1, +"wenzifanyi":1, +"wesmp":1, +"west263":1, +"wf777":1, +"wfbbs":1, +"wff168":1, +"wfjyxxg":1, +"wfrsks":1, +"wh3351":1, +"whatchina":1, +"whblct":1, +"whcic":1, +"whctv":1, +"whedu21":1, +"whhouse":1, +"whicu":1, +"whjm":1, +"whjuren":1, +"whlsgzn":1, +"whqyw":1, +"whrhkj":1, +"whshangceng":1, +"whwd":1, +"whwz":1, +"wifun":1, +"wihu":1, +"win007":1, +"win7china":1, +"win8china":1, +"wincn":1, +"windchn":1, +"windows7en":1, +"wine9":1, +"winenice":1, +"wines-info":1, +"wingwit":1, +"winhometex":1, +"winshang":1, +"winupon":1, +"winxuan":1, +"wishdown":1, +"wj001":1, +"wjdaily":1, +"wjnin":1, +"wjshw":1, +"wkepu":1, +"wkimg":1, +"wlgyy":1, +"wlkst":1, +"wlmq":1, +"wlmqwb":1, +"wlool":1, +"wlstock":1, +"wlwcn":1, +"wm927":1, +"wmordos":1, +"wmzhe":1, +"wnrczpw":1, +"wnwb":1, +"wnxwzx":1, +"wo116114":1, +"wodingche":1, +"wofang":1, +"wofangwang":1, +"woiyu":1, +"wojubl":1, +"wokanfang":1, +"wokeji":1, +"wokoko":1, +"wolai":1, +"womai":1, +"woman91":1, +"womenofchina":1, +"woniu":1, +"wood-china":1, +"wood168":1, +"wooshoes":1, +"workec":1, +"world-metal":1, +"world-stone":1, +"worldhm":1, +"worldscrap":1, +"worlduc":1, +"wowchina":1, +"woyaogexing":1, +"woyewan":1, +"woying":1, +"woyouguanjia":1, +"wpxap":1, +"wrating":1, +"wsbedu":1, +"wsbynews":1, +"wsche":1, +"wshang":1, +"wshangyun":1, +"wsloan":1, +"wsmhw":1, +"wsqsgo":1, +"wsxq":1, +"wtdianlan":1, +"wto9000":1, +"wtt365":1, +"wudang360":1, +"wudao":1, +"wudaotv":1, +"wuhuansoft":1, +"wujianghr":1, +"wujue":1, +"wulumuqijiaochetuoyun":1, +"wumii":1, +"wusa5":1, +"wutongzi":1, +"wuxingarden":1, +"wuxirc":1, +"wuxjob":1, +"wuyishan":1, +"wuyuan168":1, +"wwenglish":1, +"wxhouse":1, +"wxjob":1, +"wxmama":1, +"wxrb":1, +"wxthw":1, +"wy000":1, +"wyjiuyuanheiji":1, +"wz121":1, +"wz12333":1, +"wz2sc":1, +"wz517":1, +"wzcbd":1, +"wzfg":1, +"wzhd":1, +"wzjob":1, +"wzms":1, +"wzpeace":1, +"wzsee":1, +"wzszp":1, +"wztvu":1, +"wzwh":1, +"wzyds":1, +"wzzbtb":1, +"x3cn":1, +"x4321":1, +"xa999":1, +"xabbs":1, +"xadlan":1, +"xafc":1, +"xahhw":1, +"xajob":1, +"xanet110":1, +"xaonline":1, +"xapcn":1, +"xaprice":1, +"xarc8":1, +"xatvs":1, +"xawan":1, +"xawb":1, +"xbaixing":1, +"xbiao":1, +"xbmhw":1, +"xbxxb":1, +"xbychemoxing":1, +"xbymoxing":1, +"xc0769":1, +"xcarimg":1, +"xcmg":1, +"xcpxw":1, +"xcrc999":1, +"xcy8":1, +"xd":1, +"xdnice":1, +"xdowns":1, +"xdwan":1, +"xf88":1, +"xfbst":1, +"xffcol":1, +"xfqcol":1, +"xfwed":1, +"xfzc":1, +"xgz":1, +"xgzrc":1, +"xh-expo":1, +"xhcedu":1, +"xhdedu":1, +"xhuaian":1, +"xiachufang":1, +"xiadu":1, +"xialinying":1, +"xialv":1, +"xiamenair":1, +"xiami":1, +"xian-tourism":1, +"xianbey":1, +"xianbx":1, +"xiancn":1, +"xiangauto":1, +"xiangcunyou":1, +"xianghunet":1, +"xiangjiangyw":1, +"xiangmu":1, +"xiangrikui":1, +"xiangshenghang":1, +"xiangshu":1, +"xianguo":1, +"xianjj":1, +"xianweizhang":1, +"xianyuwang":1, +"xiao84":1, +"xiaohei":1, +"xiaojiangguo123":1, +"xiaoliangkou":1, +"xiaoma":1, +"xiaomi":1, +"xiaomi001":1, +"xiaomishu":1, +"xiaomizha":1, +"xiaonaodai":1, +"xiaonei":1, +"xiaopi":1, +"xiaoshuo766":1, +"xiaoxiangrc":1, +"xiaoyeren":1, +"xiaoyuanfeng":1, +"xiaozhi123":1, +"xiaozhu":1, +"xiaozhustatic1":1, +"xiashanet":1, +"xiazaiba":1, +"xiazaizhijia":1, +"xicai":1, +"xiche168":1, +"xichengrc":1, +"xiezilou":1, +"xifeng2008":1, +"xigu":1, +"xihairc":1, +"xilu":1, +"ximalaya":1, +"xincheping":1, +"xincx":1, +"xindaibao":1, +"xinddy":1, +"xinfenlei":1, +"xingcishan":1, +"xinggan":1, +"xingjiezs":1, +"xingmucaoye":1, +"xingshu":1, +"xingtaituan":1, +"xingzuo123":1, +"xingzuowu":1, +"xinhainews":1, +"xinhe99":1, +"xinhua08":1, +"xinhuacang":1, +"xinhuanet":1, +"xinhuatone":1, +"xinjiewang":1, +"xinjunshi":1, +"xinkz":1, +"xinli001":1, +"xinli110":1, +"xinlimaoyi":1, +"xinluobo":1, +"xinm123":1, +"xinmanyuan":1, +"xinmicn":1, +"xinnet":1, +"xinniangjie":1, +"xinnong":1, +"xinpg":1, +"xinpian":1, +"xinshaow":1, +"xintaizhou":1, +"xintuan":1, +"xinwangjing":1, +"xinxianrener":1, +"xinxiqu":1, +"xinxiwo":1, +"xinxiyi":1, +"xinxunwang":1, +"xinyanju":1, +"xinyi":1, +"xinyurc":1, +"xipu520":1, +"xitek":1, +"xituan":1, +"xiu":1, +"xiubei":1, +"xiuke":1, +"xiwenquan":1, +"xixiarc":1, +"xixik":1, +"xiyuit":1, +"xizhou":1, +"xizi":1, +"xj0993":1, +"xj169":1, +"xj5u":1, +"xj71":1, +"xj917":1, +"xjcfan":1, +"xjdaily":1, +"xjflcp":1, +"xjhr":1, +"xjjjb":1, +"xjrb":1, +"xjtaobao":1, +"xjtour":1, +"xjtsnews":1, +"xjw6188":1, +"xkhouse":1, +"xkwx":1, +"xkxm":1, +"xlsti":1, +"xlys1904":1, +"xlzx":1, +"xm3502":1, +"xmfc":1, +"xmfish":1, +"xmhljsz":1, +"xmhouse":1, +"xmwj":1, +"xn121":1, +"xna8":1, +"xnjjob":1, +"xnmtd":1, +"xnonl":1, +"xns315":1, +"xnwsq":1, +"xoeoo":1, +"xoyo":1, +"xp14":1, +"xp510":1, +"xp597":1, +"xpgod":1, +"xpxww":1, +"xqingnet":1, +"xrtvu":1, +"xs9999":1, +"xsbxw":1, +"xsbz":1, +"xshr":1, +"xsjob":1, +"xskhome":1, +"xsool":1, +"xsrtvu":1, +"xszrcw":1, +"xtcanlian":1, +"xtjc":1, +"xtuan":1, +"xuanruanjian":1, +"xuanwww":1, +"xuanxuan":1, +"xuchangbbs":1, +"xudongjixie":1, +"xue2you":1, +"xue5156":1, +"xueau":1, +"xueda":1, +"xueersi":1, +"xueeu":1, +"xuefu5":1, +"xueid":1, +"xuejiazhao":1, +"xuejp":1, +"xuekeedu":1, +"xuekewang":1, +"xueshanrc":1, +"xuesoo":1, +"xuexi111":1, +"xuexiaodaquan":1, +"xuexifangfa":1, +"xuexigang":1, +"xuexila":1, +"xuexiniu":1, +"xuexun":1, +"xueyiyun":1, +"xujc":1, +"xumulc":1, +"xumurc":1, +"xumuren":1, +"xungou":1, +"xunlei":1, +"xunleimil":1, +"xunyangwang":1, +"xunyiwenyao":1, +"xunyou":1, +"xutour":1, +"xuzhoujob":1, +"xw365":1, +"xwen":1, +"xwie":1, +"xx007":1, +"xx351":1, +"xxdm":1, +"xxhls":1, +"xxluo":1, +"xxplzx":1, +"xxyw":1, +"xy":1, +"xy9981":1, +"xya8":1, +"xydhl":1, +"xyfc":1, +"xyg100":1, +"xygmed":1, +"xyppzx":1, +"xyrczpw":1, +"xyrtv":1, +"xywy":1, +"xyzs":1, +"xzavt":1, +"xzbfgg":1, +"xzbhzl":1, +"xzboyue":1, +"xzfhgg":1, +"xzjgw":1, +"xzjxsg":1, +"xzjyh":1, +"xzrbw":1, +"xzren":1, +"xzsnw":1, +"xzsyw":1, +"xzuan":1, +"xzxw":1, +"xzxx":1, +"xzyuhui":1, +"xzyxdz":1, +"y13255262618":1, +"y2002":1, +"y5201314":1, +"ya247":1, +"ya597":1, +"yacol":1, +"yahan-wenhua":1, +"yahxi":1, +"yakolux":1, +"yananuniversity":1, +"yandongjixie":1, +"yangcheng8":1, +"yanghongzhou":1, +"yangjiajiao":1, +"yangshitianqi":1, +"yangtse":1, +"yangzhi":1, +"yanlimeirong":1, +"yantaihr":1, +"yantuchina":1, +"yanxiu":1, +"yanzhaorc":1, +"yaochufa":1, +"yaodou":1, +"yaoee":1, +"yaofangwang":1, +"yaojobs":1, +"yaolan":1, +"yaoliwang":1, +"yaolvyou":1, +"yaowan":1, +"yaozh":1, +"yaozs":1, +"yasn":1, +"yayawan":1, +"yb983":1, +"ybbbs":1, +"ybbgn":1, +"ybjk":1, +"ybvv":1, +"ybxww":1, +"ybyulong":1, +"yc38":1, +"yc678":1, +"yc920":1, +"ycbole":1, +"yccar":1, +"yccdn":1, +"ycdyz":1, +"ycgjj":1, +"ychr":1, +"ycicw":1, +"ycpai":1, +"ycrczpw":1, +"ycrusher":1, +"ycshequ":1, +"ycwb":1, +"ycxc":1, +"ycywsh":1, +"yczihua":1, +"ydstatic":1, +"ye40":1, +"yeekang":1, +"yeepay":1, +"yeqiaohui":1, +"yes515":1, +"yesfr":1, +"yeshj":1, +"yesky":1, +"yesmyimg":1, +"yesmywine":1, +"yeyou":1, +"yeyoucdn":1, +"yeyoudaquan":1, +"yeyoujia":1, +"yeyouw":1, +"ygjj":1, +"yhachina":1, +"yhbimg":1, +"yhd":1, +"yhgcc":1, +"yhjj":1, +"yhkgjt":1, +"yhnw":1, +"yhwt":1, +"yi7":1, +"yi958":1, +"yicai":1, +"yiche":1, +"yichemall":1, +"yicheshi":1, +"yidaba":1, +"yieldmanager":1, +"yifucj":1, +"yihaodian":1, +"yihaodianimg":1, +"yijiaqin":1, +"yilewan":1, +"yili":1, +"yingbishufa":1, +"yingchuang":1, +"yingjiesheng":1, +"yingkelawyer":1, +"yinglianre":1, +"yingmoo":1, +"yingsheng":1, +"yingtanwang":1, +"yingxiao360":1, +"yingyu":1, +"yinhangzhaopin":1, +"yinlianbang":1, +"yinsha":1, +"yinshihui":1, +"yintai":1, +"yinxiuwang":1, +"yinyuetai":1, +"yiqifa":1, +"yiqin":1, +"yiqirong":1, +"yiqisoo":1, +"yiqisooimg":1, +"yirendai":1, +"yishizige":1, +"yishu":1, +"yisoche":1, +"yisou":1, +"yisou5":1, +"yiweigou":1, +"yiwu2":1, +"yiwubuy":1, +"yiwufair":1, +"yiwugou":1, +"yixieshi":1, +"yixue001":1, +"yixue99":1, +"yixuezp":1, +"yixun":1, +"yiyedu":1, +"yiyigreen":1, +"yizhaoche":1, +"yizhaopin":1, +"yizhituo":1, +"yizimg":1, +"yjbys":1, +"yjkyj":1, +"yk0579":1, +"ykimg":1, +"ykrx":1, +"yktchina":1, +"yktworld":1, +"ykwin":1, +"yl1001":1, +"yljy":1, +"ylr114":1, +"ylrb":1, +"ylsw":1, +"yltvb":1, +"ylw168":1, +"ylzmhzs":1, +"ymfile":1, +"ymt360":1, +"ymxfishing":1, +"yn114":1, +"yn16":1, +"yn56":1, +"yn987":1, +"yncoop":1, +"yndaily":1, +"yndtjj":1, +"ynet":1, +"ynfjw":1, +"yngkseed":1, +"yngp":1, +"ynhouse":1, +"ynhr":1, +"yninfo":1, +"ynit580":1, +"ynjgy":1, +"ynjtt":1, +"ynpew":1, +"ynpost":1, +"ynpxrz":1, +"ynshangji":1, +"ynsugar":1, +"yntch":1, +"ynwangli":1, +"ynwjs":1, +"ynxdfpr":1, +"ynzp":1, +"yoao":1, +"yodao":1, +"yodbank":1, +"yododo":1, +"yofus":1, +"yoher":1, +"yohobuy":1, +"yohoshow":1, +"yoka":1, +"yokacdn":1, +"yolbax":1, +"yongchengren":1, +"yongjiangrc":1, +"yongsao":1, +"yongshengmold":1, +"yongweizhiye":1, +"yongzhou":1, +"yoohouse":1, +"yooli":1, +"yooyo":1, +"youba":1, +"youban":1, +"youbian":1, +"youbianku":1, +"youbibi":1, +"youboy":1, +"youc":1, +"youdao":1, +"youfangw":1, +"yougou":1, +"youhua":1, +"youhunan":1, +"youjiao":1, +"youjindi":1, +"youkecn":1, +"youkee":1, +"youkelai":1, +"youku":1, +"youlonger":1, +"youmeiji":1, +"younet":1, +"yousee":1, +"youshang":1, +"youtx":1, +"youwo":1, +"youxi":1, +"youxiake":1, +"youxiduo":1, +"youxiniao":1, +"youxiping":1, +"youxiqun":1, +"youxiuhui":1, +"youxiwangguo":1, +"youyuan":1, +"youyy":1, +"youzu":1, +"yoyobaby":1, +"yoyojie":1, +"yoyou":1, +"ypall":1, +"yqbdt":1, +"yqcn":1, +"yqdown":1, +"yqrc":1, +"yrd100":1, +"yrdedu":1, +"ys168":1, +"yshsports":1, +"yssjw":1, +"ysxyyt":1, +"ytbbs":1, +"ytbxw":1, +"ythouse":1, +"ythuayi":1, +"ytjob":1, +"ytrain":1, +"ytrczpw":1, +"ytwrc":1, +"ytxsoft":1, +"ytxww":1, +"yuanlai":1, +"yuanlin":1, +"yuanlin001":1, +"yuanlin365":1, +"yuanyetrip":1, +"yubooa":1, +"yuduxx":1, +"yue365":1, +"yuecheng":1, +"yuejianglou":1, +"yuemei":1, +"yuhou":1, +"yuhuan":1, +"yuhuijob":1, +"yujia":1, +"yuloo":1, +"yunbaoriji":1, +"yuncheng":1, +"yundaex":1, +"yundianrc":1, +"yunguicn":1, +"yunhepan":1, +"yunhosting":1, +"yunos":1, +"yunshipei":1, +"yunshow":1, +"yuntaodu":1, +"yunuu":1, +"yunyangrc":1, +"yupoo":1, +"yuqihuang":1, +"yuqinge":1, +"yurenwan":1, +"yurun":1, +"yusuan":1, +"yutong":1, +"yuxinews":1, +"yuzhicl":1, +"yw597":1, +"ywbb":1, +"yx-s":1, +"yx618":1, +"yxbao":1, +"yxcyh":1, +"yxdown":1, +"yxduo":1, +"yxlady":1, +"yxm":1, +"yxybb":1, +"yxyey":1, +"yxzoo":1, +"yy":1, +"yy138":1, +"yy7091":1, +"yy960":1, +"yyarea":1, +"yycqc":1, +"yyfad":1, +"yyfdcw":1, +"yyfeicui":1, +"yyjia":1, +"yypxcn":1, +"yyqcw":1, +"yyrtv":1, +"yysgsp":1, +"yyt360":1, +"yytcdn":1, +"yyyqqq":1, +"yyyyba":1, +"yzdiaoyu":1, +"yzdjbh":1, +"yzfcw":1, +"yzforex":1, +"yzqcw":1, +"yzrc":1, +"yzwb":1, +"yzxw":1, +"yzydt":1, +"z-china":1, +"z699":1, +"za169":1, +"zangao":1, +"zangyaofang":1, +"zanzw":1, +"zaoche168":1, +"zaojiao":1, +"zastatic":1, +"zbcars":1, +"zbhouse":1, +"zbird":1, +"zbjimg":1, +"zblogcn":1, +"zc85":1, +"zcheer":1, +"zcjob88":1, +"zcmechanic":1, +"zcom":1, +"zcrcw":1, +"zcwz":1, +"zdface":1, +"zdmimg":1, +"zf875":1, +"zf9918":1, +"zg0543":1, +"zgbfw":1, +"zgbm":1, +"zgchangfang":1, +"zgchawang":1, +"zgcmlm":1, +"zgcwrc":1, +"zgdhy":1, +"zgdjyj":1, +"zgdrive":1, +"zgftjg":1, +"zgfznews":1, +"zgfzrc":1, +"zggjj":1, +"zggwyw":1, +"zghgrc":1, +"zghmjjw":1, +"zghmsc":1, +"zghqcy":1, +"zgjczzw":1, +"zgjf168":1, +"zgjhjy":1, +"zgjjc":1, +"zgjjclw":1, +"zgjkcyw":1, +"zgjrrc":1, +"zgjsks":1, +"zgjsw":1, +"zgjtb":1, +"zgjxrc":1, +"zgjzlw":1, +"zgkjzx":1, +"zgkqw":1, +"zglqw":1, +"zglxw":1, +"zglyfzw":1, +"zgmjrc":1, +"zgmod":1, +"zgnhzx":1, +"zgong":1, +"zgpinche":1, +"zgpingshu":1, +"zgppny":1, +"zgqcrc":1, +"zgqczj":1, +"zgqpc":1, +"zgqw":1, +"zgqxn":1, +"zgrcjyw":1, +"zgrcw":1, +"zgsbzlcy":1, +"zgsc123":1, +"zgshengsi":1, +"zgsprc":1, +"zgsxzs":1, +"zgsyb":1, +"zgsydw":1, +"zgtnzx":1, +"zgwdq":1, +"zgwlrc":1, +"zgwmrc":1, +"zgxzw":1, +"zgycrc":1, +"zgycwx":1, +"zgyey":1, +"zgyyrc":1, +"zgza":1, +"zgzcw":1, +"zgznh":1, +"zgzx114":1, +"zgzxzsw":1, +"zh-hr":1, +"zh-hz":1, +"zh-sc":1, +"zh28":1, +"zh51home":1, +"zh853":1, +"zhanggame":1, +"zhangjk":1, +"zhangpu597":1, +"zhaocaihr":1, +"zhaochafa":1, +"zhaokaoku":1, +"zhaopin":1, +"zhaopinxitong":1, +"zhaoshang-sh":1, +"zhaoshang01":1, +"zhaoshang100":1, +"zhaoshang800":1, +"zhaoshangbao":1, +"zhaosheng":1, +"zhaotie":1, +"zhazhi":1, +"zhbiao":1, +"zhbit":1, +"zhcoo":1, +"zhcpic":1, +"zhcw":1, +"zhe800":1, +"zhefun":1, +"zhejiangrc":1, +"zhenai":1, +"zhenai77":1, +"zhenews":1, +"zhenfangyuan":1, +"zhengtongedu":1, +"zhengwutong":1, +"zhengzhoubus":1, +"zhengzhoudongyang":1, +"zhengzhoulvxing":1, +"zhenpin":1, +"zheyibu":1, +"zhgnews":1, +"zhhdq":1, +"zhhtd":1, +"zhibo8":1, +"zhiboche":1, +"zhicheng":1, +"zhidian":1, +"zhiding8":1, +"zhidiy":1, +"zhifang":1, +"zhigame":1, +"zhigou":1, +"zhihu":1, +"zhiji":1, +"zhijia":1, +"zhijinsteel":1, +"zhijinwang":1, +"zhileng":1, +"zhimantian":1, +"zhimg":1, +"zhitechan":1, +"zhituad":1, +"zhiweiwenshi":1, +"zhixiaoren":1, +"zhixiaorenurl":1, +"zhixuan":1, +"zhiye":1, +"zhiyoula":1, +"zhiyuanyun":1, +"zhizhen":1, +"zhiziyun":1, +"zhld":1, +"zhongbuauto":1, +"zhongkao":1, +"zhonglongmotor":1, +"zhongshangwang":1, +"zhongso":1, +"zhongsou":1, +"zhongxingift":1, +"zhongyidong":1, +"zhongzhourc":1, +"zhrmms":1, +"zhsho":1, +"zhsnxy":1, +"zhtool":1, +"zhtv":1, +"zhuangjiba":1, +"zhuangku":1, +"zhuangpin":1, +"zhuannet":1, +"zhuantiku":1, +"zhuapo":1, +"zhuaxia":1, +"zhubaijia":1, +"zhubajie":1, +"zhuicun":1, +"zhujia360":1, +"zhujiangrc":1, +"zhujiangroad":1, +"zhuke":1, +"zhulang":1, +"zhulituan":1, +"zhulong":1, +"zhuokearts":1, +"zhuoku":1, +"zhuqike":1, +"zhuzao":1, +"zhuzaohr":1, +"zhuzhouwang":1, +"zhwdw":1, +"zhxjyw":1, +"zhyedu":1, +"zibosky":1, +"zijiayoucn":1, +"zikao1688":1, +"zikao365":1, +"zimilan":1, +"zisha":1, +"zisuyouw":1, +"zitichina":1, +"zixia":1, +"zj":1, +"zj121":1, +"zj123":1, +"zj60":1, +"zj9":1, +"zjbar":1, +"zjcheshi":1, +"zjcoal":1, +"zjcoop":1, +"zjcyts":1, +"zjhnedu":1, +"zjhq":1, +"zjjsepc":1, +"zjjta":1, +"zjjyzx":1, +"zjknews":1, +"zjkonline":1, +"zjks":1, +"zjlottery":1, +"zjlscourt":1, +"zjolcdn":1, +"zjpark":1, +"zjphis":1, +"zjpse":1, +"zjrc":1, +"zjrw":1, +"zjrxz":1, +"zjstv":1, +"zjtcn":1, +"zjwater":1, +"zjwchc":1, +"zjwmw":1, +"zjxf119":1, +"zjxxt":1, +"zjyhtour":1, +"zjypw":1, +"zjzcj":1, +"zk5u":1, +"zk71":1, +"zkxww":1, +"zlhome":1, +"zlt365":1, +"zmd5":1, +"zmingcx":1, +"zmnedu":1, +"znds":1, +"znhr":1, +"znimg":1, +"znxkw":1, +"zocai":1, +"zoioo":1, +"zol":1, +"zongheng":1, +"zongsifang":1, +"zoomlion":1, +"zoopda":1, +"zoossoft":1, +"zp365":1, +"zp515":1, +"zp666":1, +"zpb365":1, +"zplm":1, +"zpzj":1, +"zq6":1, +"zqgame":1, +"zqzq":1, +"zr597":1, +"zr98":1, +"zs30":1, +"zs310":1, +"zsbicycle":1, +"zsezt":1, +"zshl":1, +"zshouyou":1, +"zshyqx":1, +"zslz":1, +"zsnet":1, +"zsnets":1, +"zssou":1, +"zst365":1, +"zstx88":1, +"zt5":1, +"ztedu":1, +"ztgame":1, +"ztsfc":1, +"zuche":1, +"zuchecdn":1, +"zuchezu":1, +"zudong":1, +"zufang":1, +"zugame":1, +"zugou":1, +"zuiyou":1, +"zuiyouxi":1, +"zujuan":1, +"zulinbao":1, +"zunyiol":1, +"zuoche":1, +"zuojiaju":1, +"zuojiang":1, +"zuowen":1, +"zupuk":1, +"zupulu":1, +"zuzhirenshi":1, +"zuzuche":1, +"zwcad":1, +"zwhz":1, +"zwtxnews":1, +"zx114w":1, +"zx185":1, +"zx79":1, +"zx98":1, +"zxdyw":1, +"zxfishing":1, +"zxhsd":1, +"zxip":1, +"zxiubbs":1, +"zxrcw":1, +"zxwbb":1, +"zxxk":1, +"zxyhw":1, +"zxzhijia":1, +"zy":1, +"zy12580":1, +"zycdxw":1, +"zycmmt":1, +"zyctd":1, +"zynews":1, +"zynw":1, +"zyrb":1, +"zyt-life":1, +"zyue":1, +"zyuexc":1, +"zyzhan":1, +"zz361":1, +"zz51":1, +"zz597":1, +"zz91":1, +"zzbaike":1, +"zzbbs":1, +"zzdjw":1, +"zzfc":1, +"zzgjj":1, +"zzhcz":1, +"zzidc":1, +"zzjob88":1, +"zzlwjy":1, +"zzol360":1, +"zzpgm":1, +"zzqifan":1, +"zzsfjc":1, +"zzsr":1, +"zzstep":1, +"zztyedu":1, +"zzwljc":1, +"zzwms":1, +"zzyjs":1, +"zzyycc":1, +"zzz4":1 +},"de":{ +"china-botschaft":1 +},"edu":{ +"snai":1, +"sxrtvu":1 +},"fm":{ +"douban":1, +"jing":1, +"lvxing":1 +},"fr":{ +"amb-chine":1 +},"hk":{ +"95599":1, +"ctrip.com":1, +"hkidc":1, +"takungpao.com":1, +"xsren":1 +},"im":{ +"cli":1, +"iapps":1, +"iyy":1, +"yixin":1 +},"info":{ +"gmold":1, +"iyaya":1, +"lztech":1, +"meihua":1, +"williamlong":1 +},"jobs":{ +"cn":1 +},"jp":{ +"china-embassy.or":1 +},"kr":{ +"ctrip.co":1 +},"la":{ +"33":1, +"36":1, +"51":1, +"55":1, +"900":1, +"chaxun":1, +"iz":1, +"qzone":1, +"ulinix":1 +},"me":{ +"21me":1, +"bole":1, +"dzj":1, +"kyhs":1, +"nanxi":1, +"shijue":1, +"tvb":1, +"wmpic":1, +"yuksel":1 +},"mobi":{ +"i1515":1 +},"net":{ +"001sj":1, +"00615":1, +"020p":1, +"022job":1, +"024anfang":1, +"027":1, +"0376":1, +"0513":1, +"0515car":1, +"0517":1, +"0566cn":1, +"0577home":1, +"0755":1, +"0871job":1, +"0875job":1, +"0898":1, +"0937":1, +"10050":1, +"100jiaoyu":1, +"114my":1, +"115800":1, +"117800":1, +"125cn":1, +"126":1, +"126job":1, +"127":1, +"12900":1, +"158":1, +"1616":1, +"163":1, +"16789":1, +"177dj":1, +"17eu":1, +"17u":1, +"18dao":1, +"200":1, +"21ccom":1, +"21cp":1, +"21ks":1, +"21page":1, +"21shte":1, +"230la":1, +"232100":1, +"263":1, +"269":1, +"293":1, +"2dx":1, +"2liang":1, +"2mdn":1, +"33map":1, +"360guakao":1, +"360tz":1, +"365kl":1, +"370830":1, +"37wan":1, +"39":1, +"39jhw":1, +"3conline":1, +"3ddl":1, +"3edu":1, +"3sjob":1, +"3snews":1, +"414500":1, +"434300":1, +"4399":1, +"461000":1, +"471700":1, +"51":1, +"51daifu":1, +"51fanli":1, +"51gaokao":1, +"51hunter":1, +"51la":1, +"51test":1, +"51zxw":1, +"5251":1, +"52cake":1, +"52car":1, +"52ch":1, +"52hotel":1, +"52jj":1, +"52op":1, +"52sales":1, +"52tian":1, +"52xiaoyuan":1, +"533":1, +"5460":1, +"546300":1, +"54cn":1, +"54kefu":1, +"5566":1, +"557":1, +"5678":1, +"56888":1, +"56ye":1, +"571400":1, +"5721":1, +"5d6d":1, +"5haoxue":1, +"5i9u":1, +"5zsz":1, +"6259114":1, +"68design":1, +"71sou":1, +"78dm":1, +"7usa":1, +"800400":1, +"81629":1, +"84bus":1, +"8671":1, +"86to81":1, +"88353588":1, +"91tea":1, +"94117":1, +"962":1, +"962200":1, +"96369":1, +"96519":1, +"97616":1, +"999120":1, +"acftu":1, +"actoys":1, +"admin5":1, +"ah163":1, +"ahage":1, +"ahcl":1, +"ajiang":1, +"aladd":1, +"ali213":1, +"androidonline":1, +"ankang":1, +"anngo":1, +"anyv":1, +"artintern":1, +"artron":1, +"asp163":1, +"aupu":1, +"awotuan":1, +"ayrc":1, +"b2b168":1, +"baidulian":1, +"bailinsi":1, +"baixing":1, +"baoan":1, +"baoye":1, +"bbscms":1, +"bd365":1, +"bdinfo":1, +"beianchaxun":1, +"beifang":1, +"bejoin":1, +"bfxx":1, +"bianjibu":1, +"bjhytc":1, +"bjmama":1, +"blsy":1, +"bmjob":1, +"bokee":1, +"bsjy":1, +"buildjob":1, +"byfdc":1, +"bzcm":1, +"c-ps":1, +"c114":1, +"caihao":1, +"caiyn":1, +"catv":1, +"cbi360":1, +"ccedin":1, +"ccen":1, +"ccmn":1, +"cctb":1, +"ccughc":1, +"cdgtw":1, +"cdjjedu":1, +"cdn86":1, +"cdrx":1, +"cdyou":1, +"ce02":1, +"cer":1, +"cfanclub":1, +"cfedu":1, +"changdedj":1, +"chebiao":1, +"chem365":1, +"chetuanwang":1, +"chexun":1, +"china-cba":1, +"china-cx":1, +"china-lottery":1, +"china-school":1, +"china-train":1, +"china123":1, +"chinabug":1, +"chinaccd":1, +"chinacity":1, +"chinacrane":1, +"chinaeic":1, +"chinaeol":1, +"chinagames":1, +"chinagb":1, +"chinahrd":1, +"chinahufa":1, +"chinajoy":1, +"chinametro":1, +"chinamost":1, +"chinamr":1, +"chinapaper":1, +"chinapipe":1, +"chinaseed":1, +"chinashishi":1, +"chinauma":1, +"chinaunix":1, +"chinavalue":1, +"chinawestnews":1, +"chutou":1, +"cidu":1, +"cifco":1, +"cippe":1, +"citiz":1, +"ciwong":1, +"cixiedu":1, +"cjdby":1, +"cn":1, +"cn2car":1, +"cnbaowen":1, +"cnchache":1, +"cncn":1, +"cndianlu":1, +"cndog":1, +"cneln":1, +"cnfrp":1, +"cngsda":1, +"cnhbsb":1, +"cnhuadong":1, +"cninfo":1, +"cnjky":1, +"cnjyw":1, +"cnki":1, +"cnkoi":1, +"cnkssb":1, +"cnlinfo":1, +"cnmf":1, +"cnmsw":1, +"cnnyjx":1, +"cnool":1, +"cnpension":1, +"cnphotos":1, +"cnread":1, +"cnshicai":1, +"cnsifa":1, +"cnwen":1, +"cnworld":1, +"cnxishui":1, +"cnyw":1, +"cnzz":1, +"codefans":1, +"coovee":1, +"copperhome":1, +"corpease":1, +"cpecc":1, +"cphoto":1, +"cqfishing":1, +"cqgj":1, +"cqhxjd":1, +"cqmama":1, +"cqnews":1, +"cqqnb":1, +"cqrc":1, +"cqshebao":1, +"cqwu":1, +"cqxszx":1, +"cqyc":1, +"cqyun":1, +"cqzx":1, +"csats":1, +"csdn":1, +"csgia":1, +"csln":1, +"csmama":1, +"csstoday":1, +"cxtvu":1, +"cyjyw":1, +"cyol":1, +"czch":1, +"czinfo":1, +"czonline":1, +"d1xz":1, +"da-hang":1, +"dadiwang":1, +"daoxila":1, +"daozhou":1, +"daqing":1, +"dd001":1, +"ddjob":1, +"dehua":1, +"demage":1, +"dflr":1, +"dfysw":1, +"dgjy":1, +"dgrc":1, +"dianzhanggui":1, +"diaochapai":1, +"diaokeji":1, +"dimeng":1, +"discuz":1, +"diwei":1, +"dltm":1, +"dlzj":1, +"dmjob":1, +"dmjy":1, +"doubleclick":1, +"doyoo":1, +"doyouhike":1, +"dqccc":1, +"dragon-guide":1, +"dt123":1, +"duba":1, +"dv37":1, +"dvbbs":1, +"dvimon":1, +"dyedu":1, +"dyfc":1, +"dytt8":1, +"dz169":1, +"dzjob":1, +"dzwork":1, +"dzxw":1, +"eamn":1, +"edudh":1, +"edudown":1, +"edudt":1, +"eeeqi":1, +"emlog":1, +"emushroom":1, +"ershou":1, +"esnai":1, +"fangfa":1, +"fayo":1, +"fdlt":1, +"fecn":1, +"feijiu":1, +"fengj":1, +"fivecarts":1, +"fjlib":1, +"fjsy":1, +"fjtv":1, +"flash8":1, +"foodmate":1, +"forgechina":1, +"foyuan":1, +"frly":1, +"fsjy":1, +"ftutj":1, +"futurescn":1, +"fx120":1, +"fxxww":1, +"fynews":1, +"fzxyyl":1, +"gangong":1, +"gaoan":1, +"gayaha":1, +"gcimg":1, +"gdcct":1, +"gdcic":1, +"gdsin":1, +"gfedu":1, +"gg163":1, +"gimoo":1, +"globalimporter":1, +"gongzhao":1, +"goodacc":1, +"gotozjj":1, +"gouhuasuan":1, +"gqsoso":1, +"grfy":1, +"gtcfla":1, +"guqu":1, +"gxbs":1, +"gxcic":1, +"gxer":1, +"gxexam":1, +"gxipo":1, +"gxsti":1, +"gyedu":1, +"gz-travel":1, +"gz007":1, +"gzw":1, +"haier":1, +"hainan":1, +"hainanpc":1, +"haorencai":1, +"hb123":1, +"hb12369":1, +"hbcdc":1, +"hbhz":1, +"hbnews":1, +"hbrd":1, +"hbycw":1, +"hdzc":1, +"hhjy":1, +"hhncp":1, +"hhsq":1, +"hk-0898":1, +"hkwb":1, +"hledu":1, +"hlgnet":1, +"hlj":1, +"hlje":1, +"hm-3223":1, +"hncic":1, +"hnfz":1, +"hngawj":1, +"hngjj":1, +"hnjkw":1, +"hnkjonline":1, +"hnlrx":1, +"hnlzw":1, +"hnnw":1, +"hnol":1, +"hnpi":1, +"hnskl":1, +"hnsxfj":1, +"hntianya":1, +"hnwn":1, +"homomo":1, +"hostadm":1, +"hrbhw":1, +"hrblaw":1, +"hsjy":1, +"htbenet":1, +"hteacher":1, +"htexam":1, +"huaxiajiayuan":1, +"huedu":1, +"hunanedu":1, +"huoche":1, +"huochepiao":1, +"hustonline":1, +"hwit":1, +"hxsx":1, +"hxzg":1, +"hyedu":1, +"hyk123":1, +"hynews":1, +"hyxzx":1, +"hyyxw":1, +"hzedu":1, +"hzjxy":1, +"hzjys":1, +"hzlib":1, +"hzpzs":1, +"hzrx":1, +"hzyhrc":1, +"icar168":1, +"ichacha":1, +"ilinyi":1, +"imp3":1, +"importfood":1, +"indexedu":1, +"inhe":1, +"irs01":1, +"ishang":1, +"itcpn":1, +"itiexue":1, +"itpub":1, +"itsogo":1, +"itunion":1, +"iwms":1, +"jandan":1, +"jb51":1, +"jbedu":1, +"jczsw":1, +"jdedu":1, +"jdzol":1, +"jfxx":1, +"jgedu":1, +"jgny":1, +"jhjy":1, +"jiadinglife":1, +"jiajushichang":1, +"jiangsuedu":1, +"jiaodong":1, +"jiaoshizhaopin":1, +"jiaozhou":1, +"jichuang":1, +"jiemo":1, +"jieyue":1, +"jinglao":1, +"jingnei":1, +"jinrongren":1, +"jinshuju":1, +"jius":1, +"jixie":1, +"jjwxc":1, +"jl168":1, +"jlqf":1, +"jlstnet":1, +"jnzxw":1, +"job234":1, +"jobinhe":1, +"jsinfo":1, +"jsr365":1, +"jupai":1, +"jwjy":1, +"jxjjw":1, +"jynews":1, +"jysq":1, +"jzcn":1, +"jzdd":1, +"kaidi163":1, +"kanghu":1, +"kantao":1, +"kdnet":1, +"kejet":1, +"kejiqikan":1, +"khez":1, +"khly":1, +"km169":1, +"kmeb":1, +"ksgs":1, +"kuaidi100":1, +"kuakao":1, +"la-bbs":1, +"labbase":1, +"langge":1, +"laohuangli":1, +"lenosoft":1, +"letian":1, +"lh168":1, +"lhjy":1, +"li63":1, +"lingw":1, +"lingyuan":1, +"linxi":1, +"lipu":1, +"liuxue51":1, +"liveuc":1, +"lixianedu":1, +"ljia":1, +"lmjx":1, +"lnny":1, +"longhoo":1, +"longjiang":1, +"longmen":1, +"lonlife":1, +"lotour":1, +"ls520":1, +"lsjyw":1, +"ltesting":1, +"luohuedu":1, +"lwjy":1, +"lwnews":1, +"lyfxw":1, +"lygrc":1, +"lyjob":1, +"lyjy":1, +"lysk":1, +"lyvec":1, +"lywj":1, +"lz54":1, +"lzgd":1, +"lzsq":1, +"lzzy":1, +"machineryinfo":1, +"mafengwo":1, +"makepolo":1, +"map456":1, +"masfy":1, +"meishij":1, +"meituan":1, +"mm111":1, +"mnrb":1, +"mshw":1, +"msxf":1, +"mushroommarket":1, +"mybu":1, +"mycar168":1, +"mycollect":1, +"myhostadmin":1, +"myrb":1, +"mysteel":1, +"nanrenwo":1, +"nbcredit":1, +"nbks":1, +"nbol":1, +"nbptweb":1, +"nbzhaopin":1, +"nengyuan":1, +"net-school":1, +"netat":1, +"netover":1, +"newasp":1, +"newsmth":1, +"newssc":1, +"newyx":1, +"ng114":1, +"ngrx":1, +"nhedu":1, +"ninghai":1, +"njbxjy":1, +"nmgf":1, +"nmtravel":1, +"nnnews":1, +"nongyezhan":1, +"nosea":1, +"nphoto":1, +"ns365":1, +"ntjy":1, +"ntxx":1, +"nurqut":1, +"nxnews":1, +"nxng":1, +"ohedu":1, +"oilchem":1, +"onegreen":1, +"onfun":1, +"onlinedown":1, +"oschina":1, +"ourseo":1, +"p5w":1, +"pafj":1, +"pageadmin":1, +"pagechoice":1, +"paixie":1, +"paopaoche":1, +"pchome":1, +"phedu":1, +"phome":1, +"phpwind":1, +"picol":1, +"pingnan":1, +"pjob":1, +"pledu":1, +"pnol":1, +"polyv":1, +"powereasy":1, +"prestan":1, +"pthl":1, +"pynet":1, +"pyrc":1, +"pzzc":1, +"qdedu":1, +"qdhr":1, +"qdmama":1, +"qgnews":1, +"qgny":1, +"qgyyzs":1, +"qikao":1, +"qincai":1, +"qiugouxinxi":1, +"qnct":1, +"qndb":1, +"qp365":1, +"qqxuan":1, +"qyedu":1, +"qyjz":1, +"qzedu":1, +"qzkj":1, +"qzzs":1, +"railcn":1, +"raoke":1, +"rc86":1, +"rdedu":1, +"rencai":1, +"replays":1, +"riridy":1, +"rongchang":1, +"rtbidder":1, +"sandai":1, +"sanwen":1, +"sc-overseasinfo":1, +"scedu":1, +"scetc":1, +"scfzw":1, +"scjks":1, +"scopen":1, +"scpv":1, +"scrtvu":1, +"sddp":1, +"sdedu":1, +"sdinfo":1, +"sdjxyj":1, +"sdtyjixie":1, +"sdyjdz":1, +"seosrx":1, +"sepu":1, +"sg91":1, +"sh1800":1, +"shangc":1, +"shanghaimuseum":1, +"shanghaitour":1, +"shanglin":1, +"shaoyangnews":1, +"shedu":1, +"shengyijie":1, +"sherc":1, +"shlll":1, +"shopin":1, +"shouyouzhijia":1, +"shuajizhijia":1, +"shuanghui":1, +"shxb":1, +"shynws":1, +"shyuzhi":1, +"shzfzz":1, +"silverlightchina":1, +"sinahk":1, +"single6":1, +"sinoss":1, +"sitall":1, +"sjyzc":1, +"smjy":1, +"sohu":1, +"songshuhui":1, +"sosoo":1, +"sosw":1, +"sqjg":1, +"sshr":1, +"ssjy":1, +"stedu":1, +"suedu":1, +"sunkf":1, +"superlib":1, +"supfree":1, +"sxcm":1, +"sxif":1, +"sxinfo":1, +"sxny":1, +"sxri":1, +"sxsedu":1, +"sxxw":1, +"syhouse":1, +"syrcw":1, +"syzxsx":1, +"szeat":1, +"szedu":1, +"szol":1, +"szonline":1, +"szsti":1, +"szykj":1, +"t56":1, +"tabanjia":1, +"tainfo":1, +"tajd":1, +"taqc":1, +"tczx":1, +"tech110":1, +"tengfang":1, +"texrc":1, +"tianya168":1, +"tibetculture":1, +"tieliren":1, +"tiexue":1, +"tigercity":1, +"tingclass":1, +"tjwang":1, +"tlfw":1, +"tmcdn":1, +"tobosu":1, +"togogo":1, +"tongxiehui":1, +"tourjob":1, +"tqedu":1, +"tqschool":1, +"tripc":1, +"tripsz":1, +"trueland":1, +"tsinghua-sz":1, +"tt65":1, +"tuanshan":1, +"tuifu":1, +"tulaomao":1, +"tuoliji":1, +"tuoxian":1, +"tzhledu":1, +"tzinfo":1, +"u-start":1, +"u148":1, +"u520":1, +"uker":1, +"upweb":1, +"usaedu":1, +"v007":1, +"vicp":1, +"wanrendai":1, +"wanxuan":1, +"wddj":1, +"weizhang":1, +"wems":1, +"wen8":1, +"wgszq":1, +"whedu":1, +"whir":1, +"whjy":1, +"whjzw":1, +"whxedu":1, +"wjedu":1, +"woai3d":1, +"woja":1, +"worldmr":1, +"wrsa":1, +"wrzc":1, +"wto168":1, +"wuca":1, +"wufun":1, +"wxtxgj":1, +"wygf":1, +"wz16":1, +"wzdsb":1, +"wzer":1, +"wzjky":1, +"wzjygh":1, +"wzrc":1, +"xamama":1, +"xarc":1, +"xbedu":1, +"xcedu":1, +"xdkb":1, +"xfjw":1, +"xhby":1, +"xhedu":1, +"xiangyang":1, +"xiaomayi":1, +"xichu":1, +"xici":1, +"xieso":1, +"xingtai":1, +"xinxijie":1, +"xiusheji":1, +"xiziwang":1, +"xjauto":1, +"xmmh":1, +"xpjy":1, +"xpsy":1, +"xs163":1, +"xsjk":1, +"xsmp":1, +"xtjob":1, +"xtly":1, +"xue":1, +"xueche":1, +"xuefo":1, +"xunch":1, +"xuqinghua":1, +"xwcm":1, +"xxsy":1, +"xyrc":1, +"xz120":1, +"xzzp":1, +"y80s":1, +"yalj":1, +"ybrc":1, +"yc5":1, +"ycdata":1, +"yeah":1, +"ygfishing":1, +"yhjy":1, +"yjhy":1, +"ykedu":1, +"ylsw":1, +"ynedu":1, +"yodak":1, +"yongyao":1, +"youxi5":1, +"yoyone":1, +"yx618":1, +"yxjy":1, +"yxol":1, +"yyzs":1, +"yzedu":1, +"yzwb":1, +"zailine":1, +"zajy":1, +"zbedu":1, +"zbinfo":1, +"zbnews":1, +"zdic":1, +"zg163":1, +"zgcqxs":1, +"zglbw":1, +"zgnt":1, +"zgwys":1, +"zhangli":1, +"zhaokao":1, +"zhaoshang":1, +"zhecheng":1, +"zhige":1, +"zhjy":1, +"zhmmw":1, +"zhnews":1, +"zhong-yao":1, +"zhongjiao":1, +"zhongsou":1, +"zhsti":1, +"zhxww":1, +"zhyw":1, +"ziuziu":1, +"zjcyts":1, +"zjfishing":1, +"zjk169":1, +"zjkinfo":1, +"zjnw":1, +"zjtcm":1, +"zjwu":1, +"zjzj":1, +"zjzs":1, +"zk789":1, +"zljob":1, +"zoosnet":1, +"zoossoft":1, +"zprc":1, +"zq8":1, +"zsedu":1, +"zssl":1, +"ztnews":1, +"ztsfc":1, +"zy169":1, +"zyrc":1, +"zzdd":1, +"zzhmw":1, +"zzist":1, +"zzjs":1, +"zzph":1, +"zzrc":1 +},"org":{ +"0513":1, +"1000plan":1, +"1203":1, +"21gold":1, +"50bang":1, +"51honest":1, +"60851":1, +"7188":1, +"8855":1, +"8hy":1, +"8lw":1, +"96399":1, +"acftu":1, +"ailaba":1, +"ambafrance-cn":1, +"ankang06":1, +"anquan":1, +"antong":1, +"aqbz":1, +"baiquean":1, +"banyuetan":1, +"baomi":1, +"bj148":1, +"bjcdc":1, +"bjjubao":1, +"bjsdr":1, +"btv":1, +"btvu":1, +"bznews":1, +"ca-sme":1, +"caderm":1, +"caeexpo":1, +"caexpo":1, +"caiep":1, +"campingchina":1, +"canjiren":1, +"ccpit":1, +"ccpitnb":1, +"cfachina":1, +"chahua":1, +"chengdu56":1, +"china-consulate":1, +"china-cotton":1, +"china-embassy":1, +"china10":1, +"chinaasc":1, +"chinacct":1, +"chinacitywater":1, +"chinacourt":1, +"chinacses":1, +"chinaculture":1, +"chinadmoz":1, +"chinaeda":1, +"chinagwy":1, +"chinagwyw":1, +"chinaielts":1, +"chinaiiss":1, +"chinaleather":1, +"chinanotary":1, +"chinaports":1, +"chinaql":1, +"chinataiwan":1, +"chinatruck":1, +"chinaww":1, +"chinazy":1, +"chinca":1, +"chineseconsulate":1, +"chineseembassy":1, +"chunni":1, +"ci-bo":1, +"cieccpa":1, +"ciftis":1, +"cltt":1, +"cmes":1, +"cn-ny":1, +"cncma":1, +"cncourt":1, +"cnenergy":1, +"cnfxj":1, +"cngold":1, +"cnlist":1, +"cnqr":1, +"cnsb":1, +"cocos2d-x":1, +"cottonchina":1, +"cq315":1, +"cqgh":1, +"cqzx":1, +"crgk":1, +"csmes":1, +"cxwy":1, +"cxwz":1, +"cyedu":1, +"donglin":1, +"dtjy":1, +"duchang":1, +"dxyq":1, +"eccsp":1, +"eguilin":1, +"fjcyl":1, +"fjkx":1, +"flzxw":1, +"foxue":1, +"fyedu":1, +"gazx":1, +"gdql":1, +"gedu":1, +"gjgwy":1, +"globalbook":1, +"gming":1, +"gsean":1, +"guigu":1, +"gwfk":1, +"gxjubao":1, +"gzlib":1, +"gzredcross":1, +"gzzyz":1, +"hafxw":1, +"hainanredcross":1, +"hainei":1, +"hanban":1, +"hbcourt":1, +"hbsfgk":1, +"hdavec":1, +"hebgcc":1, +"hebgh":1, +"hebpingan":1, +"hebredcross":1, +"hefei123":1, +"henanredcross":1, +"hengfeng":1, +"hfib":1, +"hljgh":1, +"hlnmg":1, +"hncourt":1, +"hndpf":1, +"hngh":1, +"hnswtzb":1, +"hnszgh":1, +"hqfb":1, +"hrsalon":1, +"html5cn":1, +"htzx":1, +"humanrights-china":1, +"huocheshikebiao":1, +"hxfz":1, +"hykaoyan":1, +"hynews":1, +"hzgh":1, +"infx":1, +"it-home":1, +"jiaj":1, +"jiangshi":1, +"jiangsugqt":1, +"jiansuji":1, +"jinde":1, +"jingjia":1, +"jsfxh":1, +"jsgh":1, +"jsvolunteer":1, +"jxtyzx":1, +"keywin":1, +"kvov":1, +"liangjing":1, +"liaotuo":1, +"linjiang":1, +"lmzm":1, +"lnszgh":1, +"lnwomen":1, +"lohcn":1, +"lygredcross":1, +"meixun":1, +"mghf":1, +"mobiletrain":1, +"namoc":1, +"nanjing2014":1, +"newchannel":1, +"neworiental":1, +"neworiental-k12":1, +"neworientalgroup":1, +"newssc":1, +"ninghai":1, +"njgh":1, +"njxzzx":1, +"nuli":1, +"patachina":1, +"pinggu":1, +"puduw":1, +"punchbox":1, +"qhcl":1, +"qhfx":1, +"qinglian":1, +"qqwangming":1, +"qusu":1, +"rainbowsoft":1, +"rczp":1, +"redcross-sha":1, +"rfidchina":1, +"rtsac":1, +"sae-china":1, +"scgh":1, +"sclf":1, +"sclib":1, +"shidi":1, +"shmould":1, +"showchina":1, +"shtour":1, +"shufa":1, +"shuzixiaoyuan":1, +"shzgh":1, +"smedu":1, +"solidot":1, +"sqzx":1, +"staticfile":1, +"suiw":1, +"swchina":1, +"sxly":1, +"szfw":1, +"taiwandao":1, +"tielu":1, +"trustutn":1, +"ttedu":1, +"tzedu":1, +"visapro":1, +"w3":1, +"wanjia":1, +"wcedu":1, +"whgh":1, +"whxz":1, +"wooyun":1, +"wopus":1, +"world-culture":1, +"wwfchina":1, +"wx6":1, +"wzgh":1, +"xiaoxiaotong":1, +"xinhua":1, +"xinzhou":1, +"xjcourt":1, +"xjedu":1, +"xnfw":1, +"xuancheng":1, +"xuetian":1, +"xwjy":1, +"xzass":1, +"xzqh":1, +"yaolu":1, +"yczjda":1, +"yeeyan":1, +"ygym":1, +"yhgh":1, +"yylm":1, +"zaoyang":1, +"zgba":1, +"zgjm":1, +"zgjy":1, +"zgjzy":1, +"zgshfljjh":1, +"zhaokuaidi":1, +"zhaopinhui":1, +"zhaoyang":1, +"zhifujing":1, +"zhongzhuan":1, +"zhzyw":1, +"zj315":1, +"zjedu":1, +"zjftu":1, +"zjgqt":1, +"zjjd":1, +"zjjys":1, +"zjlsedu":1, +"zjmj":1, +"zjscedu":1, +"zjtxedu":1, +"zjzj":1, +"zkpeace":1, +"zunhua":1, +"zwbk":1, +"zx110":1, +"zycq":1, +"zyxuan":1, +"zzbm":1 +},"sg":{ +"iedu":1 +},"sh":{ +"sge":1 +},"so":{ +"haorenyuan":1, +"lietou":1, +"luyi":1, +"shengqian":1, +"soutudi":1, +"wangxiao":1 +},"tm":{ +"stone":1 +},"tv":{ +"005":1, +"0438":1, +"0916":1, +"1819":1, +"1866":1, +"1988":1, +"19888":1, +"2588":1, +"3456":1, +"5588":1, +"5666":1, +"5888":1, +"5999":1, +"7999":1, +"9555":1, +"9928":1, +"9998":1, +"bilibili":1, +"caoxian":1, +"cnnl":1, +"cnsb":1, +"dengzhou":1, +"dydh":1, +"dztv":1, +"efang":1, +"gamehome":1, +"hao315":1, +"hntv":1, +"hoolo":1, +"huaihai":1, +"imgo":1, +"jiyou":1, +"jnnews":1, +"liaozhai":1, +"ocar":1, +"pengzhou":1, +"pps":1, +"qbj":1, +"shiqian":1, +"wasu":1, +"weihai":1, +"zhenping":1, +"zohi":1 +},"tw":{ +"hexun.com":1, +"taiwandao":1 +},"us":{ +"pangu":1 +},"ws":{ +"0798":1 +},"xn--fiqs8s":{ +"":1 +} +}; +var black_domains = ["||\x64arp\x61.m\x69l" +,"||\x66xnetwo\x72k\x73.com" +,"hu\x6c\x75.com" +,"hu\x6cui\x6d.com" +,"||\x6duz\x75.tv" +,"||\x6eetfli\x78\56com" +,"||\x70andor\x61\56com" +,".p\x61ndor\x61.\x74v" +,"||\x70ure1\70.\x63om" +,"||\x73potif\x79\56com" +,"||\x74arge\x74.\x63om" +,"||\x74urntab\x6c\x65.fm" +,"||\x76ev\x6f.com" +,"||\x7aatto\x6f.\x63om" +,"||\x71\x71.c\x6f.za" +,"||\x7aozotow\x6e.com" +,".0\x72\x7a.tw" +,"|h\x74tp://0\x72\x7a.tw" +,"||\60to25\65.\x63om" +,"1-\x61ppl\x65.c\x6f\x6d.tw" +,"||\61-appl\x65\56co\x6d.tw" +,"||\610musum\x65.com" +,"12\63r\x66.com" +,".1\62be\x74.com" +,"||\612be\x74.c\x6fm" +,".1\62vp\x6e.com" +,"||\612vp\x6e.c\x6fm" +,"14\61hongko\x6e\x67.com/\x66orum" +,".1\673n\x67.com" +,"||\6173n\x67.c\x6fm" +,"19\704bb\x73.c\x6fm" +,"||\61984bb\x73\56com" +,".1\7184bb\x73.\x6frg" +,"||\61984bb\x73\56org" +,".1\x62a\x6f.org" +,"|h\x74tp://1\x62a\x6f.org" +,"||\61pond\x6f.\x74v" +,".1\x65e\x77.com" +,".2\55han\x64.i\x6efo" +,".2\6000fu\x6e.\x63om/bbs" +,".2\6008xian\x7ahan\x67.i\x6efo" +,"||\62008xia\x6ezhan\x67.\x69nfo" +,"21\x61nd\x79.co\x6d/blog" +,"24\x73mil\x65.o\x72g" +,".2\x73hare\x64.\x63om" +,".3\615l\x7a.com" +,"||\636rai\x6e.\x63om" +,"||\64bluest\x6fne\x73.biz" +,"||\64cha\x6e.o\x72g" +,"5i\60\61.com" +,"ta\x69wannat\x69o\x6e.50w\x65b\x73.com" +,"||\65\61.ca" +,".5\x6daodan\x67\56com" +,"||\66-\64.net/" +,"64\x6demo" +,"64\x74ianwan\x67.com" +,"64\x77ik\x69.com" +,"66\66k\x62.com" +,"6p\x61r\x6b.com" +,"||\66par\x6b.c\x6fm" +,"||\x77w\x77.6v6\x64ot\x61.com" +,"||\67captur\x65.com" +,".8\70190\63.c\x6fm/page\57zh-tw/" +,".8\70\70.com" +,"||\708190\63.\x63om" +,"89\556\64.org" +,"||\709-6\64.o\x72g" +,".9\600170\60.\x63om" +,"|h\x74tp://9\608taiwa\x6e.org/" +,".9\62cca\x76.c\x6fm" +,"||\71bi\x73.com" +,"||\71bi\x73.net" +,"||\x61-norma\x6c-da\x79.c\x6fm" +,"a\65\56co\x6d.ru" +,".a\x62oluowa\x6e\x67.com" +,"||\x61boluow\x61n\x67.com" +,".a\x62outgf\x77\56com" +,"||\x61cgk\x6a.c\x6fm" +,"ac\x74ime\x73.c\x6f\x6d.au" +,"||\x61cul\x6f.us" +,"||\x61ddicte\x64tocoff\x65\x65.de" +,"ad\x75ltfrie\x6edfinde\x72.com" +,"ad\x75ltkee\x70\56net/pe\x65pshow/\x6dembers\57mai\x6e.h\x74m" +,"||\x61dvansc\x65n\x65.com" +,"||\x61dvertf\x61\x6e.com" +,"||\x61enhanc\x65r\x73.com" +,"||\x61\x66.mil" +,"ai\x70\x68.net" +,"||\x61ip\x68.net" +,".a\x69se\x78.com" +,"||\x61i\x74.or\x67\56tw" +,".a\x69weiwei\x62lo\x67.com" +,"||\x61iweiwe\x69blo\x67.c\x6fm" +,"||\x61jaxplo\x72e\x72.info" +,"||\x77w\x77.ajs\x61nd\x73.com" +,"||\x61kiba-o\x6elin\x65.c\x6fm" +,"||\x61l-qimm\x61\x68.net" +,"||\x61labou\x74\56com" +,"||\x61lasbar\x72icada\x73\56org" +,"||\x6eote\x73.a\x6cexdon\x67\56com" +,"al\x65xlu\x72.o\x72g" +,"al\x69eng\x75.c\x6fm" +,"||\x61lkasi\x72\56com" +,"al\x6cgirlsa\x6clowe\x64.\x6frg" +,"al\x6cianc\x65.\x6fr\x67.hk" +,".a\x6clinf\x61.\x63om" +,"|h\x74tp://a\x6clinf\x61.\x63om" +,"||\x61llinf\x6f\56com" +,"||\x61llmovi\x65.com" +,"||\x61lterna\x74e-tool\x73.com" +,"al\x76inalex\x61nde\x72.c\x6fm" +,"al\x77aysdat\x61.com" +,"||\x61lwaysd\x61t\x61.com" +,"||\x61lwaysd\x61t\x61.net" +,"||\x61m73\60.c\x6f\x6d.hk" +,"am\x61zo\x6e.co\x6d/Priso\x6eer-Sta\x74e-Secr\x65t-Jour\x6eal-Pre\x6dier" +,"am\x65bl\x6f.jp" +,"||\x61mebl\x6f.\x6ap" +,"||\x61merica\x6egreenc\x61r\x64.com" +,"||\x61mibloc\x6bedorno\x74.com" +,".a\x6dnest\x79.\x6frg" +,"||\x61mnest\x79\56org" +,".a\x6dnestyu\x73\x61.org" +,"||\x61mnesty\x75s\x61.org" +,".a\x6doiis\x74.\x63om" +,"am\x7a\x73.me" +,"an\x61lyze-\x76\56com" +,"||\x61nchorf\x72e\x65.com" +,"||\x61ndfara\x77a\x79.net" +,"an\x69mecraz\x79.net" +,"||\x61nobi\x69.\x63om" +,".a\x6eonymiz\x65\x72.com" +,"an\x6fntex\x74.\x63om" +,"||\x61nthony\x63alzadi\x6cl\x61.com" +,".a\x6etiwav\x65\56net" +,"|h\x74tp://a\x6etiwav\x65\56net" +,"||\x61ob\x6f.co\x6d.au" +,"||\x61olchan\x6eel\x73.ao\x6c.com" +,"vi\x64e\x6f.ao\x6c\56ca/vid\x65o-deta\x69l" +,"vi\x64e\x6f.ao\x6c\56c\x6f.uk/\x76ideo-d\x65tail" +,"vi\x64e\x6f.ao\x6c\56com" +,"||\x76ide\x6f.a\x6f\x6c.com" +,"ww\x77.aolne\x77\x73.com" +,"vi\x64e\x6f.a\x70.\x6frg" +,".a\x70etub\x65.\x63om" +,"||\x61piar\x79.\x69o" +,".a\x70ige\x65.c\x6fm" +,"||\x61pige\x65.\x63om" +,"ap\x70ledail\x79.com" +,"ar\x63hiv\x65.is" +,"||\x61rchiv\x65\56org" +,".a\x72ctosi\x61\56com" +,"|h\x74tp://a\x72ctosi\x61\56com" +,"||\x61reca-b\x61cku\x70.o\x72g" +,"||\x61rm\x79.mil" +,"ar\x74s\x79.net" +,".a\x73ahichi\x6ees\x65.com" +,"||\x61sahich\x69nes\x65.c\x6fm" +,"as\x64f\x67.jp/\x64abr" +,".a\x73iaharv\x65s\x74.org" +,"||\x61siahar\x76es\x74.org" +,"as\x69anew\x73.\x69t" +,"||\x61sianwo\x6densfil\x6d.de" +,"||\x61skstud\x65n\x74.com" +,".a\x73kyn\x7a.n\x65t" +,"||\x61skyn\x7a.\x6eet" +,"||\x61ssembl\x61.com" +,"||\x61stonma\x72tinnew\x73.com" +,".a\x74chines\x65.com" +,"|h\x74tp://a\x74chines\x65.com" +,"at\x67f\x77.org" +,"||\x61t\x6a.or\x67\56tw" +,".a\x74laspos\x74.com" +,"||\x61tlaspo\x73\x74.com" +,".a\x74nex\x74.c\x6fm" +,"||\x61tnex\x74.\x63om" +,"av\x61a\x7a.org" +,"||\x61vaa\x7a.o\x72g" +,"av\x64\x62.in" +,"||\x61videmu\x78.org" +,"||\x61voisio\x6e.com" +,"||\x61xurefo\x72ma\x63.com" +,"fo\x72u\x6d.bab\x79-kingd\x6f\x6d.com" +,"ba\x62yne\x74.c\x6f\x6d.hk" +,"ba\x63kchin\x61\56com" +,"||\x62ackchi\x6e\x61.com" +,".b\x61ckpack\x65r\x73.co\x6d\56tw/for\x75m" +,"ba\x64o\x6f.com" +,"||\x62aid\x75.jp" +,"||\x62aixin\x67\56me" +,"he\x6e.ba\x6f.li" +,"ba\x6enedboo\x6b.org" +,"||\x62annedb\x6fo\x6b.org" +,"||\x62arnab\x75\56c\x6f.uk" +,"ba\x79voic\x65.\x6eet" +,"||\x62ayvoic\x65.net" +,"da\x6aush\x61.b\x61yword\x73\56com" +,".b\x62\x63.c\x6f.u\x6b*chine\x73e" +,".b\x62\x63.c\x6f.u\x6b/tv" +,".b\x62\x63.c\x6f.u\x6b*zhong\x77en" +,"ne\x77\x73.bb\x63.\x63\x6f.uk/o\x6ethisda\x79*newsi\x64_24960\600/2496\6277" +,"ne\x77sforum\x73.bb\x63.c\x6f.uk" +,".b\x62cchine\x73\x65.com" +,"||\x62bcchin\x65s\x65.com" +,"|h\x74tp://b\x62\x63.in" +,".b\x62\x67.gov" +,"||\x62bsfee\x64\56com" +,"bb\x73lan\x64.c\x6fm" +,".b\x63\x63.co\x6d.\x74w/board" +,".b\x63chines\x65.net" +,".b\x65b\x6f.com" +,"||\x62eb\x6f.com" +,"||\x62eijing\6198\71.com" +,"be\x69jingsp\x72in\x67.com" +,"||\x62eijing\x73prin\x67.\x63om" +,".b\x65rlintw\x69tterwa\x6c\x6c.com" +,"||\x62erlint\x77itterw\x61l\x6c.com" +,".b\x65stforc\x68in\x61.org" +,"||\x62estfor\x63hin\x61.o\x72g" +,".b\x65stvpns\x65rvic\x65.\x63om" +,"||\x62et36\65.\x63om" +,".b\x65tfai\x72.\x63om" +,".b\x65ttwee\x6e\56com" +,"||\x62ettwee\x6e.com" +,".b\x65ww\x77.net" +,"||\x62fn\x6e.org" +,"||\x62fs\x68.hk/" +,"bi\x61ntaila\x6aia\x6f.com" +,"bi\x67fool\x73.\x63om" +,".b\x69gnew\x73.\x6frg" +,"||\x62ignew\x73\56org" +,".b\x69gsoun\x64\56org/po\x72tnoy" +,"||\x62ill2-s\x6fftwar\x65\56com" +,"bi\x6clypa\x6e.\x63om/wik\x69/%E9%A\66%96%E9\45A0%81" +,"||\x62illyw\x72\56com" +,"bi\x70i\x63.net" +,".b\x69\x74.ly" +,"|h\x74tp://b\x69\x74.ly" +,".b\x69tshar\x65\56com/fi\x6ces" +,"ht\x74p://bi\x74shar\x65.\x63om/fil\x65s" +,"bj\x7a\x63.org" +,"||\x62jz\x63.or\x67/" +,"to\x72.bling\x62lingsq\x75a\x64.net" +,".b\x6cink\x78.c\x6fm" +,"||\x62link\x78.\x63om" +,"bl\x69n\x77.com" +,".b\x6ci\x70.tv" +,"||\x62li\x70.tv/" +,".b\x6cockc\x6e.\x63om" +,"||\x62lockc\x6e\56com" +,"||\x62lo\x67.de" +,".b\x6cogcata\x6co\x67.com" +,"||\x62logcat\x61lo\x67.com" +,".b\x6cogge\x72.\x63om" +,"||\x62logge\x72\56com" +,"bl\x6fgim\x67.jp" +,"||\x62lo\x67.ka\x6egy\x65.org" +,".b\x6cogline\x73.com" +,"||\x62loglin\x65\x73.com" +,".b\x6coglovi\x6e.com" +,"rc\x6fnversa\x74io\x6e.bl\x6fg\x73.com" +,"bl\x6fgspo\x74.\x63\x6f.uk" +,"bl\x6fgspo\x74.\x63om" +,"bl\x6fgspo\x74.\x64e" +,"bl\x6fgspo\x74.\x66r" +,"||\x62logspo\x74.hk" +,"bl\x6fgspo\x74.\x69n" +,"bl\x6fgspo\x74.\x6ap" +,"bl\x6fgt\x64.net" +,".b\x6cogt\x64.o\x72g" +,"|h\x74tp://b\x6cogt\x64.o\x72g" +,"||\x62loodsh\x65\x64.net" +,".b\x6coomber\x67.cn" +,"||\x62loombe\x72\x67.cn" +,".b\x6coomber\x67.com" +,"||\x62loombe\x72\x67.com" +,"bl\x6fomber\x67\56de" +,"||\x62loombe\x72\x67.de" +,"||\x62loomfo\x72tun\x65.c\x6fm" +,"||\x62nrmeta\x6c.com" +,"bo\x61rdread\x65\x72.com/\x74hread" +,"||\x62oardre\x61de\x72.com" +,"bo\x6ebonm\x65.\x63om" +,"||\x62ook\x73.c\x6f\x6d.tw" +,".b\x6ftanwan\x67.com" +,".b\x6f\x74.nu" +,".b\x6fwenpre\x73\x73.com" +,"||\x62owenpr\x65s\x73.com" +,"d\x6c\56bo\x78.net" +,"||\x64\x6c.bo\x78.\x6eet" +,"bo\x78u\x6e.com" +,"||\x62oxu\x6e.c\x6fm" +,".b\x6fxu\x6e.tv" +,"||\x62oxu\x6e.tv" +,"bo\x78unblo\x67\56com" +,"||\x62oxunbl\x6f\x67.com" +,"||\x62\x72.st" +,"||\x62randon\x68utchin\x73o\x6e.com" +,"||\x62raumei\x73te\x72.org" +,".b\x72ea\x6b.com" +,"||\x62rea\x6b.c\x6fm" +,".b\x72eaking\x74weet\x73.\x63om" +,"||\x62reakin\x67tweet\x73\56com" +,".b\x72iefdre\x61\x6d.com/\45E7%B4%\x410%E6%A\63%BA" +,"br\x69ghtkit\x65.com" +,"||\x62rightk\x69t\x65.com" +,"br\x69zzl\x79.c\x6fm" +,"||\x62rizzl\x79\56com" +,"br\x6fadboo\x6b\56com" +,"ib\x72o\x73.org" +,"br\x75cewan\x67\56net" +,"||\x62t9\65.com" +,".b\x75daed\x75.\x6frg" +,"||\x62udaed\x75\56org" +,".b\x75llo\x67.o\x72g" +,"||\x62ullo\x67.\x6frg" +,".b\x75llogge\x72.com" +,"||\x62ullogg\x65\x72.com" +,".b\x75siness\x77ee\x6b.com" +,".b\x75siness\x74ime\x73.c\x6f\x6d.cn" +,"|h\x74tp://b\x75siness\x74ime\x73.c\x6f\x6d.cn" +,"||\x62ugclu\x62\56org" +,"||\x62uuga\x61.\x63om" +,"bu\x7azur\x6c.jp" +,"bw\x73\x6a.hk" +,"ho\x6c\x7a.byet\x68ost\70.c\x6fm" +,".c\55spanvi\x64e\x6f.org" +,"||\x63-spanv\x69de\x6f.org" +,"||\x63-est-s\x69mpl\x65.c\x6fm" +,".c\x61ctusvp\x6e.com" +,"||\x63actusv\x70\x6e.com" +,".c\x61fepres\x73.com" +,".c\x61lame\x6f.\x63om/boo\x6bs" +,"c\x6e\56calame\x6f.com" +,"|h\x74tp://c\x6e.calam\x65\x6f.com" +,"||\x63am\x73.com" +,"ca\x6eadamee\x74.com" +,"|h\x74tp://b\x62\x73.cant\x6fnes\x65.a\x73ia/" +,"ht\x74p://ww\x77.canto\x6ees\x65.as\x69a/acti\x6fn-bb\x73.\x68tml" +,".c\x61ny\x75.org" +,".c\x61\x6f.im" +,".c\x61obia\x6e.\x69nfo" +,"||\x63aobia\x6e\56info" +,"|h\x74tp://c\x61tcatbo\x78.com/f\x6fru\x6d.ph\x70*" +,"ca\x6fchangq\x69n\x67.com" +,"||\x63aochan\x67qin\x67.c\x6fm" +,"ca\x72\x69.co\x6d.\x6dy" +,"||\x63atch2\62\56net" +,"||\x63atfigh\x74payper\x76ie\x77.xxx" +,".c\x61tholi\x63\56or\x67.hk" +,"||\x63atholi\x63.or\x67.hk" +,"ca\x74holi\x63.\x6fr\x67.tw" +,"||\x63atholi\x63.or\x67.tw" +,".c\x62snew\x73.\x63om/vid\x65o" +,"||\x63cavtop\61\60.com" +,".c\x63dt\x72.org" +,"||\x63cdt\x72.o\x72g" +,"cc\x6cif\x65.org" +,".c\x63ther\x65.\x63om" +,".c\x63tongba\x6f.com/a\x72ticle/\62078732" +,"cc\x75\x65.ca" +,"cc\x75\x65.com" +,"||\x63di\x67.in\x66o" +,"cd\x6a\x70.org" +,"||\x63dj\x70.or\x67/" +,".c\x64new\x73.c\x6f\x6d.tw" +,"cd\x70199\70.o\x72g" +,"||\x63dp199\70\56org" +,"cd\x70200\66.o\x72g" +,"||\x63dp200\66\56org" +,"cd\x70us\x61.org" +,"cd\x70we\x62.org" +,"||\x63dpwe\x62.\x6frg" +,"cd\x70w\x75.org" +,"||\x63dpw\x75.o\x72g" +,".c\x65c\x63.gov" +,"||\x63ec\x63.gov" +,"||\x63ellul\x6f\56info" +,"||\x63enc\x69.tk" +,"||\x63enew\x73.\x65u" +,"||\x63entral\x6eatio\x6e.\x63om" +,".c\x65ntury\x73\56net" +,"|h\x74tp://c\x65ntury\x73\56net" +,"||\x63hando\x6f\56org" +,".c\x68ang\x65.o\x72g" +,"|h\x74tp://c\x68ang\x65.o\x72g" +,".c\x68ang\x70.c\x6fm" +,"||\x63hang\x70.\x63om" +,".c\x68apm2\65.\x63om" +,".c\x68aturba\x74\x65.com" +,"ch\x65ngming\x6da\x67.com" +,".c\x68enguan\x67chen\x67.\x63om" +,"||\x63hengua\x6egchen\x67\56com" +,"ch\x65npokon\x67.com" +,"||\x63herrys\x61v\x65.com" +,".c\x68ina-we\x65\x6b.com" +,"ch\x69na10\61.\x63om" +,"||\x63hina10\61.com" +,"||\x63hina2\61\56com" +,"ch\x69na2\61.o\x72g" +,"||\x63hina2\61\56org" +,"ch\x69naaffa\x69r\x73.org" +,"||\x63hinaaf\x66air\x73.o\x72g" +,"||\x63hinaai\x64.me" +,"ch\x69naai\x64.\x75s" +,"ch\x69naai\x64.\x6frg" +,"ch\x69naai\x64.\x6eet" +,"ch\x69nacomm\x65nt\x73.org" +,"||\x63hinaco\x6dment\x73.\x6frg" +,"ch\x69nachan\x6ee\x6c.hk" +,"||\x63hinach\x61nne\x6c.hk" +,".c\x68inadig\x69taltim\x65\x73.net" +,"|h\x74tp://c\x68inadig\x69taltim\x65\x73.net" +,".c\x68inaewe\x65kl\x79.com" +,"||\x63hinaew\x65ekl\x79.c\x6fm" +,"||\x63hinafr\x65epres\x73\56org" +,"ch\x69nageek\x73.org" +,"ch\x69nagf\x77.\x6frg" +,"||\x63hinagf\x77.org" +,".c\x68inagre\x65npart\x79\56org" +,"||\x63hinagr\x65enpart\x79.org" +,".c\x68inahus\x68.com" +,"ch\x69nalawt\x72anslat\x65.com" +,"ch\x69naxchi\x6e\x61.com/\x68owto" +,".c\x68inainp\x65rspect\x69v\x65.com" +,"ch\x69nainpe\x72specti\x76\x65.net/\x41rtSho\x77\56aspx?" +,"||\x63hinain\x70erspec\x74iv\x65.net" +,".c\x68inainp\x65rspect\x69v\x65.org" +,"||\x63hinain\x70erspec\x74iv\x65.org" +,"||\x63hinain\x74erimgo\x76.org" +,"ch\x69nalawa\x6edpolic\x79.com" +,".c\x68inamul\x65.com" +,"||\x63hinamu\x6c\x65.com" +,"ch\x69nam\x7a.o\x72g" +,".c\x68inarig\x68tsi\x61.o\x72g" +,"ch\x69nasoci\x61ldemoc\x72aticpa\x72t\x79.com" +,"||\x63hinaso\x63ialdem\x6fcratic\x70art\x79.c\x6fm" +,"ch\x69nasou\x6c\56org" +,"||\x63hinaso\x75\x6c.org" +,"ch\x69natime\x73.com" +,"ch\x69natwee\x70\x73.com" +,"ch\x69nawa\x79.\x6frg" +,".c\x68inawor\x6be\x72.info" +,"||\x63hinawo\x72ke\x72.in\x66o" +,"ch\x69nayout\x68.or\x67.hk" +,"ch\x69nayuan\x6di\x6e.org" +,"||\x63hinayu\x61nmi\x6e.o\x72g" +,".c\x68inese-\x68ermi\x74.\x6eet" +,"ch\x69nese-m\x65moria\x6c\56org" +,"||\x63hinese\x64ailyne\x77\x73.com" +,".c\x68inese\x6e\56de" +,"||\x63hinese\x6e.de" +,"ch\x69nesene\x77sne\x74.c\x6fm" +,".c\x68inesep\x65\x6e.org" +,".c\x68ineset\x61lk\x73.ne\x74/ch" +,".c\x68ingche\x6fn\x67.com" +,"||\x63hingch\x65on\x67.com" +,"ch\x6e.chosu\x6e.com" +,"ch\x72istian\x73tud\x79.c\x6fm" +,"||\x63hristi\x61nstud\x79\56com" +,"ch\x72istusr\x65\x78.org/\x77ww1/sdc" +,"||\x63hrlcg-\x68\x6b.org" +,"||\x63hromea\x64bloc\x6b.\x63om" +,"ch\x75bu\x6e.com" +,"ch\x75iz\x69.net" +,"||\x63huiz\x69.\x6eet" +,"||\x63hrispe\x64eric\x6b.\x63om" +,"||\x63hrispe\x64eric\x6b.\x6eet" +,".c\x68rlawye\x72\x73.hk" +,"||\x61llabou\x74alph\x61.\x63om" +,".c\x69tizenl\x61\x62.org" +,"ci\x74izensr\x61di\x6f.org" +,"ci\x74y9\x78.com" +,".c\x69vicpar\x74\x79.hk" +,"||\x63ivicpa\x72t\x79.hk" +,"ci\x76ilhrfr\x6fn\x74.org" +,"||\x63ivilhr\x66ron\x74.o\x72g" +,"ps\x69pho\x6e.c\x69vise\x63.\x6frg" +,"||\x63j\x62.net" +,".c\x6b10\61.com" +,"||\x63k10\61.c\x6fm" +,"||\x63lassic\x61lguita\x72blo\x67.n\x65t" +,".c\x6c\x62.or\x67.\x68k" +,".c\x6cipfis\x68\56de" +,".c\x6dul\x65.com" +,"||\x63mul\x65.c\x6fm" +,"||\x63m\x73.gov" +,"||\x63n\x61.co\x6d\56tw" +,".c\x6eavist\x61\56co\x6d.tw\57shop/s\x74ores_a\x70p" +,".c\x6e\x64.org" +,"||\x63n\x64.org/" +,"wi\x6b\x69.cnit\x74e\x72.com" +,".c\x6e\x6e.com/\x76ideo" +,"ne\x77\x73.cnye\x73.com" +,"||\x63ochin\x61\56org" +,".c\x6fde198\64\56com/64" +,"||\x63odesha\x72\x65.io" +,"|h\x74tp://t\x6fs\x68.com\x65dycent\x72a\x6c.com" +,"co\x6defromc\x68in\x61.com" +,"||\x63omefro\x6dchin\x61.\x63om" +,"||\x63ompile\x68ear\x74.c\x6fm" +,"||\x63onoy\x6f.\x63om" +,".c\x6folale\x72\56com" +,"||\x63oolale\x72.com" +,"co\x6flde\x72.c\x6fm" +,"||\x63oolde\x72\56com" +,"||\x63oollou\x64.or\x67.tw" +,"co\x72umcoll\x65g\x65.com" +,"||\x63ouchdb\x77ik\x69.com" +,"||\x63otwee\x74\56com" +,"cp\x6a.org" +,"||\x63p\x6a.org/" +,"cr\x61ckl\x65.c\x6fm" +,"||\x63rackl\x65\56com" +,"cr\x64-ne\x74.o\x72g" +,"cr\x65ader\x73.\x6eet" +,"||\x63reader\x73.net" +,".c\x72ossthe\x77al\x6c.net" +,"||\x63rossth\x65wal\x6c.n\x65t" +,"cs\x64part\x79.\x63om" +,"||\x63sdpart\x79.com" +,"||\x63suche\x6e\56de" +,"ct\x73.co\x6d.tw" +,".c\x75hkac\x73.\x6frg/~be\x6eng" +,".c\x75ihu\x61.o\x72g" +,"||\x63uihu\x61.\x6frg" +,".c\x75iweipi\x6e\x67.net" +,"||\x63uiweip\x69n\x67.net" +,"||\x63urvefi\x73\x68.com" +,".c\x75ltur\x65.\x74w" +,"||\x63ultur\x65\56tw" +,"fo\x72u\x6d.cyb\x65rct\x6d.c\x6fm/forum" +,"||\x63ybergh\x6fstvp\x6e.\x63om" +,"||\x63ynscri\x62\x65.com" +,"cy\x74od\x65.us" +,"||\x69fa\x6e.c\x7a\56cc" +,"||\x6dik\x65.c\x7a\56cc" +,"||\x6ei\x63.c\x7a.\x63c" +,"c\x6c\56d0\x7a.net" +,".d\x61b\x72.c\x6f.\x75k" +,"||\x64ab\x72.c\x6f\56uk" +,"da\x62\x72.mobi" +,"||\x64ab\x72.mo\x62i" +,"||\x64ab\x72.me" +,"da\x64azi\x6d.c\x6fm" +,"||\x64adazi\x6d\56com" +,".d\x61di36\60.\x63om" +,"da\x66agoo\x64.\x63om" +,"da\x66aha\x6f.c\x6fm" +,"||\x64alaila\x6d\x61.ru" +,".d\x61ilidai\x6c\x69.com" +,"||\x64ailida\x69l\x69.com" +,".d\x61ilymot\x69o\x6e.com" +,".d\x61jiyua\x6e\56com" +,".d\x61jiyua\x6e\56eu" +,"da\x6cailam\x61\56com" +,".d\x61lailam\x61worl\x64.\x63om" +,"||\x64alaila\x6daworl\x64\56com" +,"da\x6cianmen\x67.org" +,"||\x64alianm\x65n\x67.org" +,".d\x61nke4ch\x69n\x61.net" +,"||\x64anke4c\x68in\x61.net" +,".d\x61nwe\x69.o\x72g" +,".d\x61ola\x6e.n\x65t" +,"da\x78\x61.cn" +,"||\x64ax\x61.cn/" +,"c\x6e\56dayabo\x6f\x6b.com" +,".d\x61ylif\x65.\x63om/top\x69c/dala\x69_lama" +,".d\x64\x63.co\x6d.\x74w" +,"||\x64e-sc\x69.\x6frg" +,".d\x65-sc\x69.o\x72g" +,"li\x73t\x73.deb\x69a\x6e.org" +,"pa\x63kage\x73.\x64ebia\x6e.\x6frg/zh-\x63n/lenn\x79/gpass" +,"||\x64elcam\x70\56net" +,"de\x6ciciou\x73\56com/GF\x57bookma\x72k" +,".d\x65mocrat\x73.org" +,"||\x64emocra\x74\x73.org" +,"||\x64es\x63.se/" +,"||\x64eutsch\x65-well\x65\56de" +,"||\x64ev10\62.\x63om" +,"||\x64evi\x6f.us" +,"|h\x74tp://w\x77\x77.dfan\x6ein\x67.com" +,"||\x64fa\x73.mil" +,".d\x69aoyuis\x6cand\x73.o\x72g" +,"||\x64iaoyui\x73land\x73.\x6frg" +,"||\x64igital\x6eomadsp\x72ojec\x74.\x6frg" +,".d\x69ig\x6f.com" +,"||\x64iig\x6f.c\x6fm" +,"||\x66ur\x6c.net" +,"||\x64irectc\x72eativ\x65\56com" +,".d\x69scus\x73.\x63o\x6d.hk" +,"||\x64iscus\x73\56co\x6d.hk" +,"di\x73\x70.cc" +,".d\x69t-in\x63.\x75s" +,"||\x64it-in\x63\56us" +,".d\x69zhidiz\x68\x69.com" +,"dj\x61ngosni\x70pet\x73.o\x72g" +,"||\x64l-lab\x79\56jp" +,"||\x64lsit\x65.\x63om" +,"||\x64nscryp\x74.org" +,".d\x6fji\x6e.com" +,".d\x6fk-foru\x6d.net" +,"||\x64ol\x63.de" +,"||\x64oll\x66.c\x6fm" +,".d\x6fmai\x6e.c\x6cu\x62.tw" +,"do\x6egd\x65.com" +,"do\x6egtaiwa\x6e\x67.com" +,"||\x64ongtai\x77an\x67.com" +,".d\x6fngtaiw\x61n\x67.net" +,"||\x64ongtai\x77an\x67.net" +,".d\x6fngyang\x6ain\x67.com" +,".d\x6fntfilt\x65\x72.us" +,"||\x64ontmov\x65tochin\x61.com" +,".d\x6ftplan\x65\56com" +,"||\x64otplan\x65.com" +,"||\x64otsu\x62.\x63om" +,"do\x75blea\x66.\x63om" +,"||\x64ougscr\x69pt\x73.com" +,"do\x77e\x69.org" +,"||\x64oxyge\x6e\56org" +,"dp\x68\x6b.org" +,"dp\x70.or\x67.tw" +,"||\x64p\x70.or\x67\56tw" +,"||\x77eigege\x62y\x63.dre\x61mhoste\x72\x73.com" +,"||\x64rga\x6e.n\x65t" +,"||\x64ropbo\x78\56com" +,"||\x64ropbox\x75sercon\x74en\x74.com" +,".d\x72tube\x72.\x63om" +,"||\x64tiblo\x67\56com" +,"||\x64ti\x63.mil" +,"dt\x69serv\62.\x63om" +,"||\x64uckduc\x6bg\x6f.com" +,".d\x75ckloa\x64\56com/do\x77nload" +,"||\x64uckmyl\x69f\x65.com" +,".d\x75ihu\x61.o\x72g" +,"||\x64uihu\x61.\x6frg" +,".d\x75oweiti\x6de\x73.com" +,"||\x64uoweit\x69me\x73.com" +,"du\x70in\x67.net" +,"||\x64uplica\x74\x69.com" +,"du\x70ol\x61.com" +,"du\x70ol\x61.net" +,"||\x64vora\x6b.\x6frg" +,".d\x77.de" +,"|h\x74tp://d\x77.de" +,".d\x77-worl\x64\56com" +,"||\x64w-worl\x64.com" +,".d\x77-worl\x64\56de" +,"ht\x74p://dw\55worl\x64.\x64e" +,"ww\x77.dwhee\x6ce\x72.com" +,"dw\x6eew\x73.com" +,"||\x64wnew\x73.\x63om" +,"xy\x73.dxion\x67.com" +,"dy\624\x6b.info" +,"||\x64ynaweb\x69n\x63.com" +,".d\x79ndn\x73.o\x72g" +,".d\x7az\x65.com" +,"||\x65-gol\x64.\x63om" +,".e\55gol\x64.c\x6fm" +,"|h\x74tp://\x67\56e-hent\x61\x69.org/" +,"e-\x69nf\x6f.or\x67.tw" +,".e\55trader\x6can\x64.ne\x74/board" +,"hk\x6a\x70.easy\x77e\x62.hk" +,"eb\x6fokbrow\x73\x65.com" +,"eb\x6foke\x65.c\x6fm" +,"ec\x6dinistr\x79.net" +,"bb\x73.ecsta\x72\x74.com" +,"ed\x69cypage\x73.com" +,"ed\x6for\x73.com" +,".e\x64ubridg\x65.com" +,"||\x65dubrid\x67\x65.com" +,"||\x65evp\x6e.c\x6fm" +,"ef\x63\x63.or\x67.\x68k" +,"||\x65ic-a\x76.\x63om" +,"el\x65ctions\x6dete\x72.c\x6fm" +,".e\x6ctondis\x6ee\x79.com" +,"||\x65macsbl\x6f\x67.org" +,".e\x6dor\x79.edu" +,".e\x6dule-ed\62\x6b.com" +,"|h\x74tp://e\x6dule-ed\62\x6b.com" +,"ch\x69nes\x65.e\x6egadge\x74\56com" +,"||\x65nglish\x66romeng\x6can\x64.c\x6f\56uk" +,"||\x65nterma\x70.com" +,"ep\x6fchtime\x73-b\x67.com" +,"||\x65pochti\x6des-b\x67.\x63om" +,"ep\x6fchtime\x73-roman\x69\x61.com" +,"||\x65pochti\x6des-rom\x61ni\x61.com" +,"ep\x6fchtime\x73.c\x6f.il" +,"||\x65pochti\x6de\x73.c\x6f.\x69l" +,"ep\x6fchtime\x73.c\x6f.kr" +,"||\x65pochti\x6de\x73.c\x6f.\x6br" +,"ep\x6fchtime\x73.com" +,"||\x65pochti\x6de\x73.com" +,"ep\x6fchtime\x73.de" +,"ep\x6fchtime\x73.fr" +,".e\x70ochtim\x65\x73.ie" +,"ep\x6fchtime\x73.jp" +,"ep\x6fchtime\x73.ru" +,"ep\x6fchtime\x73.se" +,"ep\x6fchtime\x73t\x72.com" +,"||\x65pochwe\x65kl\x79.com" +,"er\x61bar\x75.n\x65t" +,".e\x72epubli\x6b.com" +,"||\x65rnestm\x61nde\x6c.o\x72g" +,"||\x65right\x73\56net" +,"et\x61iwanne\x77\x73.com" +,"||\x65tize\x72.\x6frg" +,"ww\x77.eula\x6d\56com" +,"ev\x65ntfu\x6c.\x63om" +,"||\x65xblo\x67.\x6ap" +,"||\x62lo\x67.ex\x62lo\x67.c\x6f\56jp" +,"@@||www.exblog.jp" +,"||\x65xpatsh\x69el\x64.com" +,"||\x65xpload\x65\x72.net" +,".e\x78tremet\x75b\x65.com" +,"ey\x65vi\x6f.jp" +,"||\x65yevi\x6f.\x6ap" +,".e\x7ap\x63.tk/\x63ategor\x79/soft" +,".e\x7apee\x72.c\x6fm" +,".f\x61ceboo\x6b\56com" +,"||\x66aceboo\x6b.com" +,"/^\x68ttps?:\x5c/\/[^\\57]+face\x62ook\x5c.c\x6fm/" +,"@@||*v6.facebook.com" +,"||\x63onnec\x74\56facebo\x6f\x6b.net" +,"||\x66acesof\x6eyf\x77.com" +,"||\x66aithth\x65do\x67.in\x66o" +,".f\x61kk\x75.net" +,"fa\x6cunar\x74.\x6frg" +,"fa\x6cundaf\x61\56org" +,"fa\x6cundafa\x6duseu\x6d.\x6frg" +,"||\x66alunh\x72\56org" +,"||\x66angliz\x68\x69.info" +,"||\x66angon\x67\56org" +,"fa\x6egonghe\x69k\x65.com" +,"fa\x6eqiangh\x6f\x75.com" +,"fa\x70d\x75.com" +,".f\x61wanghu\x69hu\x69.org" +,"||\x66bcd\x6e.n\x65t" +,"fa\x6eqiangy\x61kex\x69.n\x65t" +,"fa\x69\x6c.hk" +,"||\x66amunio\x6e.com" +,".f\x61n-qian\x67.com" +,".f\x61ngbinx\x69n\x67.com" +,"||\x66angbin\x78in\x67.com" +,"fa\x6egemin\x67\56com" +,"||\x66answon\x67.com" +,".f\x61nyu\x65.i\x6efo" +,".f\x61rwestc\x68in\x61.com" +,"||\x66astl\x79.\x6eet" +,"fa\x76oriou\x73\56com" +,"||\x66avorio\x75\x73.com" +,"e\x6e\56favott\x65\x72.net" +,"||\x66aststo\x6e\x65.org" +,"fa\x76sta\x72.fm" +,"||\x66avsta\x72\56fm" +,"fa\x79da\x6f.co\x6d/weblog" +,"||\x66\x62.com" +,"f\x62\56me" +,"||\x66\x62.me" +,"||\x66bsb\x78.c\x6fm" +,"fc\62.com" +,".f\x632chin\x61\56com" +,"sh\x69feik\x65.\x62log12\65\56fc2blo\x67.net" +,"vi\x64e\x6f.fdb\x6f\x78.com" +,".f\x64c8\71.jp" +,"||\x66ourfac\x65.nodes\x6eoo\x70.com" +,"fe\x65dbook\x73\56mobi" +,"fe\x65d\x73.fee\x64burne\x72\56com" +,"fe\x65ds\62.fe\x65dburne\x72.com/c\x68inagfw\x62log" +,"||\x66eedzsh\x61r\x65.com" +,"||\x66eelss\x68\56com" +,"fe\x65\x72.com" +,"fe\x6cixca\x74.\x6eet" +,"||\x66elixca\x74.net" +,"||\x66eminis\x74teache\x72.com" +,".f\x65ngzhen\x67h\x75.com" +,"||\x66engzhe\x6egh\x75.com" +,"ff\x6cic\x6b.com" +,"fg\x6dt\x76.net" +,".f\x67mt\x76.org" +,".f\x69lefact\x6fr\x79.com\57file" +,".f\x69les2m\x65\56com" +,"|h\x74tp://f\x69les2m\x65\56com" +,".f\x69leserv\x65.com/f\x69le" +,"fi\x6clthesq\x75ar\x65.org" +,"||\x66inalio\x6e.jp" +,"fi\x6edboo\x6b.\x74w" +,"fi\x6ele\x72.net" +,".f\x69reofli\x62ert\x79.o\x72g" +,"||\x66ireofl\x69bert\x79.\x6frg" +,".f\x61lsefir\x65.com" +,"||\x66alsefi\x72\x65.com" +,"fl\x65shbo\x74.\x63om" +,"||\x66lick\x72.\x63om" +,"||\x73taticf\x6cick\x72.c\x6fm" +,"fl\x69ckrhiv\x65min\x64.n\x65t" +,"yu\x6din\x67.fl\x6ee\x74.org" +,"|h\x74tp://c\x6e.fmnno\x77.com" +,"bl\x6f\x67.fool\x73mounta\x69\x6e.com" +,"ww\x77.forum\64h\x6b.com" +,"pi\x6fneer-w\x6frke\x72.f\x6frums-f\x72e\x65.com" +,"|h\x74tp://4\x73\x71.com" +,"||\x66oto\x70.n\x65t" +,"vi\x64e\x6f.fox\x62usines\x73.com" +,"||\x66ringen\x65twor\x6b.\x63om" +,"||\x66lechei\x6ethepec\x68\x65.fr" +,".f\x6fcusvp\x6e\56com" +,"||\x66of\x67.org" +,".f\x6fooo\x6f.c\x6fm" +,"||\x66oooo\x6f.\x63om" +,"fo\x6ftwibal\x6c.com" +,"||\x66ourthi\x6eternat\x69ona\x6c.o\x72g" +,"||\x66oxdi\x65.\x75s" +,"||\x66oxsu\x62.\x63om" +,"fo\x78tan\x67.c\x6fm" +,"||\x66qroute\x72.com" +,"||\x66rankl\x63\56com" +,".f\x72eaksha\x72\x65.com" +,"|h\x74tp://f\x72eaksha\x72\x65.com" +,"||\x66ree4\x75.\x63o\x6d.ar" +,"fr\x65e-gat\x65\56org" +,".f\x72e\x65.fr/\x61dsl" +,"||\x61llonli\x6eu\x78.fre\x65.fr" +,"||\x64imitri\x6b.fre\x65.\x66r" +,"ki\x6eeo\x78.fr\x65\x65.fr" +,"||\x70uttyc\x6d\56fre\x65.fr" +,"||\x66reeali\x6d.com" +,"wh\x69tebea\x72\56freebe\x61rblo\x67.\x6frg" +,".f\x72eecha\x6c\56com" +,".f\x72eedomh\x6fus\x65.org" +,"||\x66reedom\x68ous\x65.o\x72g" +,".f\x72eega\x6f.\x63om" +,"||\x66reega\x6f\56com" +,".f\x72eelott\x6f.com" +,"||\x66reelot\x74\x6f.com" +,"fr\x65eman\62.\x63om" +,".f\x72eeopen\x76p\x6e.com" +,"fr\x65emore\x6e\56com" +,"fr\x65emoren\x65w\x73.com" +,"fr\x65enet-c\x68in\x61.org" +,"fr\x65enewsc\x6e.com" +,".f\x72eeo\x7a.o\x72g/bbs" +,"||\x66reeo\x7a.\x6frg" +,"||\x77w\x77.bul\x62ou\x73.fr\x65eserv\x65\56c\x6f.uk" +,"||\x66reess\x68\56us" +,".f\x72ee-ss\x68\56com" +,"||\x66ree-ss\x68.com" +,"||\x66reenet\x70rojec\x74\56org" +,".f\x72eeo\x7a.o\x72g" +,"ww\x77.freet\x69be\x74.org" +,"||\x66reewal\x6cpaper\64\56me" +,".f\x72eeweb\x73\56com" +,"||\x66reewei\x62\x6f.com" +,".f\x72eexinw\x65\x6e.com" +,"fr\x69endfee\x64.com" +,"fr\x69endfee\x64-medi\x61\56com/e9\71a4ebe2\x66b4c198\65c2a587\675eb442\62961aa5\x612e" +,"|h\x74tp://f\x66.im" +,"|h\x74tp://w\x77\x77.zens\x75\x72.free\x72\x6b.com/" +,"fr\x65evp\x6e.nl" +,".f\x72in\x67.com" +,"||\x66rin\x67.c\x6fm" +,"||\x66romme\x6c\56net" +,".f\x72ontlin\x65defend\x65r\x73.org" +,"||\x66scke\x64.\x6frg" +,".f\x73ur\x66.com" +,".f\x75ckcnni\x63.net" +,"||\x66uckcnn\x69\x63.net" +,"fu\x63kgf\x77.o\x72g" +,"fu\x6cu\x65.com" +,".f\x75n\x66.tw" +,"fu\x6e\x70.com" +,"||\x66urinka\x6e.com" +,".f\x75turech\x69naforu\x6d.org" +,"||\x66uturem\x65ssag\x65.\x6frg" +,"||\x66\x77.cm" +,"fz\x6899\71.com" +,"fz\x6899\71.net" +,"||\x67abocor\x70.com" +,"||\x67alenw\x75\56com" +,"||\x67ame73\65\56com" +,"ga\x6debas\x65.\x63o\x6d.tw" +,"||\x67ame\x72.c\x6f\x6d.tw" +,".g\x61me\x72.co\x6d.tw" +,".g\x61me\x7a.co\x6d.tw" +,"||\x67ame\x7a.c\x6f\x6d.tw" +,".g\x61omin\x67.\x6eet" +,"||\x67aomin\x67\56net" +,"ga\x6ege\x73.com" +,".g\x61op\x69.net" +,"|h\x74tp://g\x61op\x69.net" +,"||\x67app\x70.o\x72g" +,"ga\x72dennet\x77ork\x73.c\x6fm" +,"||\x67ardenn\x65twork\x73\56org" +,"7\62\565\62.8\61.\622" +,"||\x67artliv\x65.com" +,"||\x67athe\x72.\x63om" +,"||\x67ayma\x70.\x63c" +,".g\x61zotub\x65\56com" +,"||\x67azotub\x65.com" +,"||\x67cloone\x79.com" +,".g\x63pnew\x73.\x63om" +,".g\x64b\x74.net\57forum" +,"gd\x7a\x66.org" +,"||\x67eek-ar\x74.net" +,"ge\x65kerhom\x65.com/2\6010/03/\x78ixiang\55projec\x74-cross\55gfw" +,"||\x67eekman\x75al\x73.com" +,"||\x67enuite\x63.com" +,".g\x65ocitie\x73.c\x6f.jp" +,".g\x65ocitie\x73.com/S\x69liconV\x61lley/C\x69rcuit/\65683/do\x77nloa\x64.\x68tml" +,"h\x6b\56geocit\x69e\x73.com" +,"ge\x6fcitie\x73\56jp" +,"||\x67eoho\x74.\x63om" +,"||\x67eometr\x69ctool\x73\56com" +,"||\x67et-dig\x69tal-he\x6c\x70.com" +,".g\x65tch\x75.c\x6fm" +,"||\x67etfoxy\x70rox\x79.o\x72g" +,".g\x65tfreed\x75\x72.com" +,".g\x65tlante\x72\x6e.org" +,"||\x67etlant\x65r\x6e.org" +,".g\x65tjets\x6f\56com/fo\x72um" +,"ge\x74ito\x6e.c\x6fm" +,".g\x65tsocia\x6cscop\x65.\x63om" +,"gf\x77.or\x67.ua" +,".g\x67ss\x6c.com" +,"||\x67gss\x6c.c\x6fm" +,"||\x67hos\x74.o\x72g" +,"||\x67iga-we\x62.jp" +,"|h\x74tp://c\x6e.gigan\x65w\x73.com/" +,"gi\x67porn\x6f.\x72u" +,"||\x67impsho\x70.com" +,"||\x67irlban\x6be\x72.com" +,"||\x67lennhi\x6cto\x6e.com" +,"gl\x6fbaljih\x61\x64.net" +,"gl\x6fbalmus\x65umonco\x6dmunis\x6d\56org" +,"||\x67lobalr\x65scu\x65.n\x65t" +,".g\x6cobalvo\x69cesonl\x69n\x65.org" +,"||\x67lobalv\x6ficeson\x6cin\x65.org" +,"gm\x62\x64.cn" +,"||\x67mh\x7a.org" +,"||\x67oagen\x74\56biz" +,"||\x67oagent\x70lu\x73.com" +,"go\x64footst\x65p\x73.org" +,"||\x67odfoot\x73tep\x73.o\x72g" +,"|h\x74tp://w\x77\x77.gold\x65nmelod\x79.co\x6d.tw" +,"||\x67oldwav\x65.com" +,"go\x6egmen\x67.\x69nfo" +,"go\x6eg\x6d.in" +,"go\x6egminli\x6cian\x67.c\x6fm" +,".g\x6fngw\x74.c\x6fm" +,".g\x6fodread\x73.com" +,"||\x67oodrea\x64\x73.com" +,".g\x6fodread\x65r\x73.com" +,"||\x67oodrea\x64er\x73.com" +,"||\x67oofin\x64\56com" +,"||\x67oogleu\x73ercont\x65n\x74.com" +,"||\x67oogled\x72iv\x65.com" +,".g\x6foglesi\x6c\x65.com" +,".g\x6fpetiti\x6f\x6e.com" +,"||\x67ooglev\x69de\x6f.com" +,"||\x67opetit\x69o\x6e.com" +,"||\x67ot\x77.ca/" +,"gr\x61ndtria\x6c.org" +,"||\x67raphi\x73\56n\x65.jp" +,"gr\x65atfire\x77al\x6c.biz" +,"||\x67reatfi\x72ewallo\x66chin\x61.\x6eet" +,".g\x72eatfir\x65wallof\x63hin\x61.o\x72g" +,"||\x67reatfi\x72ewallo\x66chin\x61.\x6frg" +,".g\x72eenpar\x74\x79.or\x67.\x74w" +,"gp\x61ss\61.com" +,"gr\x65at-fir\x65wal\x6c.c\x6fm" +,"gr\x65at-ro\x63\56org" +,"gr\x65atro\x63.\x6frg" +,"gr\x65atzhon\x67hu\x61.org" +,".g\x72eenvp\x6e\56net" +,"||\x67reenvp\x6e.net" +,"gs\55discus\x73.com" +,"||\x67trick\x73\56com" +,"gu\x61nch\x61.o\x72g" +,".g\x75n-worl\x64.net" +,"||\x67utteru\x6ecensor\x65\x64.com" +,"||\x67v\x6d.co\x6d\56tw" +,".g\x7a\x6d.tv" +,"||\x67zone-a\x6eim\x65.in\x66o" +,"||\x61pi\x73.go\x6fgl\x65.com" +,".a\x70pspo\x74.\x63om" +,"||\x61ppspo\x74\56com" +,"/^\x68ttps?:\x5c/\/[^\\57]+apps\x70ot\x5c.co\x6d/" +,"co\x64\x65.goog\x6c\x65.com/\x70/gappp\x72oxy" +,"co\x64\x65.goog\x6c\x65.com/\x70/gaepr\x6fxy" +,"co\x64\x65.goog\x6c\x65.com/\x70/west-\x63hamber\55season\553" +,"co\x64\x65.goog\x6c\x65.com/\x70/icefox" +,"co\x64\x65.goog\x6c\x65.com/\x70/break\x77all" +,"co\x64\x65.goog\x6c\x65.com/\x70/progr\x61m-thin\x6b/wiki/\x53oftware" +,"co\x64\x65.goog\x6c\x65.com/\x70/schol\x61rzhang" +,"co\x64\x65.goog\x6c\x65.com/\x70/vforc\x68rome/w\x69ki/Sta\x72t" +,"co\x64\x65.goog\x6c\x65.com/\x70/tuite" +,"co\x64\x65.goog\x6c\x65.com/\x70/twite\x73e" +,"co\x64\x65.goog\x6c\x65.com/\x70/twip" +,"co\x64\x65.goog\x6c\x65.com/\x70/huham\x68ire-ho\x73ts" +,"|h\x74tp://a\x75toprox\x79-gfwli\x73\x74.goog\x6cecod\x65.\x63om/svn\57trunk/\x67fwlis\x74\56txt" +,"|h\x74tps://\x61utopro\x78y-gfwl\x69s\x74.goo\x67lecod\x65\56com/sv\x6e/trunk\57gfwlis\x74.txt" +,"gf\x77interc\x65pto\x72.g\x6fogleco\x64\x65.com" +,"go\x61gen\x74.g\x6fogleco\x64\x65.com" +,"gt\x61\x70.goog\x6cecod\x65.\x63om" +,"ss\x68tunne\x6c\56google\x63od\x65.com" +,"tu\x69t\x65.goo\x67lecod\x65\56com" +,"ga\x65prox\x79.\x67ooglec\x6fd\x65.com" +,"do\x63\x73.goog\x6c\x65.com/\x44oc?doc\x69d=0Ae9\x6aWMoUhg\x561ZHd0c\x6aJ2NV81\x4eGQ5MnI\60d3E1" +,"do\x63\x73.goog\x6c\x65.com/\x56iew?id\75d8xbpp\66_4hhpb\62dfd" +,"do\x63\x73.goog\x6c\x65.com/\x56iew?id\75dds68d\x7a_9cqgm\70vgq" +,"do\x63\x73.goog\x6c\x65.com*\x56iew*id\52dg5mtm\x6a9_8g3h\x6b27f5" +,"do\x63\x73.goog\x6c\x65.com*\x56iew*id\52dg5mtm\x6a9_3188\x7848zcn" +,"do\x63\x73.goog\x6c\x65.com*\x64gtbmwd\66_934gg\719v6g4cc" +,"do\x63\x73.goog\x6c\x65.com/\x56iew?id\75dhh5gt\x78b_145n\x73xgctcc" +,"ch\x69naai\x64.\x6eet" +,"ec\x68ofo\x6e.c\x6fm" +,"||\x67olan\x67.\x6frg" +,"||\x77w\x77.kli\x70.me" +,"@@||site.locql.com" +,"@@||download.syniumsoftware.com" +,"|h\x74tp://u\x62\60.cc" +,"wo\x7a\x79.in" +,"@@||ipv6.google.com" +,".g\x6fogl\x65.*\45D8%BA%\x448%A7" +,".g\x6fogl\x65.*\57comple\x74e/sear\x63h" +,"/s\x65arch?q\75cache" +,"/s\x65arch%3\x46q%3Dca\x63he" +,"%2\x46search\453Fq%3D\x63ache" +,"go\x6fgle*se\x61rch*q=\x63ache" +,".g\x6fogl\x65.*\x73earch*\70964" +,".g\x6fogl\x65.*\x62oxun" +,".g\x6fogl\x65.*\x63hinese\53people\53eating\53babies" +,".g\x6fogl\x65.*\x68ttp*do\x74su\x62.com" +,".g\x6fogl\x65.*\x65ast*tu\x72kistan" +,".g\x6fogl\x65.*\x66acebook" +,".g\x6fogl\x65.*\x66alun" +,".g\x6fogl\x65.*\x66reechi\x6ea" +,".g\x6fogl\x65.*\x66reetib\x65t" +,".g\x6fogl\x65.*\x67fw" +,".g\x6fogl\x65.*\x67oagent" +,".g\x6fogl\x65.*\46q=gpass" +,".g\x6fogl\x65.*\x67reat*f\x69rewall" +,".g\x6fogl\x65.*\x68ujin" +,".g\x6fogl\x65.*\x68uayuan" +,".g\x6fogl\x65.*\x6aiaqing\x6cin" +,".g\x6fogl\x65.*\x6aiangze\x6din" +,".g\x6fogl\x65.*\46q=jzm&" +,".g\x6fogl\x65.*\x6eytimes" +,".g\x6fogl\x65.*\x70eaceha\x6cl" +,".g\x6fogl\x77.*\x70reside\x6e\x74.go\x76.\x74w" +,".g\x6fogl\x65.*\x70rotest\521989" +,".g\x6fogl\x65.*\x70risone\x72+of+th\x65+state" +,".g\x6fogl\x65.*\x73earch*\x54ankman" +,".g\x6fogl\x65.*\x74bm=mbl" +,".g\x6fogl\x65.*\x74bm%3Dm\x62l" +,".g\x6fogl\x65.*\x73earch*\x74bs=mbl" +,".g\x6fogl\x65.*\x73earch*\x74bs%3Dm\x62l" +,".g\x6fogl\x65.*\x74bs=qdr" +,".g\x6fogl\x65.*\x74bs%3Dq\x64r" +,".g\x6fogl\x65.*\x74bs=rltm" +,".g\x6fogl\x65.*\x74bs%3Dr\x6ctm" +,".g\x6fogl\x65.*\x54iananm\x65n" +,".g\x6fogl\x65.*\x74ianwang" +,".g\x6fogl\x65.*\x54ibetan\52indepe\x6edence" +,".g\x6fogl\x65.*\x74witter" +,".g\x6fogl\x65.*\x78ijinpi\x6eg" +,".g\x6fogl\x65.*\46q=ytht&" +,".g\x6fogl\x65.*\x7ahouyon\x67kang" +,".g\x6fogl\x65.*\608*%E5%\x41E%AA%E\67%AB%A0" +,".g\x6fogl\x65.*\664*%E7%\71C%9F%E\67%9B%B8" +,".g\x6fogl\x65.*\664*%E9%\715%87%E\65%8E%8B" +,".g\x6fogl\x65.*\45E5%85%\x41B%E4%B\71%9D" +,".g\x6fogl\x65.*\45E5%8C%\717%E5%9\x42%BD%E4\45B9%8B%\x456%98%A5" +,".g\x6fogl\x65.*\45E5%8C%\717%E4%B\x41%AC%E4\45B9%8B%\x456%98%A\65 " +,".g\x6fogl\x65.*\45E9%99%\708%E7%A\60%B4%E7\45A9%BA" +,".g\x6fogl\x65.*\45E8%B5%\x414%E5%8\x43%AA" +,".g\x6fogl\x65.*\45E8%BE%\x42E%E8%B\65%96%E5\4596%87%\x455%98%9B" +,".g\x6fogl\x65.*\45E4%BB%\x413%E5%B\x43%80*%E\65%8F%91\45E7%A5%\x418" +,".g\x6fogl\x65.*\45E5%8F%\711%E7%A\65%A8*%E\64%BB%A3\45E5%BC%\700" +,".g\x6fogl\x65.*\45E4%BB%\x413%E7%9\60%86" +,".g\x6fogl\x65.*\45E5%9C%\x420%E4%B\70%8B%E6\4595%99%\x454%BC%9A" +,".g\x6fogl\x65.*\45E7%8B%\x41C%E7%A\x42%8B%E5\458F%B0%\x456%B9%B\x45%E4%BC\459A" +,".g\x6fogl\x65.*\45E7%8B%\x41C%E7%A\x42%8B%E4\45B8%AD%\x456%96%8\67%E7%AC\4594%E4%\x42C%9A" +,".g\x6fogl\x65.*\45E5%8F%\711%E8%A\x46%BE" +,".g\x6fogl\x65.*\45E6%B3%\715%E6%8\x42%89%E5\4588%A9" +,".g\x6fogl\x65.*\45E7%BF%\x42B%E5%A\62%99" +,".g\x6fogl\x65.*\45E6%96%\x429%E6%B\x42%A8%E5\4585%B4" +,".g\x6fogl\x65.*\45E9%98%\x422%E7%8\61%AB%E9\4595%BF%\x455%9F%8E" +,".g\x6fogl\x65.*\45E5%86%\x41F%E6%A\x44%A3%E8\4599%8E" +,".g\x6fogl\x65.*\45E9%AB%\718%E6%9\71%BA%E6\4599%9F" +,".g\x6fogl\x65.*\45E9%9D%\x419%E5%9\61%BD" +,".g\x6fogl\x65.*\45E5%85%\x421%E4%B\x41%A7" +,".g\x6fogl\x65.*\45E5%85%\x423%E9%9\64%AE%E6\4597%B6%\x455%88%BB" +,".g\x6fogl\x65.*\45E5%B9%\x42F%E5%9\x43%BA" +,".g\x6fogl\x65.*\45E5%9B%\x42D%E5%A\x45%B6%E9\4598%B2%\x457%81%A\x42%E5%A2\4599" +,".g\x6fogl\x65.*\45E8%9B%\x414%E8%9\x46%86" +,".g\x6fogl\x65.*\45E9%9B%\706%E4%B\x43%9A" +,".g\x6fogl\x65.*\45E8%AE%\x420%E8%8\60%85%E6\4597%A0%\x457%96%8\66%E7%95\458C" +,".g\x6fogl\x65.*\45E5%AE%\x426%E5%A\x45%9D" +,".g\x6fogl\x65.*\45E5%81%\707%E5%B\x41%86%E6\45B7%8B" +,".g\x6fogl\x65.*\45E6%88%\712%E4%B\70%A5" +,".g\x6fogl\x65.*\45E9%94%\x416%E6%B\66%9B" +,".g\x6fogl\x65.*\45E7%BB%\70F%E6%9\66%87" +,".g\x6fogl\x65.*\45E8%BF%\711%E5%B\71%B3" +,".g\x6fogl\x65.*\45E4%B9%\71D%E5%B\70%B8%E5\45A7%94" +,".g\x6fogl\x65.*\45E9%85%\x427%E5%8\70%91" +,".g\x6fogl\x65.*\45E4%BB%\x414" +,".g\x6fogl\x65.*\45E9%9B%\x426%E5%8\65%AB*%E\65%AE%AA\45E7%AB%\x410" +,".g\x6fogl\x65.*\x73earch*\45E5%88%\718%E6%B\67%87" +,".g\x6fogl\x65.*\45E5%85%\x41D*%E5%\71B%9B" +,".g\x6fogl\x65.*\45E9%B2%\701%E6%9\70%95" +,".g\x6fogl\x65.*\45E9%A9%\x41C%E5%8\67%AF" +,".g\x6fogl\x65.*\45E9%A9%\x41C%E5%8\x41%9B" +,".g\x6fogl\x65.*\45E9%BA%\x416%E5%B\x44%93%E5\458A%B3" +,".g\x6fogl\x65.*\45E6%AF%\71B%E5%9\60%91%E8\45BE%89" +,".g\x6fogl\x65.*\45E5%86%\705%E8%9\62%99*%E\67%A4%BA\45E5%A8%\701" +,".g\x6fogl\x65.*\45E5%AD%\71F%E5%B\x42%BA%E6\459F%B1" +,".g\x6fogl\x65.*\45E6%A2%\x416%E8%9\60%A6%E6\459C%AA%\x455%90%8\x44%E6%B9\4596" +,".g\x6fogl\x65.*\45E5%85%\70D%E8%B\64%B9*vpn" +,".g\x6fogl\x65.*\45E8%8C%\709%E8%8\x45%89" +,".g\x6fogl\x65.*\45E8%8E%\x41B%E6%9\67%A5%E6\45A0%B9" +,".g\x6fogl\x65.*\45E7%BA%\x423%E7%B\61%B3%E6\45AF%94%\x454%BA%9A" +,".g\x6fogl\x65.*\45E8%AF%\x42A%E8%B\64%9D%E5\45B0%94%\x455%92%8\x43%E5%B9\45B3%E5%\x415%96" +,".g\x6fogl\x65.c\x6fm*sear\x63h*nami\x62ia*nuc\x74ech" +,".g\x6fogl\x65.*\45E7%9B%\718%E5%8\x46%A4%E4\45B9%90%\x459%98%9F" +,".g\x6fogl\x65.*\45E5%BD%\x41D%E4%B\70%BD%E5\45AA%9B" +,".g\x6fogl\x65.*\45E8%BF%\x41B%E5%A\x45%B3" +,".g\x6fogl\x65.*\45E5%89%\70D%E4%B\70%96%E4\45BB%8A%\x457%94%9F" +,".g\x6fogl\x65.*\45E6%9E%\x41A%E5%A\63%B0" +,".g\x6fogl\x65.*\45E9%9D%\712%E5%A\64%A9%E7\4599%BD%\x456%97%A\65%E6%97\4597" +,".g\x6fogl\x65.*\45E4%BA%\x42A%E6%B\60%91%E5\4585%AC%\x455%9B%AD" +,".g\x6fogl\x65.*\45E6%97%\x415%E8%A\x45%B0" +,".g\x6fogl\x65.*\45E4%B8%\709%E9%8\60%80" +,".g\x6fogl\x65.*\45E4%B8%\716%E7%B\x42%B4%E4\45BC%9A" +,".g\x6fogl\x65.*\45E5%8F%\x428%E5%B\x45%92%E5\458D%8E" +,".g\x6fogl\x65.*\45E5%A4%\x41A%E5%A\x44%90" +,".g\x6fogl\x65.*\x73earch*\45E8%B0%\x41D%E4%B\x44%9C%E4\45BA%BA" +,".g\x6fogl\x65.*\45E5%A4%\x419%E5%A\x45%89%E9\4597%A8" +,".g\x6fogl\x65.*\45E5%A4%\x419%E7%8\61%AD" +,".g\x6fogl\x65.*\45E5%A4%\x419%E7%B\x41%BF%E5\45AE%9D%\x455%AE%9\x44*%E5%B\x41%B7%E5\45B8%88%\x455%82%85" +,".g\x6fogl\x65.*\45E5%BA%\x427%E5%B\70%88%E5\4582%85*\45E5%A4%\x419%E7%B\x41%BF%E5\45AE%9D%\x455%AE%9D" +,".g\x6fogl\x65.*\45E7%AA%\701%E5%B\60%BC%E6\4596%AF" +,".g\x6fogl\x65.*\45E6%8E%\x418%E7%8\71%B9" +,".g\x6fogl\x65.*\45E6%B1%\x41A%E6%9\66%AF" +,".g\x6fogl\x65.*\45E5%94%\x41F%E8%8\71%B2" +,".g\x6fogl\x65.*\45E5%A8%\701%E8%A\67%86%E5\4585%AC%\x455%8F%B8" +,".g\x6fogl\x65.c\x6fm*%E6%\708%91%E\66%B2%A1\45E6%9C%\709%E6%9\65%8C%E4\45BA%BA" +,".g\x6fogl\x65.*\45E4%B9%\70C%E5%B\60%94%E5\4587%AF%\x458%A5%BF" +,".g\x6fogl\x65.*\45E4%BC%\70D%E5%8\67%A1" +,".g\x6fogl\x65.*\45E7%86%\719%E6%9\x44%A5" +,".g\x6fogl\x65.%\x456%96%B\60%E7%96\4586*%E7\458B%AC%\x457%AB%8B" +,".g\x6fogl\x65.*\45E6%96%\x420%E4%B\x41%AC%E6\458A%A5" +,".g\x6fogl\x65.*\45E5%BE%\710%E6%8\71%8D%E5\458E%9A" +,".g\x6fogl\x65.c\x6f\x6d.*%E5\45AE%A3%\x458%A8%80" +,".g\x6fogl\x65.*\45E5%AD%\x416%E6%B\x44%AE" +,".g\x6fogl\x65.*\45E8%80%\700%E9%8\62%A6" +,".g\x6fogl\x65.*\45E6%9C%\708%E6%9\x43%88" +,".g\x6fogl\x65.*\45E5%BD%\x421%E5%B\70%9D" +,".g\x6fogl\x65.*\x73earch*\45E4%BF%\71E%E6%A\x44%A3%E5\45A3%B0" +,".g\x6fogl\x65.*\45E9%98%\705%E5%9\60%8E%E5\458D%B3%\x457%84%9A" +,".g\x6fogl\x65.*\45E8%97%\70F%E7%8\x42%AC" +,".g\x6fogl\x65.*\45E6%B3%\x42D%E6%B\60%91" +,".g\x6fogl\x65.*\45E5%BC%\x410%E9%A\x42%98%E4\45B8%BD" +,".g\x6fogl\x65.*\45E6%B5%\719%E5%A\64%A7%E6\458B%9B%\x457%94%9\x46%E5%8A\459E" +,".g\x6fogl\x65.*\45E7%9C%\71F%E7%9\60%86%E9\4583%A8" +,".g\x6fogl\x65.*\45E7%9C%\71F%E7%9\x42%B8" +,".g\x6fogl\x65.*\45E6%94%\x42F%E5%8\x46%98" +,".g\x6fogl\x65.*\45E6%94%\x42F%E6%B\62%BB%E5\45B1%80" +,".g\x6fogl\x65.*\45E7%9F%\x415%E6%8\63%85%E8\4580%85" +,".g\x6fogl\x65.*\45E4%B8%\x41D%E5%8\65%B1" +,".g\x6fogl\x65.*\45E4%B8%\x41D%E5%9\x42%BD*%E\67%A6%81\45E9%97%\x42B" +,".g\x6fogl\x65.*\45E4%B8%\x41D%E5%9\x42%BD%E6\45B0%91%\x454%B8%B\x42%E5%85\459A" +,".g\x6fogl\x65.*\45E4%B8%\x41D%E5%9\x42%BD%E6\45B0%91%\x454%B8%B\x42%E8%BF\4590%E5%\70A%A8" +,".g\x6fogl\x65.*\45E4%B8%\x41D%E5%9\x42%BD%E6\4595%B0%\x455%AD%9\67%E6%97\45B6%E4%\x42B%A3" +,".g\x6fogl\x65.*\45E4%B8%\x41D%E5%9\x42%BD%E5\4586%A4%\x456%B0%9\61%E5%A4\45A7%E5%\710%8C%E\67%9B%9F" +,".g\x6fogl\x65.*\45E8%BF%\x42D%E6%9\x46%A5%E5\459B%BD%\x459%99%85" +,".g\x6fogl\x65.*\45E7%B4%\x41B%E9%9\70%B3" +,".g\x6fogl\x65.*\45E8%87%\x41A%E7%9\64%B1%E9\4597%A8" +,".g\x6fogl\x65.*\45E8%87%\x41A%E7%9\64%B1%E9\4596%80" +,".g\x6fogl\x65.*\x71=%E8%8\67%AA%E7\4594%B1%\x454%BA%9\x41%E6%B4\45B2%E7%\714%B5%E\65%8F%B0" +,".g\x6fogl\x65.c\x6fm/m*%E\70%B4%BA" +,".g\x6fogl\x65.*\57search\52%E8%B4\45BA" +,".g\x6fogl\x65.c\x6fm/m*%E\70%83%A1" +,".g\x6fogl\x65.*\57search\52%E8%83\45A1" +,".g\x6fogl\x65.c\x6fm/m*%E\70%B4%BE" +,".g\x6fogl\x65.*\57search\52%E8%B4\45BE" +,".g\x6fogl\x65.*\45E6%B1%\71F" +,".g\x6fogl\x65.c\x6fm/m*%E\66%9D%8E" +,".g\x6fogl\x65.*\57search\52%E6%9D\458E" +,".g\x6fogl\x65.*\57search\52%E5%88\4598" +,".g\x6fogl\x65.*\45E7%8E%\70B" +,".g\x6fogl\x65.c\x6fm/m*%E\66%B8%A9" +,".g\x6fogl\x65.*\57search\52%E6%B8\45A9" +,".g\x6fogl\x65.c\x6fm/m*%E\65%90%B4" +,".g\x6fogl\x65.*\57search\52%E5%90\45B4" +,".g\x6fogl\x65.c\x6fm/m*%E\64%B9%A0" +,".g\x6fogl\x65.*\57search\52%E4%B9\45A0" +,".g\x6fogl\x65.*\45E5%91%\x418" +,"|h\x74tps://\52doc\x73.g\x6fogl\x65.c\x6fm" +,"|h\x74tps://\52driv\x65.\x67oogl\x65.\x63om" +,"|h\x74tps://\x67roup\x73.\x67oogl\x65.\x63om" +,"|h\x74tps://\x70lus\52.g\x6fogl\x65.c\x6fm" +,".g\x6fogl\x65.c\x6fm/mode\x72ator" +,".g\x6fogl\x65.c\x6fm%2Fmo\x64erator" +,".g\x6fogl\x65.c\x6fm/read\x65r/view\57feed" +,".g\x6fogl\x65.c\x6fm%2Fre\x61der%2F\x76iew%2F\x66eed" +,".g\x6fogl\x65.c\x6f\x6d.hk/w\x65nda" +,".g\x6fogl\x65.c\x6f\x6d.hk%2\x46wenda" +,"||\x65ncrypt\x65\x64.goog\x6c\x65.com" +,"fe\x65dprox\x79\56googl\x65\56com" +,"gr\x6fup\x73.go\x6fgl\x65.*g\x72oup" +,"kn\x6f\x6c.goog\x6c\x65.com/\x6b/-/08/\63jhi1zd\x7avxj3f" +,"ne\x77\x73.goog\x6c\x65.co\x6d.\x68k/nwsh\x70?hl=zh\55cn&tab\75wn" +,"pi\x63asawe\x62\56googl\x65\56com" +,"si\x74e\x73.goo\x67l\x65.com" +,"||\x73ite\x73.g\x6fogl\x65.c\x6fm" +,"|h\x74tps://\x74alkgad\x67e\x74.goo\x67l\x65.com/" +,"vi\x64e\x6f.goo\x67l\x65.com" +,"an\x74\x69.ant\x69\56cn\x6e.go\x6fglepag\x65\x73.com" +,"||\x66reegat\x65ge\x74.go\x6fglepag\x65\x73.com" +,"my\x62oook\x73.\x67ooglep\x61ge\x73.com" +,".g\x6foglevi\x64e\x6f.com" +,"|h\x74tps://\x61pi\x73.go\x6fgl\x65.com" +,"go\x73pelher\x61l\x64.com" +,"||\x67ospelh\x65ral\x64.c\x6fm" +,"|h\x74tp://h\x6b.gradc\x6fnnecti\x6f\x6e.com/" +,"||\x67raylog\62.org" +,"gr\x65atfir\x65\56org" +,"||\x67reatfi\x72\x65.org" +,"gr\x65atfire\x77allofc\x68in\x61.org" +,"||\x67reatro\x63.tw" +,"||\x67stati\x63\56com" +,".t\x68eguard\x69a\x6e.co*" +,"gu\x69sha\x6e.o\x72g" +,"||\x67uisha\x6e\56org" +,"||\x67unsame\x72ic\x61.com" +,".g\x79alwari\x6epoch\x65.\x63om" +,"||\x68-chin\x61\56org" +,"h1\x6e1chin\x61\56org" +,".h\x61cke\x6e.c\x63/bbs" +,"||\x68acktha\x74phon\x65.\x6eet" +,"ha\x68l\x6f.com" +,"||\x68akkat\x76\56or\x67.tw" +,".h\x61nuny\x69.\x63om" +,"||\x68ardsex\x74ub\x65.com" +,"||\x68asaowa\x6c\x6c.com" +,"ha\x76e\70.com" +,"@@||haygo.com" +,"||\x68dtv\x62.n\x65t" +,"||\x68eartyi\x74.com" +,".h\x65caito\x75\56net" +,"||\x68ecaito\x75.net" +,".h\x65chaj\x69.\x63om" +,"||\x68echaj\x69\56com" +,"||\x68elloan\x64roi\x64.c\x6fm" +,"||\x68elloqu\x65e\x72.com" +,"he\x6clotx\x74.\x63om" +,"||\x68ellotx\x74.com" +,"||\x68tx\x74.it/" +,".h\x65llou\x6b.\x6frg/for\x75m/lofi\x76ersion" +,".h\x65lpeach\x70eopl\x65.\x63om" +,"||\x68elpeac\x68peopl\x65\56com" +,".h\x65lpzhul\x69n\x67.org" +,"||\x64at\x65.fm" +,"||\x66lightc\x61ste\x72.c\x6fm" +,"||\x62ranc\x68.\x63om" +,"||\x61wardwi\x6eningfj\x6frd\x73.com" +,"||\x66uturem\x65.org" +,"||\x67etclou\x64ap\x70.com" +,"||\x63\x6c.ly" +,"@@||f.cl.ly" +,"||\x67etsmar\x74link\x73.\x63om" +,"||\x67it-sc\x6d\56com" +,"||\x6cesscs\x73\56org" +,"||\x6cis\x74.ly" +,"||\x6daxgi\x66.\x63om" +,"||\x6fverlap\x72.com" +,"||\x70eerpon\x67.com" +,"||\x70os\x65.com" +,"||\x73amsof\x66\56es" +,"||\x73endoi\x64\56com" +,"||\x73peckle\x61p\x70.com" +,"||\x73tuffim\x72eadin\x67\56net" +,"||\x74omayk\x6f\56com" +,"||\x74w\x74.fm" +,"||\x76iew\x73.fm" +,"he\x71inglia\x6e.net" +,"he\x72e4new\x73\56com" +,"||\x68eungko\x6egdiscu\x73\x73.com" +,"ap\x70.heywi\x72\x65.com" +,".h\x67sea\x76.c\x6fm" +,"hi\x64den-ad\x76en\x74.org" +,"||\x68idden-\x61dven\x74.\x6frg" +,"hi\x64eclou\x64\56com/bl\x6fg/2008\5707/29/\x66uck-be\x69jing-o\x6cympic\x73\56html" +,".h\x69demyas\x73.com" +,"||\x68idemya\x73\x73.com" +,"||\x68ideipv\x70\x6e.com" +,".h\x69hiforu\x6d.com" +,"|h\x74tp://h\x69hiforu\x6d.com" +,"||\x68ihisto\x72\x79.net" +,".h\x69gf\x77.com" +,"||\x68ighroc\x6bmedi\x61.\x63om" +,"||\x68ikingg\x66\x77.org" +,".h\x69memi\x78.\x63om" +,"ti\x6de\x73.hin\x65\x74.net" +,".h\x6aclu\x62.i\x6efo" +,".h\x6b-pu\x62.c\x6fm/forum" +,"|h\x74tp://h\x6b-pu\x62.c\x6fm" +,".h\x6b3216\70.\x63om" +,"||\x68k3216\70\56com" +,"ap\x70.hkatv\x6eew\x73.co\x6d/v3" +,"hk\x62\x63.net" +,".h\x6bb\x66.org" +,"hk\x64a\x79.net" +,".h\x6bdailyn\x65w\x73.co\x6d\56hk/chi\x6e\x61.php" +,".h\x6be\x6a.com" +,".h\x6bep\x63.co\x6d/forum\57viewth\x72ea\x64.ph\x70?tid=1\6153322" +,"gl\x6fba\x6c.hk\x65p\x63.com\52forum" +,"hk\x66ron\x74.o\x72g" +,"hk\x67olde\x6e.\x63om" +,".h\x6bgreenr\x61di\x6f.or\x67/home" +,".h\x6bheadli\x6e\x65.com*\x62log" +,".h\x6bheadli\x6e\x65.com/\x69nstant\x6eews" +,"hk\x68kh\x6b.com" +,"hk\x6a\x63.com" +,".h\x6bj\x70.org" +,"hk\x70t\x75.org" +,".h\x6breport\x65\x72.com" +,"||\x68krepor\x74e\x72.com" +,"||\x68kzon\x65.\x6frg" +,"ap\x70\x73.hlol\x69.net/g\x66wtube" +,"hn\x6ah\x6a.com" +,"||\x68njh\x6a.c\x6fm" +,"ho\x6cyspiri\x74speak\x73\56org" +,"||\x68olyspi\x72itspea\x6b\x73.org" +,"||\x64erekhs\x75.homei\x70.net" +,"|h\x74tp://h\x6fmeserv\x65rsho\x77.\x63om" +,"ho\x6egmeime\x69.com" +,"||\x68ongzh\x69\56li" +,"ho\x6ftsuit\x65\56com" +,"||\x68ootsui\x74\x65.com" +,"ho\x74fil\x65.c\x6fm/dl" +,"ho\x74po\x74.hk" +,"||\x68otspot\x73hiel\x64.\x63om" +,"||\x68ougaig\x65.com" +,"||\x68owtofo\x72g\x65.com" +,".h\x71cd\x70.org" +,"||\x68qcd\x70.o\x72g" +,".h\x72ci\x72.com" +,".h\x72ichin\x61\56org" +,"||\x68richin\x61.org" +,".h\x72\x77.org" +,"||\x68sj\x70.net" +,"||\x68tmldo\x67\56com" +,"hu\x61gla\x64.c\x6fm" +,".h\x75anghua\x67an\x67.org" +,"||\x68uanghu\x61gan\x67.o\x72g" +,".h\x75are\x6e.us" +,"hu\x61xia-ne\x77\x73.com" +,"hu\x61xi\x6e.ph" +,"hu\x61-yu\x65.n\x65t" +,"||\x68ugoro\x79\56eu" +,"\x74.\x68uhaita\x69.com" +,"||\x68ungers\x74rikefo\x72aid\x73.o\x72g" +,"||\x68upin\x67.\x6eet" +,"||\x68utiany\x69.net" +,"hu\x74ong\71.n\x65t" +,"||\x68winf\x6f.\x63om" +,"||\x68ypeshe\x6c\x6c.com" +,"||\x68yperra\x74\x65.com" +,"||\x69\61.hk" +,"||\x692p\62.de/" +,"||\x692runne\x72.com" +,"ia\x73\x6b.ca" +,"||\x69as\x6b.ca" +,"ia\x73\x6b.bz" +,"||\x69as\x6b.bz" +,"ib\x69bli\x6f.o\x72g/pub/\x70ackage\x73/ccic" +,"||\x69blogse\x72v-\x66.net" +,"|h\x74tp://c\x6e.ibtim\x65\x73.com" +,"bl\x6fg\x73.ice\x72ocke\x74.\x63om/tag" +,".i\x63i\x6a.org" +,"||\x69cl-f\x69.\x6frg" +,"||\x69conpap\x65\x72.org" +,"\x77.\x69daiwa\x6e\56com/fo\x72um" +,"id\x65mocrac\x79.asia" +,".i\x64ent\x69.ca" +,"||\x69dent\x69.\x63a" +,"||\x69diomco\x6enectio\x6e.com" +,"|h\x74tp://w\x77\x77.idlc\x6fyot\x65.c\x6fm" +,".i\x64oug\x61.c\x6fm" +,"fo\x72u\x6d.ids\x61\x6d.com" +,".i\x64\x76.tw" +,"ie\x61synew\x73\56net" +,".i\x65d2\x6b.net" +,"if\x61nqian\x67\56com" +,".i\x66an\x72.co\x6d/857" +,".i\x66cs\x73.org" +,"||\x69fcs\x73.o\x72g" +,"if\x6a\x63.org" +,"||\x61ntidr\x6d\56hp\x67.i\x67\56co\x6d.br" +,"||\x69gf\x77.net" +,"||\x69gnited\x65troi\x74.\x6eet" +,"||\x69gvit\x61.\x63om" +,"||\x69hakk\x61.\x6eet" +,"||\x69icn\x73.c\x6fm" +,"||\x69llusio\x6efactor\x79.com" +,"||\x69love8\60\56be" +,"||\x69\x6d.tv" +,"@@||myvlog.im.tv" +,"||\x69m8\70.tw" +,"||\x69magefl\x65\x61.com" +,"im\x61geshac\x6b.us" +,"||\x69mageve\x6eu\x65.com" +,"||\x69magezi\x6cl\x61.net" +,"|h\x74tp://w\x77\x77.imd\x62\56com/na\x6de/nm04\702730" +,".i\x6d\x67.ly" +,"||\x69m\x67.ly" +,".i\x6dke\x76.com" +,"||\x69mke\x76.c\x6fm" +,".i\x6dliv\x65.c\x6fm" +,"im\x6digrati\x6f\x6e.go\x76.\x74w" +,"|h\x74tp://t\x65ch\62.i\x6e\56com/vi\x64eo/" +,"||\x69ncredi\x62o\x78.fr" +,".i\x6emediah\x6b.net" +,"||\x69nmedia\x68\x6b.net" +,"||\x69nnermo\x6egoli\x61.\x6frg" +,"in\x73tagra\x6d\56com" +,"||\x69nterfa\x63eaddic\x74io\x6e.com" +,"||\x69nterna\x74ionalr\x69ver\x73.o\x72g" +,"|h\x74tp://i\x6eterne\x74\56org/" +,"in\x74ernetd\x65fensel\x65agu\x65.o\x72g" +,"in\x74ernetf\x72eedo\x6d.\x6frg" +,"||\x69nterne\x74popcul\x74ur\x65.com" +,"in\x78ia\x6e.com" +,"||\x69nxia\x6e.\x63om" +,"||\x69phoneh\x61ck\x73.com" +,"||\x69phoni\x78\56fr" +,"||\x69pictur\x65.ru" +,"ip\x6fba\x72.com" +,"||\x69ppot\x76.\x63om" +,"|h\x74tps://\52.iptor\x72ent\x73.c\x6fm" +,"|h\x74tps://\x69ptorre\x6et\x73.com" +,"||\x69pvanis\x68.com" +,"ir\x65dmai\x6c.\x6frg" +,"||\x69ronics\x6fftwar\x65\56com" +,"||\x69ronbig\x66ool\x73.c\x6fmpytho\x6e.net" +,"||\x69ronpyt\x68o\x6e.net" +,".b\x65t\x61.ise\x74.co\x6d.t\x77/forum" +,"ht\x74p://be\x74\x61.ise\x74\56co\x6d.tw\57forum" +,"fo\x72u\x6d.ise\x74.co\x6d.tw" +,".i\x73la\x6d.or\x67.hk" +,".i\x73aacma\x6f\56com" +,"||\41--isaa\x63ma\x6f.com" +,"||\x69sgrea\x74\56org" +,"||\x69smprof\x65ssiona\x6c.net" +,"is\x6fhun\x74.c\x6fm" +,"||\x69srabo\x78\56com" +,"bl\x6f\x67.iste\x66.info/\62007/10\5721/mye\x6etunnel" +,".i\x73tockph\x6ft\x6f.com" +,"is\x75naffai\x72\x73.com" +,"is\x75nt\x76.com" +,"it\x61bo\x6f.in\x66o" +,"||\x69tabo\x6f.\x69nfo" +,"it\x68el\x70.it\x68om\x65.co\x6d.tw" +,"||\x69tshidd\x65\x6e.com" +,".i\x74wee\x74.n\x65t" +,"|h\x74tp://i\x74wee\x74.n\x65t" +,".i\x754\65.com" +,"||\x69xquic\x6b\56com" +,".i\x7aaoba\x6f.\x75s" +,"||\x67mozom\x67\56izihos\x74.org" +,".i\x7ale\x73.net" +,"||\x6a.mp" +,"bl\x6f\x67.jack\x6ai\x61.com" +,"jb\x74alk\x73.cc" +,"jb\x74alk\x73.c\x6fm" +,"jb\x74alk\x73.my" +,"je\x61nyi\x6d.c\x6fm" +,"||\x6agoodie\x73.com" +,"||\x6aiaoyou\70.com" +,".j\x69ehu\x61.cz" +,"||\x68\x6b.jiep\x61n\x67.com" +,"||\x74\x77.jiep\x61n\x67.com" +,"ji\x65shibao\x62a\x6f.com" +,".j\x69mopart\x79.com" +,"|h\x74tp://j\x69mopart\x79.com" +,"ji\x6ebush\x65.\x6frg" +,"||\x6ainbush\x65.org" +,"zh\x61\x6f.jinh\x61\x69.de" +,"ji\x6egpi\x6e.o\x72g" +,"||\x6aingpi\x6e\56org" +,"a\x63\56jirua\x6e\56net" +,"||\x6aitouc\x68\56com" +,"jk\x66oru\x6d.n\x65t" +,"re\x73earc\x68.\x6ams\x63.hk\x75.hk/so\x63ial" +,"||\x6aoachim\x73.org" +,"||\x6aobs\x6f.tv" +,"||\x6aoeedel\x6da\x6e.com" +,"||\x6aournal\x6ffdemoc\x72ac\x79.org" +,"jp\x6fpforu\x6d\56net" +,"||\x6auliere\x79\x63.com" +,"||\x6aunauz\x61\56com" +,".j\x75nefour\x74h-2\60.n\x65t" +,"||\x6aunefou\x72th-2\60.\x6eet" +,"ju\x73tfreev\x70\x6e.com" +,"zh\55t\x77.jus\x74i\x6e.tv" +,"ju\x73ttrist\x61\x6e.com" +,"ju\x7aiyu\x65.c\x6fm" +,"||\x6auziyu\x65\56com" +,"||\x6awmusi\x63\56org" +,"@@||music.jwmusic.org" +,".j\x79x\x66.net" +,"||\x6ba-wa\x69.\x63om" +,".k\x61gyuoff\x69c\x65.or\x67\56tw" +,"||\x6bagyuof\x66ic\x65.or\x67.tw" +,".k\x61iyua\x6e.\x64e" +,"||\x6baka\x6f.c\x6fm" +,"ka\x6ezhongg\x75\x6f.com" +,"ka\x6ezhongg\x75\x6f.eu" +,"||\x6barayo\x75\56com" +,"||\x6bcsoftw\x61re\x73.com" +,".k\x65char\x61.\x63om" +,".k\x65epands\x68ar\x65.co\x6d/visit\57visit_\x70ag\x65.ph\x70?i=688\6154" +,".k\x65ndinco\x73.net" +,".k\x65nengb\x61\56com" +,"||\x6benengb\x61.com" +,"wi\x6b\x69.kes\x6f\56cn/Home" +,".k\x68musi\x63.\x63o\x6d.tw" +,"bb\x73.kim\x79.\x63o\x6d.tw" +,"ki\x6egdomsa\x6cvatio\x6e\56org" +,"ki\x6eghos\x74.\x63om" +,".k\x69ngston\x65.co\x6d.tw" +,"ki\x6clwal\x6c.\x63om" +,"||\x6billwal\x6c.com" +,".k\x69ssbba\x6f\56cn" +,".k\x6eowledg\x65rus\x68.c\x6fm/kr/e\x6ecyclop\x65dia" +,"||\x6bodinge\x6e.com" +,"@@||www.kodingen.com" +,"||\x6bompoze\x72.net" +,"||\x6boolsol\x75tion\x73.\x63om" +,".k\x6forn\x6b.c\x6fm" +,"||\x6boorn\x6b.\x63om" +,".k\x75\x69.name\57event" +,"ku\x6e.im" +,"||\x6burtmun\x67e\x72.com" +,"ku\x73ocit\x79.\x63om" +,"kw\x6fngwa\x68.\x63o\x6d.my" +,"ky\x6fh\x6b.net" +,".k\x7aen\x67.in\x66o" +,"||\x6bzen\x67.i\x6efo" +,"la\55foru\x6d.\x6frg" +,"la\x64broke\x73\56com" +,"||\x6cabienn\x61l\x65.org" +,"la\x67ranepo\x63\x61.com" +,".l\x61lulal\x75\56com" +,"la\x6fga\x69.org" +,"||\x6caoga\x69.\x6frg" +,"la\x6fmi\x75.com" +,".l\x61oyan\x67.\x69nfo" +,"|h\x74tp://l\x61oyan\x67.\x69nfo" +,"||\x6captopl\x6fckdow\x6e\56com" +,".l\x61qingda\x6e.net" +,"||\x6carsgeo\x72g\x65.com" +,"||\x6castf\x6d.\x65s" +,"la\x74elinen\x65w\x73.com" +,"||\x6cazarse\x61rlymus\x69\x63.com" +,"||\x6ceecheu\x6bya\x6e.org" +,"||\x6cenwhit\x65.com" +,"le\x72osu\x61.o\x72g" +,"||\x6cerosu\x61\56org" +,"bl\x6f\x67.lest\x65r85\60.i\x6efo" +,"le\x74scor\x70.\x6eet" +,"||\x6cetscor\x70.net" +,"li\x61ns\x69.org" +,".l\x69anyu\x65.\x6eet" +,"||\x6ciaowan\x67xizan\x67\56net" +,".l\x69aowang\x78izan\x67.\x6eet" +,"||\x6cibera\x6c\56or\x67.hk" +,"li\x62ertyti\x6de\x73.co\x6d\56tw" +,".l\x69dechen\x67.com/b\x6cog/fuc\x6bing-gfw" +,"li\x6dia\x6f.net" +,"ab\x69tn\x6f.li\x6epi\x65.co\x6d/use-i\x70v6-to-\x66uck-gfw" +,"||\x6cin\x65.me" +,".l\x69ngling\x66\x61.com" +,".l\x69nkide\x6f\56com" +,"||\x61p\x69.lin\x6bsalph\x61\56com" +,"||\x61pidoc\x73\56linksa\x6cph\x61.com" +,"||\x77w\x77.lin\x6bsalph\x61\56com" +,"||\x68el\x70.li\x6eksalph\x61.com" +,"||\x6cinux-e\x6eginee\x72\56net" +,"||\x6cinuxco\x6efi\x67.org" +,"||\x6cinuxre\x76iew\x73.o\x72g" +,"li\x6euxto\x79.\x6frg/arc\x68ives/i\x6estalli\x6eg-west\55chambe\x72-on-ub\x75ntu" +,".l\x69puma\x6e.\x63om" +,"||\x6cistent\x6fyoutub\x65.com" +,"li\x73toriou\x73.com" +,"||\x6ciudeju\x6e.com" +,".l\x69uhany\x75\56com" +,".l\x69ujians\x68\x75.com" +,"||\x6ciujian\x73h\x75.com" +,"li\x75xiaoto\x6e\x67.com" +,"||\x6ciuxiao\x74on\x67.com" +,"li\x75.lu" +,".l\x69velea\x6b\56com" +,"||\x6civelea\x6b.com" +,".l\x69vestat\x69o\x6e.com" +,"li\x76estrea\x6d.com" +,"||\x6civestr\x65a\x6d.com" +,"||\x6civingo\x6elin\x65.us" +,"||\x6civings\x74rea\x6d.c\x6fm" +,"||\x6civevid\x65\x6f.com" +,".l\x69vevide\x6f.com" +,"li\x7ahizhua\x6egb\x69.com" +,"lk\x63\x6e.net" +,"||\x6cockdow\x6e.com" +,"||\x6cockest\x65\x6b.com" +,"lo\x67bo\x74.net" +,"||\x6cogiq\x78.\x63om" +,"||\x6cogmik\x65\56com" +,".l\x6fnghai\x72\56hk" +,"||\x6congter\x6dl\x79.net" +,".l\x6fokatga\x6d\x65.com" +,"|h\x74tp://l\x6fokatga\x6d\x65.com" +,"||\x6cooking\x67lassth\x65atr\x65.o\x72g" +,"||\x6cookpi\x63\56com" +,"hk\x72eporte\x72.love\x64\56hk" +,"||\x6crf\x7a.com" +,".l\x73\x64.or\x67.\x68k" +,"||\x6cs\x64.or\x67\56hk" +,"ls\x66oru\x6d.n\x65t" +,"||\x6cs\x6d.org" +,"||\x6csmchin\x65s\x65.org" +,"||\x6csmkore\x61\x6e.org" +,".l\x75p\x6d.org" +,"||\x6cup\x6d.org" +,"lv\x68a\x69.org" +,"||\x6cvha\x69.o\x72g" +,"||\x6dh4\x75.org" +,"m-\x74ea\x6d.cc\57forum" +,"ww\x77.macro\x76p\x6e.com" +,"||\x6dad-a\x72.\x63h" +,"||\x6dar\x63.in\x66o" +,"ma\x72guerit\x65.su" +,"||\x6dartinc\x61rtoon\x73\56com" +,".m\x61ii\x6f.net" +,"ma\x69l-arch\x69v\x65.com" +,"ma\x6caysiak\x69n\x69.com\57cn" +,"||\x6dakemym\x6fo\x64.com" +,"||\x6darine\x73\56mil" +,"ma\x72kmai\x6c.\x6frg*mes\x73age" +,"||\x6darta\x75.\x63om" +,"ma\x72ut\x61.be\57forget" +,".m\x61rxis\x74.\x63om" +,"||\x6darxis\x74\56net" +,".m\x61rxist\x73\56org/ch\x69nese" +,"ma\x73habl\x65.\x63om" +,"||\x6dashabl\x65.com" +,"||\x6datainj\x61.com" +,"||\x6dathiew\55badimo\x6e.com" +,"||\x6datsush\x69makaed\x65.com" +,"ma\x79imay\x69.\x63om" +,"||\x6dcadfor\x75m\x73.com" +,"mc\x66o\x67.com" +,".m\x64-\x74.org" +,"||\x6dd-\x74.org" +,".m\x65diafir\x65.com/?" +,"||\x6deetu\x70.\x63om" +,"me\x66eedi\x61.\x63om" +,"li\x63h35\65.m\x65gabye\x74\56net/%E\67%BD%91\45E7%BB%\71C%E7%A\65%9E%E5\4585%BD%\x455%8F%A\64%E9%B8\45BD%E8%\x42F%81%E\67%A7%BB\45E8%AE%\x420" +,".m\x65gapor\x6e\56com" +,"||\x6degarot\x69\x63.com" +,"me\x67avide\x6f\56com" +,"||\x6degurin\x65luk\x61.c\x6fm" +,"me\x69rixiao\x63ha\x6f.com" +,"||\x6delon-p\x65ac\x68.com" +,"me\x6dedi\x61.cn" +,".m\x65mrijtt\x6d.org" +,"me\x72it-tim\x65\x73.co\x6d.\x74w" +,".m\x65sot\x77.c\x6fm/bbs" +,".m\x65tacaf\x65\56com" +,"||\x6deteors\x68owerso\x6elin\x65.c\x6fm" +,"||\x6detroli\x66\x65.ca" +,"mg\x6fo\x6e.com" +,"||\x6dgstag\x65\56com" +,"mh\x72adi\x6f.o\x72g" +,"|h\x74tp://m\x69chaela\x6et\x69.com" +,"||\x6dichael\x6darket\x6c\56com" +,"mi\x64dle-wa\x79.net" +,".m\x69h\x6b.hk/\x66orum" +,"mi\x68u\x61.org" +,".m\x69mivi\x70.\x63om" +,"mi\x6eghu\x69.o\x72g" +,"||\x6dinghu\x69\56org" +,"mi\x6eghui-s\x63hoo\x6c.o\x72g" +,".m\x69ngjing\x6cish\x69.c\x6fm" +,"||\x6dingjin\x67lish\x69.\x63om" +,"mi\x6egjingn\x65w\x73.com" +,"mi\x6egpa\x6f.c\x6fm" +,".m\x69ngpaom\x6fnthl\x79.\x63om" +,"mi\x6egpaone\x77\x73.com" +,".m\x69ngpaon\x79.com" +,".m\x69ngpaos\x66.com" +,".m\x69ngpaot\x6f\x72.com" +,".m\x69ngpaov\x61\x6e.com" +,".m\x69ninov\x61\56org/to\x72/25935\603" +,".m\x69nzhuhu\x61.net" +,"||\x6dinzhuh\x75\x61.net" +,"mi\x6ezhuzho\x6eggu\x6f.o\x72g" +,"||\x6dirogui\x64\x65.com" +,"mi\x72rorboo\x6b\x73.com" +,"mi\x74bb\x73.com" +,".m\x69xer\x6f.c\x6fm" +,"||\x6dixer\x6f.\x63om" +,"mi\x78po\x64.com" +,".m\x69x\x78.com" +,"||\x6dix\x78.com" +,"||\x6dizzmon\x61.com" +,".m\x6b500\60.c\x6fm" +,".m\x6ccoo\x6c.c\x6fm" +,"||\x6dmaax\x78.\x63om" +,"pl\x75rkto\x70.\x6dmday\x73.\x63om" +,".m\x6dmc\x61.com" +,"||\x6dobate\x6b\56net" +,".m\x6fbile0\61\56com" +,"||\x6dobilew\x61y\x73.de" +,".m\x6fbypict\x75r\x65.com" +,"|h\x74tp://m\x6fb\x79.to" +,"wi\x6b\x69.moeg\x69r\x6c.org" +,"||\x6do\x67.com" +,"mo\x6cihu\x61.o\x72g" +,"||\x6donde\x78.\x6frg" +,"|h\x74tp://w\x77\x77.monl\x61mi\x74.org" +,"c1\652\62.moo\x6f.com" +,"||\x6donitor\x63hin\x61.o\x72g" +,"bb\x73.morbe\x6c\x6c.com" +,"||\x6dorning\x73u\x6e.org" +,"||\x6dovable\x74yp\x65.com" +,"||\x6doviefa\x70.com" +,"||\x77w\x77.moz\x74\x77.org" +,"mp\63y\x65.eu" +,"||\x6dp/" +,"||\x6dpetti\x73\56com" +,"mp\x66inanc\x65\56com" +,"mp\x69new\x73.c\x6fm" +,"mr\x74wee\x74.c\x6fm" +,"||\x6drtwee\x74\56com" +,"ne\x77\x73.ms\x6e.\x63o\x6d.tw" +,".m\x73guanch\x61.com" +,"||\x6dthru\x66.\x63om" +,"||\x6dultipl\x79.com" +,"mu\x6ctiprox\x79.org" +,"fo\x72u\x6d.mym\x61j\x69.com" +,"mu\x6ctiuplo\x61\x64.com" +,"||\x6duouj\x75.\x63om" +,"||\x6duselin\x6b\x73.c\x6f.jp" +,"||\x6duz\x69.com" +,"||\x6duz\x69.net" +,"||\x6dx98\61.c\x6fm" +,".m\x79-prox\x79\56com" +,"fo\x72u\x6d.my9\60\63.com" +,"my\x61ctime\x73\56com" +,"||\x6dyaudio\x63as\x74.com" +,".m\x79a\x76.co\x6d\56tw/bbs" +,"||\x62b\x73.myc\x68a\x74.to" +,"||\x6dychina\x6dyhom\x65.\x63om" +,".m\x79chinam\x79hom\x65.c\x6fm" +,"||\x77w\x77.myc\x6ful\x64.com" +,"||\x6dyeclip\x73eid\x65.c\x6fm" +,".m\x79foru\x6d.\x63o\x6d.hk" +,"||\x6dyforu\x6d\56co\x6d.hk" +,"||\x6dyforu\x6d\56co\x6d.uk" +,".m\x79freshn\x65\x74.com" +,"||\x6dyparag\x6cidin\x67.\x63om" +,"||\x6dypopes\x63\x75.com" +,"my\x73inablo\x67.com" +,".m\x79spac\x65.\x63om" +,"||\x6eaacoal\x69tio\x6e.o\x72g" +,"ol\x64.nabbl\x65.com" +,"||\x6eaiti\x6b.\x6eet" +,"||\x6eamsis\x69\56com" +,"na\x6eyan\x67.c\x6fm" +,"||\x6eanyan\x67\56com" +,".n\x61nyangp\x6fs\x74.com" +,"||\x6eanyang\x70os\x74.com" +,".n\x61nza\x6f.c\x6fm" +,"|h\x74tp://l\x69n\x65.nav\x65\x72.jp/" +,"||\x6eavica\x74\56com" +,".n\x61kid\x6f.c\x6fm" +,"||\x6eakid\x6f.\x63om" +,".n\x61o\x6c.ca" +,"cy\x62erghos\x74.natad\x6f.com" +,"||\x6eav\x79.mil" +,"nc\x63watc\x68.\x6fr\x67.tw" +,".n\x63\x68.co\x6d.\x74w" +,".n\x63\x6e.org" +,"||\x6ec\x6e.org/" +,"||\x65tool\x73.\x6eco\x6c.com" +,"ww\x77.ne\x64.o\x72g" +,"||\x6eekoslo\x76aki\x61.n\x65t" +,"\x74.\x6eeole\x65.\x63n" +,"ne\x74colon\x79\56com" +,"bo\x6ci\x6e.net\x66irm\x73.c\x6fm" +,"z\x68\56netlo\x67\56com" +,"ne\x74m\x65.cc" +,"ne\x74worked\x62log\x73.c\x6fm" +,"ne\x76erforg\x65t896\64.\x6frg" +,"ne\x77-3lunc\x68.net" +,".n\x65w-akib\x61.com" +,".n\x65wcentu\x72ym\x63.com" +,"|h\x74tp://n\x65wcentu\x72ym\x63.com" +,"ne\x77centur\x79new\x73.c\x6fm" +,"||\x6eewche\x6e\56com" +,".n\x65wche\x6e.\x63om" +,".n\x65wgroun\x64\x73.com" +,"ne\x77landma\x67azin\x65.\x63o\x6d.au" +,"ne\x77s10\60.c\x6f\x6d.tw" +,".n\x65wsc\x6e.o\x72g" +,"||\x6eewsc\x6e.\x6frg" +,"||\x6eewsmin\x65\x72.com" +,"ne\x77spea\x6b.\x63c/story" +,".n\x65wsanca\x69.com" +,".n\x65wtaiwa\x6e.co\x6d.tw" +,"ne\x77tal\x6b.tw" +,"||\x6eewtal\x6b\56tw" +,"ne\x77yorkti\x6de\x73.com" +,"hk\52.nextm\x65di\x61.com" +,"tw\52.nextm\x65di\x61.com" +,"st\x61ti\x63.ap\x70l\x65.nex\x74medi\x61.\x63om" +,"||\x6eexton-\x6ee\x74.jp" +,"ne\x78tt\x76.co\x6d.tw" +,"||\x6eg\x61.mil" +,"ng\x65nsi\x73.c\x6fm" +,".n\x69covide\x6f.jp/wa\x74ch/" +,"||\x6eighos\x74\56org" +,"ni\x6etendiu\x6d.com" +,".d\x61yaarmo\x6ego\x6c.ni\x6e\x67.com" +,"|h\x74tp://d\x61yaarmo\x6ego\x6c.ni\x6e\x67.com" +,"ta\x69wanye\x73\56nin\x67.c\x6fm" +,"||\x6ejact\x62.\x6frg" +,"nj\x75ic\x65.com" +,"||\x6ejuic\x65.\x63om" +,"nl\x66reevp\x6e\56com" +,"no\x62elpriz\x65.org/n\x6fbel_pr\x69zes/pe\x61ce/lau\x72eates/\62010" +,"no\x62odycan\x73to\x70.us" +,"||\x6eobodyc\x61nsto\x70.\x75s" +,"||\x6eokogir\x69.org" +,"||\x6eokol\x61.\x63om" +,"||\x6eoobbo\x78\56com" +,"||\x6eovelas\x69\x61.com" +,"||\x6eownew\x73\56com" +,".n\x6fwtorre\x6et\x73.com" +,".n\x6fyp\x66.com" +,"||\x6eoyp\x66.c\x6fm" +,"||\x6ep\x61.g\x6f.\x6ap" +,".n\x70\x73.gov" +,".n\x72\x6b.no" +,"||\x6er\x6b.no" +,"||\x6es\x63.go\x76\56tw" +,"nt\x64t\x76.co" +,"nt\x64t\x76.ca" +,"nt\x64t\x76.org" +,"nt\x64t\x76.ru" +,"||\x63b\x73.nt\x75\56ed\x75.tw" +,"||\x6euexp\x6f.\x63om" +,"||\x6eurgo-s\x6fftwar\x65\56com" +,".n\x75vi\x64.com" +,"nu\x7aco\x6d.com" +,".n\x79du\x73.ca" +,"||\x6ey\x74.com" +,"||\x6eytc\x6f.c\x6fm" +,".n\x79time\x73.\x63om" +,"||\x6eytime\x73\56com" +,"||\x6eytim\x67.\x63om" +,"ny\x73ingta\x6f\56com" +,"||\x6ezchine\x73\x65.ne\x74.\x6ez" +,"ob\x73ervech\x69n\x61.net" +,"oc\x6c\x70.hk" +,"||\x6fctober\55revie\x77\56org" +,"of\x66beatch\x69n\x61.com" +,"||\x6fgaog\x61.\x6frg" +,"tw\x74r2sr\x63.\x6fgaog\x61.\x6frg" +,".o\x69ko\x73.co\x6d.tw/v4" +,".o\x69kt\x76.com" +,"oi\x7aoblo\x67.\x63om" +,".o\x6bayfree\x64o\x6d.com" +,"ol\x64-ca\x74.n\x65t" +,"||\x6flump\x6f.\x63om" +,".o\x6cympicw\x61tc\x68.org" +,"om\x67il\x69.com" +,"om\x6eital\x6b.\x63om" +,"fo\x72u\x6d.om\x79\56sg" +,"ne\x77\x73.om\x79.\x73g" +,"sh\x6fwbi\x7a.o\x6d\x79.sg" +,"th\x65-su\x6e.o\x6e.cc" +,"t\x76\56o\x6e.cc" +,"|h\x74tps://\x6fnedriv\x65.liv\x65.\x63om/" +,".o\x6elylad\x79\56cn" +,"on\x6doo\x6e.net" +,"on\x6doo\x6e.com" +,".o\x6fpsforu\x6d.com" +,"op\x65\x6e.co\x6d.\x68k" +,"op\x65ndemoc\x72ac\x79.net" +,"my\x6fpeni\x64.\x63om" +,"||\x6dyopeni\x64.com" +,"op\x65ni\x64.net" +,"||\x6fpeni\x64.\x6eet" +,"||\x62lo\x67.op\x65ninkpo\x74.org" +,".o\x70enleak\x73.org" +,"||\x6fpenlea\x6b\x73.org" +,"op\x65nvp\x6e.n\x65t" +,"||\x6fpenvp\x6e\56net" +,"||\x6fpenweb\x73te\x72.com" +,"|h\x74tp://h\x65l\x70.ope\x72\x61.com" +,"m\x79\56oper\x61.\x63om/dah\x65ma" +,"||\x64em\x6f.op\x65ra-min\x69.net" +,"op\x6ei\x72.com\57215/my\x65ntunne\x6c-ssh-a\x75toprox\x79-cross\55gfw" +,"ww\x77.orchi\x64bb\x73.com" +,"||\x6frient-\x64ol\x6c.com" +,"or\x69entald\x61il\x79.co\x6d.my" +,"||\x6frienta\x6cdail\x79.\x63o\x6d.my" +,"or\x69entald\x61il\x79.o\x6e\56cc" +,"||\x6fr\x6e.jp" +,"\x74.\x6frzdrea\x6d.com" +,"||\x74.orzdr\x65a\x6d.com" +,"tu\x69.orzdr\x65a\x6d.com" +,"||\x6frzisti\x63.org" +,"||\x6fsfoor\x61\56com" +,"\x6d.\x6fulov\x65.\x6frg" +,"||\x6furdear\x61m\x79.com" +,"ou\x72sog\x6f.c\x6fm" +,"ou\x72step\x73.\x63o\x6d.au" +,"xi\x6eqimen\x67\56over-b\x6co\x67.com" +,"sh\x61r\x65.ov\x69\56com/me\x64ia" +,"|h\x74tp://o\x77\x6c.li" +,"|h\x74tp://h\x74.ly" +,"|h\x74tp://h\x74\x6c.li" +,"|h\x74tp://m\x61s\x68.to" +,"ww\x77.owin\x64\56com" +,"|h\x74tp://w\x77\x77.oxi\x64\56it" +,"oy\x61\x78.com" +,".o\x7achines\x65.com/b\x62s" +,"||\x6f\x77.ly" +,"bb\x73.ozchi\x6ees\x65.com" +,".o\x7ayoy\x6f.c\x6fm" +,".p\x61cificp\x6fke\x72.com" +,".p\x61cketi\x78\56net" +,"pa\x67e2rs\x73.\x63om" +,"||\x70agodab\x6f\x78.com" +,"||\x70ain\x74.n\x65t" +,"||\x63overin\x67we\x62.com" +,".p\x61lacemo\x6f\x6e.com" +,"fo\x72u\x6d.pal\x6dislif\x65\56com" +,"||\x70aper-r\x65plik\x61.\x63om" +,"||\x65rivers\x6ff\x74.com" +,"pa\x70e\x72.li" +,"pa\x70er\x62.us" +,".p\x61nlua\x6e.\x6eet" +,"||\x70anlua\x6e\56net" +,".p\x61norami\x6f.com" +,".p\x61rad\x65.c\x6fm/dict\x61tors/2\6009" +,"pa\x73tebi\x6e.\x63om" +,".p\x61sti\x65.o\x72g" +,"||\x70asti\x65.\x6frg" +,"||\x62lo\x67.pa\x74htosha\x72epoin\x74\56com" +,"pb\x73.org/w\x67bh/pag\x65s/fron\x74line/g\x61te" +,"pb\x73.org/w\x67bh/pag\x65s/fron\x74line/t\x61nkman" +,"pb\x77ik\x69.com" +,"||\x70bwork\x73\56com" +,"||\x64evelop\x65r\x73.bo\x78\56net" +,"||\x77ik\x69.oa\x75t\x68.net" +,"||\x77ik\x69.ph\x6fnega\x70.\x63om" +,"||\x77ik\x69.jq\x75eryu\x69.\x63om" +,"||\x70bxe\x73.c\x6fm" +,"||\x70bxe\x73.o\x72g" +,".p\x63discus\x73.com" +,"pc\x64v\x64.co\x6d\56tw" +,".p\x63hom\x65.c\x6f\x6d.tw" +,"||\x70c\x74.or\x67\56tw" +,"pd\x65tail\x73.\x63om" +,"||\x70dprox\x79\56com" +,"pe\x61cefir\x65\56org" +,"pe\x61cehal\x6c\56com" +,".p\x65easia\x6e\56com" +,".p\x65kingdu\x63\x6b.org" +,"||\x70ekingd\x75c\x6b.org" +,"pe\x6echines\x65.com" +,"||\x70enchin\x65s\x65.net" +,".p\x65nchine\x73\x65.net" +,"pe\x6egyulon\x67.com" +,"||\x62lo\x67.pe\x6etalogi\x63.net" +,".p\x65nthous\x65.com" +,".p\x65op\x6f.org" +,"||\x70eop\x6f.o\x72g" +,".p\x65rc\x79.in" +,"pe\x72fectvp\x6e.net" +,"pe\x72fspo\x74.\x63om" +,"||\x70erlhow\x74\x6f.com" +,"ph\x69ll\x79.com" +,"||\x70hotofo\x63u\x73.com" +,"||\x70huquoc\x73ervice\x73.com" +,".p\x69cida\x65.\x6eet" +,"||\x69mg\52.pi\x63turedi\x70.com" +,"pi\x63tureso\x63ia\x6c.com" +,".p\x69dow\x6e.c\x6fm" +,".p\x69g\x6e.net" +,"bl\x6f\x67.pilo\x74moo\x6e.c\x6fm" +,".p\x69n\66.com" +,"||\x70in\66.com" +,".p\x69n\x67.fm" +,"||\x70in\x67.fm/" +,"||\x70inoy-\x6e\56com" +,".p\x69rin\x67.c\x6fm" +,"||\x70ixelq\x69\56com" +,"||\x63s\x73.pix\x6ee\x74.in" +,"||\x70ixne\x74.\x6eet" +,".p\x69xne\x74.n\x65t" +,".p\x6b.com" +,"||\x70lacemi\x78.com" +,".p\x6canetsu\x7a\x79.org" +,"|h\x74tp://p\x69cture\x73\56playbo\x79.com" +,"||\x70laybo\x79\56com" +,"pl\x61y\x73.co\x6d\56tw" +,"||\x6d.plix\x69\56com" +,"pl\x6d.or\x67.hk" +,"pl\x75nde\x72.c\x6fm" +,".p\x6cus2\70.c\x6fm" +,".p\x6cusb\x62.c\x6fm" +,".p\x6date\x73.c\x6fm" +,"||\x70o2\x62.com" +,"||\x70odicti\x6fnar\x79.c\x6fm" +,".p\x6fkersta\x72\x73.com" +,"||\x70okerst\x61r\x73.com" +,"z\x68\56pokers\x74rateg\x79\56com" +,"po\x6citical\x63hin\x61.o\x72g" +,"||\x70opular\x70age\x73.n\x65t" +,"po\x70yar\x64.c\x6fm" +,"||\x70opyar\x64\56org" +,".p\x6fr\x6e.com" +,".p\x6frn\62.com" +,".p\x6frnbas\x65\56org" +,".p\x6frnhu\x62.\x63om" +,"||\x70ornm\x6d.\x6eet" +,".p\x6frnox\x6f.\x63om" +,".p\x6frnrapi\x64shar\x65.\x63om" +,"||\x70ornrap\x69dshar\x65\56com" +,".p\x6frnstar\x63lu\x62.com" +,".p\x6frntub\x65\56com" +,".p\x6frnvisi\x74.com" +,"po\x73tadul\x74\56com" +,"||\x70owerc\x78\56com" +,"||\x77w\x77.pow\x65rpoint\x6einj\x61.c\x6fm" +,"||\x70t\x73.or\x67\56tw" +,"pu\x62\x75.co\x6d.\x74w" +,"pu\x66finbro\x77se\x72.com" +,"|h\x74tp://p\x6fs\x74.ly" +,".p\x6fsterou\x73.com" +,"|h\x74tp://p\x6fsterou\x73.com" +,"||\x70os\x74.an\x79\x75.org" +,"||\x62rali\x6f.\x63om" +,"||\x63alebel\x73to\x6e.com" +,"||\x64esigne\x72o\x6c.com" +,"||\x62lo\x67.fi\x7azi\x6b.com" +,"||\x6e\x66.i\x64.au" +,"||\x6darkmil\x69a\x6e.com" +,"||\x6co\x67.rik\x75.me" +,"||\x73ograd\x79\56me" +,"||\x76at\x6e.org" +,"||\x76eempii\x72\x65.com" +,"||\x77w\x77.veg\x6frpeder\x73e\x6e.com" +,"||\x76enture\x73wel\x6c.c\x6fm" +,"||\x77ebfe\x65.\x74k" +,"||\x77hereis\x77erne\x72.\x63om" +,"||\x62il\x6c.zh\x6fn\x67.p\x70.\x72u" +,".p\x6fwe\x72.com" +,"||\x70owe\x72.c\x6fm" +,"po\x77erappl\x65.com" +,"||\x61b\x63.p\x70.\x72u" +,"he\x69\x78.p\x70.ru" +,"||\x70rayfor\x63hin\x61.n\x65t" +,"||\x70remefo\x72window\x73\67.com" +,"||\x70resent\x61tionze\x6e.com" +,"||\x70restig\x65-a\x76.com" +,"pr\x69soner-\x73tate-s\x65cret-j\x6furnal-\x70remier" +,"||\x70rivacy\x62o\x78.de" +,"||\x70rivate\x69nterne\x74acces\x73\56com" +,"pr\x69vatepa\x73t\x65.com" +,"||\x70rivate\x70ast\x65.c\x6fm" +,"pr\x69vatetu\x6ene\x6c.com" +,"||\x70rivate\x74unne\x6c.\x63om" +,"||\x70rocopy\x74ip\x73.com" +,"pr\x6fvideoc\x6falitio\x6e.com" +,"||\x70rosibe\x6e.de" +,"pr\x6fxifie\x72\56com" +,"ap\x69.proxl\x65\x74.com" +,"||\x70roxomi\x74ro\x6e.in\x66o" +,"pr\x6fx\x79.org" +,".p\x72oxyp\x79.\x6eet" +,"||\x70roxyp\x79\56net" +,"pr\x6fxyroa\x64\56com" +,"pr\x6fz\x7a.net" +,"ps\x62lo\x67.na\x6de" +,"||\x70sblo\x67.\x6eame" +,"ps\x69pho\x6e.ca" +,".p\x74\x74.cc" +,".p\x75ffstor\x65.com" +,"||\x70ullfol\x69\x6f.com" +,"||\x70urecon\x63ept\x73.n\x65t" +,"||\x70urepd\x66\56com" +,"||\x70urevp\x6e\56com" +,".p\x75tlocke\x72.com/f\x69le" +,"pw\x6ee\x64.com" +,"py\x74ho\x6e.com" +,".p\x79tho\x6e.c\x6f\x6d.tw" +,"|h\x74tp://p\x79tho\x6e.c\x6f\x6d.tw" +,".q\x61not\x65.c\x6fm" +,"||\x71anot\x65.\x63om" +,"qi\55gon\x67.me" +,".q\x69enkue\x6e\56org" +,"||\x71ienkue\x6e.org" +,"qi\x78iangl\x75\56cn" +,"bb\x73.qmzd\x64\56com" +,".q\x6bshar\x65.\x63om" +,"qo\x6f\x73.com" +,"||\x71oo\x73.com" +,"bl\x6f\x67.qooz\x61.hk*da\x66engqixi" +,"||\x65fksof\x74\56com" +,"||\x71statu\x73\56com" +,"||\x71tweete\x72.com" +,"||\x71tra\x63.eu" +,"||\x71uadedg\x65.com" +,"||\x77w\x77.get\x79oura\x6d.\x63om" +,"||\x68iitc\x68.\x63om" +,"qu\x73i\70.net" +,".q\x76odz\x79.o\x72g" +,"ne\x6desis\62.\x71\x78.net*\x70ages*M\x79EnTunn\x65l" +,"qx\x62b\x73.org" +,"ra\x64ioaust\x72ali\x61.n\x65\x74.au" +,"op\x6d\x6c.radi\x6ftim\x65.c\x6fm" +,"||\x72adiova\x74ican\x61.\x6frg" +,"||\x72aidcal\x6c.co\x6d.tw" +,"ra\x6egze\x6e.o\x72g" +,"ra\x6eyunfe\x69\56com" +,"||\x72anyunf\x65\x69.com" +,".r\x61pbul\x6c.\x6eet" +,"||\x72apidsh\x61re\70.com" +,".r\x61pidsha\x72edat\x61.\x63om" +,"rc\x69ne\x74.ca" +,".r\x65ad10\60.\x63om" +,".r\x65adingt\x69me\x73.co\x6d.tw" +,"||\x72eading\x74ime\x73.c\x6f\x6d.tw" +,".r\x65alrapt\x61l\x6b.com" +,".r\x65cordhi\x73tor\x79.o\x72g" +,".r\x65dtub\x65.\x63om" +,"re\x66ere\x72.us" +,"||\x72efere\x72\56us" +,"||\x72eflect\x69vecod\x65\56com" +,"re\x6caxbb\x73.\x63om" +,"re\x6eminba\x6f\56com" +,".r\x65nyuren\x71ua\x6e.org" +,"||\x72enyure\x6equa\x6e.o\x72g" +,"su\x62acm\x65.r\x65route\x64\56org" +,"c\x6e\56reuter\x73.com" +,".r\x65vlef\x74.\x63om" +,"re\x74weetis\x74.com" +,"||\x72etweet\x72an\x6b.com" +,"co\x6enected\x63hin\x61.r\x65uter\x73.\x63om" +,"|h\x74tp://w\x77\x77.reut\x65r\x73.com\57news/v\x69deo" +,"re\x76ve\x72.com" +,".r\x66\x61.org" +,".r\x66achin\x61\56com" +,".r\x66amobil\x65.org" +,"||\x72fer\x6c.o\x72g" +,".r\x66\x69.fr" +,"||\x72f\x69.fr" +,"|h\x74tp://r\x66\x69.my/" +,".r\x68clou\x64.\x63om" +,".r\x69leygui\x64\x65.com" +,"ri\x6b\x75.me/" +,".r\x6cwl\x77.com" +,"||\x72lwl\x77.c\x6fm" +,"ch\x69nes\x65.r\x6e\x77.nl" +,".r\x6e\x77.nl" +,"ro\x62te\x78.com" +,"||\x72obustn\x65ssiske\x79.com" +,"||\x72ocm\x70.o\x72g" +,"||\x72oj\x6f.com" +,"||\x72onjone\x73write\x72\56com" +,".r\x6fod\x6f.com" +,".r\x73\x66.org" +,"||\x72s\x66.org/" +,".r\x73f-chin\x65s\x65.org" +,"||\x72sf-chi\x6ees\x65.org" +,".r\x73smem\x65.\x63om" +,"||\x72ssmem\x65\56com" +,".r\x74h\x6b.hk" +,"|h\x74tp://r\x74h\x6b.hk" +,".r\x74h\x6b.or\x67\56hk" +,"|h\x74tp://r\x74h\x6b.or\x67\56hk" +,"rt\x69.or\x67.tw" +,".r\x75anyife\x6e\x67.com/\x62log*so\x6de_ways\x5fto_bre\x61k_the_\x67reat_f\x69rewall" +,".r\x75shbe\x65.\x63om" +,"ru\x74ub\x65.ru" +,".r\x75yisee\x6b\56com" +,".r\x78h\x6a.net" +,"bl\x6f\x67.s13\65\56com/go\x6fgle_ssl" +,"||\x731hen\x67.\x63om" +,"||\x738foru\x6d\56com" +,"sa\x63o\x6d.hk" +,"||\x73aco\x6d.hk" +,"||\x73adpand\x61.us" +,".s\x61i\x71.me" +,"||\x73ai\x71.me/" +,".s\x61lvatio\x6e.or\x67.hk" +,"||\x73alvati\x6f\x6e.or\x67.\x68k" +,".s\x61mai\x72.r\x75/proxy\57type-01" +,"sa\x6dmyj\x73.o\x72g" +,"sa\x6ednobl\x65\56com/bo\x6fkSearc\x68/isbnI\x6equir\x79.\x61sp" +,"||\x73ankaiz\x6f\x6b.com" +,".s\x61nmi\x6e.c\x6f\x6d.tw" +,"sa\x70ikach\x75\56net" +,"sa\x76emedi\x61\56com" +,"sa\x76etibe\x74\56org" +,"sa\x76evi\x64.c\x6fm" +,"||\x73ay\62.in\x66o" +,".s\x63m\x70.com" +,".s\x63mpchin\x65s\x65.com" +,".s\x63rib\x64.c\x6fm" +,"||\x73cripts\x70o\x74.com" +,"se\x61puf\x66.c\x6fm" +,"do\x6dainhel\x70.searc\x68.com" +,"se\x63retchi\x6e\x61.com" +,"||\x73ecretg\x61rde\x6e.no" +,"||\x64efaul\x74\56secure\x73erve\x72.\x6eet" +,"se\x63urityk\x69s\x73.com" +,"se\x65smi\x63.c\x6fm" +,"||\x73eevp\x6e.\x63om" +,"||\x73eezon\x65\56net" +,"se\x6ai\x65.com" +,".s\x65ndspac\x65.com/f\x69le" +,"|h\x74tp://t\x77eet\x73.s\x65rap\x68.m\x65/" +,"se\x73aw\x65.net" +,"||\x73esaw\x65.\x6eet" +,".s\x65saw\x65.o\x72g" +,"||\x73ethwkl\x65i\x6e.net" +,"fo\x72u\x6d.set\x74\x79.co\x6d.\x74w" +,".s\x65venloa\x64.com" +,"||\x73evenlo\x61\x64.com" +,".s\x65\x78.com" +,".s\x65x-1\61.c\x6fm" +,".s\x65x\70.cc" +,".s\x65xandsu\x62missio\x6e.com" +,".s\x65xh\x75.com" +,".s\x65xhuan\x67\56com" +,"se\x78inse\x78.\x6eet" +,"||\x73exinse\x78.net" +,"6\67\5622\60.9\61\5618" +,"6\67\5622\60.9\61\5623" +,"|h\x74tp://\52\56s\x66.net" +,".s\x66ileyd\x79\56com" +,"sh\x61do\x77.ma" +,"||\x73hadows\x6fck\x73.org" +,"sh\x61hamat-\x65nglis\x68\56com" +,".s\x68angfan\x67.org" +,"||\x73hangfa\x6e\x67.org" +,"sh\x61peserv\x69ce\x73.com" +,".s\x68arebe\x65\56com" +,"||\x73hareco\x6f\x6c.org" +,"||\x73harkdo\x6cphi\x6e.c\x6fm" +,"sh\x61rpdail\x79.co\x6d.hk" +,"||\x73harpda\x69l\x79.co\x6d\56hk" +,".s\x68arpdai\x6c\x79.hk" +,".s\x68aunthe\x73hee\x70.c\x6fm" +,"||\x73haunth\x65shee\x70.\x63om" +,"sh\x65ikyerm\x61m\x69.com" +,"||\x73hellmi\x78.com" +,"sh\x65nsho\x75.\x6frg" +,"sh\x65nyunpe\x72formin\x67art\x73.o\x72g" +,"sh\x65nzhouf\x69l\x6d.com" +,"||\x73henzho\x75fil\x6d.c\x6fm" +,"sh\x69nycha\x6e\56com" +,".s\x68itaot\x76\56org" +,"||\x73hixia\x6f\56org" +,"||\x73hizha\x6f\56org" +,".s\x68izha\x6f.\x6frg" +,"sh\x6bsp\x72.mo\x62i/dabr" +,"||\x73hodanh\x71.com" +,".s\x68oppin\x67\56com" +,".s\x68owtim\x65\56jp" +,"c\x68\56shvoon\x67.com" +,".s\x68wchurc\x68\63.com" +,"|h\x74tp://s\x68wchurc\x68\63.com" +,"si\x6dplec\x64.\x6frg" +,"||\x73implec\x64.org" +,"@@||simplecd.me" +,"si\x6dplepro\x64uctivi\x74yblo\x67.\x63om" +,"bb\x73.sin\x61.\x63om/" +,"bb\x73.sin\x61.\x63om%2F" +,"bl\x6f\x67.sin\x61\56co\x6d.tw" +,"da\x69lynew\x73\56sin\x61.c\x6fm/" +,"da\x69lynew\x73\56sin\x61.c\x6fm%2F" +,"fo\x72u\x6d.sin\x61.co\x6d.hk" +,"ho\x6d\x65.sin\x61\56com" +,"||\x6dagazin\x65\x73.sin\x61\56co\x6d.tw" +,"ne\x77\x73.sin\x61\56co\x6d.hk" +,"ne\x77\x73.sin\x61\56co\x6d.tw" +,"si\x6egta\x6f.c\x6fm" +,"||\x73ingta\x6f\56com" +,"ne\x77\x73.sing\x74a\x6f.ca" +,"||\x63d\x70.sin\x69c\x61.ed\x75\56tw" +,"si\x6eo-mont\x68l\x79.com" +,"||\x73inocas\x74.com" +,"si\x6eocis\x6d.\x63om" +,"si\x6eomontr\x65a\x6c.ca" +,".s\x69none\x74.\x63a" +,".s\x69nopit\x74\56info" +,".s\x69noant\x73\56com" +,"||\x73inoant\x73.com" +,"si\x6eoquebe\x63.com" +,"||\x73ite9\60.\x6eet" +,".s\x69tebr\x6f.\x74w" +,"||\x73itek\x73.\x75\x6b.to" +,"||\x73itemap\x73.org" +,"si\x74eta\x67.us" +,"si\x73.xxx" +,"||\x73is00\61.\x63om" +,"si\x7300\61.us" +,"||\x73ju\x6d.cn/" +,"||\x73kimtub\x65.com" +,"||\x73kybe\x74.\x63om" +,"|h\x74tp://s\x6byhighp\x72emiu\x6d.\x63om/" +,"bb\x73.skyki\x77\x69.com" +,"|h\x74tp://w\x77\x77.skyp\x65.com/i\x6etl/" +,"sh\x61r\x65.sky\x70\x65.com/\x73ites/e\x6e/2008/\610/skyp\x65_presi\x64ent_ad\x64resses\x5fchin" +,"|h\x74tp://w\x77\x77.skyp\x65.com/z\x68-Hant" +,".x\x73kywalk\x65\x72.com" +,"||\x78skywal\x6be\x72.com" +,"\x6d.\x73land\x72.\x6eet" +,"||\x73lavaso\x66\x74.com" +,"||\x73lhen\x67.\x63om" +,".s\x6cidesha\x72\x65.net" +,"fo\x72u\x6d.sli\x6d\x65.co\x6d.\x74w" +,".s\x6cutloa\x64\56com" +,"sm\x68ri\x63.org" +,"||\x73o-g\x61.n\x65t" +,".s\x6f-g\x61.net" +,"||\x73o-new\x73\56com" +,".s\x6f-new\x73.\x63om" +,"ho\x6d\x65.so-n\x65\x74.ne\x74.\x74w/yisa\x5ftsai" +,"||\x73o\x63.mil/" +,"||\x73ocksli\x73\x74.net" +,".s\x6f\x64.c\x6f.jp" +,".s\x6fftethe\x72.org" +,"||\x73ofteth\x65\x72.org" +,".s\x6fftethe\x72-downl\x6fa\x64.com" +,"||\x73ofteth\x65r-down\x6coa\x64.com" +,"||\x73ogclu\x62\56com" +,"so\x68cradi\x6f\56com" +,"||\x73ohcrad\x69\x6f.com" +,"||\x77w\x77.som\x65\x65.com" +,"||\x73orting\55algori\x74hm\x73.com" +,".s\x6fum\x6f.in\x66o" +,"||\x73ou\x70.io/" +,"@@||static.soup.io" +,".s\x6cinkse\x74\56com" +,"||\x73lickvp\x6e.com" +,".s\x6eapt\x75.c\x6fm" +,"||\x73napt\x75.\x63om" +,"sn\x65akm\x65.n\x65t" +,"||\x73noope\x72\56c\x6f.uk" +,".s\x6fbee\x73.c\x6fm" +,"||\x73obee\x73.\x63om" +,"so\x63ialwha\x6c\x65.com" +,".s\x6fftethe\x72.c\x6f.jp" +,"||\x73oftwar\x65bychuc\x6b.com" +,"bl\x6f\x67.sogo\x6f.org" +,"so\x68.tw" +,"||\x73o\x68.tw" +,"so\x68franc\x65\56org" +,"||\x73ohfran\x63\x65.org" +,"ch\x69nes\x65.s\x6fifin\x64.\x63om" +,"so\x6bamonli\x6e\x65.com" +,"||\x73ome\x65.c\x6fm" +,".s\x6fngjian\x6au\x6e.com" +,"||\x73ongjia\x6eju\x6e.com" +,".s\x6fpcas\x74.\x63om" +,".s\x6fpcas\x74.\x6frg" +,"||\x73oundcl\x6fu\x64.com" +,"so\x75ndofho\x70\x65.org" +,"||\x73oundof\x68op\x65.org" +,"||\x73oupofm\x65di\x61.com" +,".s\x6furcefo\x72g\x65.net" +,"so\x75thnew\x73\56co\x6d.tw" +,"so\x77er\x73.or\x67.hk" +,"||\x77l\x78.sow\x69k\x69.net" +,"||\x73pace-s\x63ap\x65.com" +,".s\x70ankwir\x65.com" +,".s\x70\x62.com/\x62lackbe\x72ry-sof\x74ware/t\x76/downl\x6fad" +,"|h\x74tp://s\x70\x62.com/\x62lackbe\x72ry-sof\x74ware/t\x76/downl\x6fad" +,".s\x70\x62.com/\x70ocketp\x63-softw\x61re/tv/\x64ownload" +,"|h\x74tp://s\x70\x62.com/\x70ocketp\x63-softw\x61re/tv/\x64ownload" +,".s\x70\x62.com/\x73ymbian\55softwa\x72e/tv/d\x6fwnload" +,"|h\x74tp://s\x70\x62.com/\x73ymbian\55softwa\x72e/tv/d\x6fwnload" +,".s\x70eedplu\x73\x73.org" +,"||\x73pencer\x74ippin\x67\56com" +,"sp\x69nej\x73.c\x6fm" +,"||\x73proutc\x6fr\x65.com" +,"sq\x75arespa\x63\x65.com" +,"||\x73sh9\61.c\x6fm" +,"|h\x74tp://c\x64\x6e.ssta\x74i\x63.net/" +,"ww\x77.stack\x66il\x65.co\x6d/freed\x75r" +,"us\x69nf\x6f.st\x61t\x65.gov" +,".s\x74arp2\x70.\x63om" +,"||\x73tarp2\x70\56com" +,"||\x73tartpa\x67\x65.com" +,".s\x74ate16\70\56com" +,"||\x73teel-s\x74or\x6d.com" +,"st\x68o\x6f.com" +,"||\x73tho\x6f.c\x6fm" +,".s\x74icka\x6d.\x63om" +,"st\x69ckerac\x74io\x6e.co\x6d/sesawe" +,"||\x73tonega\x6de\x73.net" +,"||\x73tonei\x70\56info" +,"||\x73torage\x6eewslet\x74e\x72.com" +,".s\x74optibe\x74crisi\x73\56net" +,"||\x73toptib\x65tcrisi\x73.net" +,"||\x73towebo\x79\x64.com" +,"||\x73treami\x6egth\x65.n\x65t" +,"c\x6e\56street\x76oic\x65.c\x6fm/arti\x63le" +,"c\x6e\56street\x76oic\x65.c\x6fm/diary" +,"cn\62.stree\x74voic\x65.\x63om" +,"t\x77\56street\x76oic\x65.c\x6fm" +,"||\x73trongv\x70\x6e.com" +,".s\x74uden\x74.\x74w/db" +,"st\x75pidvid\x65o\x73.com" +,"su\x66en\x67.org" +,"||\x73ufen\x67.\x6frg" +,".s\x75garsyn\x63.com" +,"||\x73ugarsy\x6e\x63.com" +,"su\x6dmif\x79.c\x6fm" +,".s\x75olu\x6f.o\x72g" +,"ap\x69.super\x74wee\x74.n\x65t" +,"ww\x77.super\x74wee\x74.n\x65t" +,".s\x75rfeas\x79\56co\x6d.au" +,"su\x70port/y\x6futube/\x62in/req\x75es\x74.py\77contac\x74_type=\x61buse&" +,"/s\x75pport/\x79outube\57bin/st\x61ti\x63.py\77page=s\x74ar\x74.cs&" +,"||\x73vwin\x64.\x63om" +,"||\x73weu\x78.c\x6fm" +,"||\x73wift-t\x6fol\x73.net" +,".s\x79dneyto\x64a\x79.com" +,"||\x73yncbac\x6b.com" +,"||\x73ysadmi\x6e113\70.n\x65t" +,"sy\x73rescc\x64\56org" +,".s\x79te\x73.net" +,"bl\x6f\x67.syx8\66.com/2\6009/09/\x70uff" +,"bl\x6f\x67.syx8\66.cn/20\609/09/p\x75ff" +,".s\x7abb\x73.net" +,"||\x73zetowa\x68.or\x67.hk" +,".t\63\65.com" +,".t\666\x79.com" +,"||\x7466\x79.com" +,".t\x61a-us\x61.\x6frg" +,"|h\x74tp://t\x61a-us\x61.\x6frg" +,"ta\x62tte\x72.jp" +,".t\x61ce\x6d.org" +,"||\x74afawar\x64.com" +,"ta\x67wal\x6b.c\x6fm" +,"||\x74agwal\x6b\56com" +,"ta\x69pe\x69.go\x76.tw" +,".t\x61ipeiso\x63iet\x79.o\x72g" +,"||\x74aipeis\x6fciet\x79.\x6frg" +,".t\x61iwanda\x69l\x79.net" +,"||\x74aiwant\x74.or\x67.tw" +,"||\x74aiwand\x61il\x79.net" +,"ta\x69wankis\x73.com" +,"ta\x69wannat\x69o\x6e.com" +,"ta\x69wannat\x69o\x6e.co\x6d\56tw" +,"||\x74aiwann\x65w\x73.co\x6d\56tw" +,"ta\x69wanu\x73.\x6eet" +,"ta\x69wanye\x73\56com" +,"ta\x69wan-se\x78.com" +,"||\x74amiaod\x65.tk" +,"||\x74an\x63.org" +,"ta\x6egbe\x6e.c\x6fm" +,".t\x61olu\x6e.i\x6efo" +,"||\x74aolu\x6e.\x69nfo" +,"bl\x6f\x67.tara\x67an\x61.com" +,".t\x61wee\x74.c\x6fm" +,"||\x74awee\x74.\x63om" +,"||\x74bpi\x63.i\x6efo" +,"tc\x68r\x64.org" +,"tc\x6e\x6f.net*\x64oc*tor" +,"||\x74eamsee\x73mi\x63.com" +,".t\x65ashar\x6b\56com/do\x77nloa\x64.\x68tml" +,"||\x74echlif\x65we\x62.com" +,"||\x74echpar\x61is\x6f.com" +,"||\x74ec\x6b.in/" +,".t\x65lecoms\x70ac\x65.com" +,"||\x74elegra\x70\x68.c\x6f.uk" +,".t\x65nac\x79.c\x6fm" +,"||\x74heampf\x61ctor\x79.\x63om" +,"||\x74heappl\x65blo\x67.c\x6fm" +,"||\x74heatru\x6d-bell\x69\56com" +,"||\x74hebody\x73hop-us\x61.com" +,"th\x65blemis\x68.com" +,"||\x74hebcom\x70le\x78.com" +,".t\x68echina\x62ea\x74.org" +,"||\x74hediel\x69n\x65.com" +,"||\x74hed\x77.us" +,"||\x74hegate\x73note\x73.\x63om" +,"th\x65housen\x65w\x73.com" +,"||\x74hehu\x6e.\x6eet" +,"||\x74helife\x79oucans\x61v\x65.com" +,"||\x74heliu\x73\56org" +,"th\x65pirate\x62a\x79.org" +,"||\x74hepira\x74eba\x79.se" +,"th\x65qi\x69.in\x66o/blog" +,"th\x65reallo\x76\x65.kr" +,"||\x74hesart\x6frialis\x74.com" +,"th\x65speede\x72.com" +,"||\x74hetibe\x74pos\x74.c\x6fm" +,"th\x65trotsk\x79movi\x65.\x63om/" +,"th\x65viveks\x70o\x74.com" +,".t\x68isa\x76.c\x6fm" +,"|h\x74tp://t\x68isa\x76.c\x6fm" +,"th\x6bphot\x6f.\x63om" +,"||\x74homasb\x65rnhar\x64\56org" +,"th\x72eatcha\x6f\x73.com" +,"||\x74hrough\x6eightsf\x69r\x65.com" +,".t\x68umbzil\x6c\x61.com" +,"ti\x61nanmen\x6dothe\x72.\x6frg" +,"||\x74iananm\x65nuni\x76.\x63om" +,"||\x74iananm\x65nuni\x76.\x6eet" +,"||\x74iandix\x69n\x67.org" +,".t\x69anhuay\x75a\x6e.com" +,"ti\x61ntiboo\x6b\x73.org" +,".t\x69anzh\x75.\x6frg" +,".t\x69be\x74.com" +,"||\x74ibe\x74.c\x6fm" +,".t\x69be\x74.net" +,"||\x74ibe\x74.n\x65t" +,"ti\x62e\x74.or\x67\56tw" +,"ti\x62etal\x6b.\x63om" +,".t\x69betany\x6futhcon\x67res\x73.o\x72g" +,"||\x74ibetan\x79outhco\x6egres\x73.\x6frg" +,"ti\x62etfun\x64\56org" +,"ti\x62etonli\x6e\x65.com" +,"||\x74ibeton\x6cin\x65.com" +,"||\x74ibeton\x6cin\x65.tv" +,".t\x69betonl\x69n\x65.tv" +,"||\x74ibetwr\x69te\x73.org" +,".t\x69m\x65.com\57time/t\x69me100/\x6ceaders\57profil\x65/rebel" +,".t\x69m\x65.com\57time/s\x70ecials\57packag\x65s/arti\x63le/0,2\70804" +,".t\x69m\x65.com\57time/m\x61gazine" +,"||\x62lo\x67.ti\x6ee\x79.com" +,"ti\x6eycha\x74.\x63om" +,"||\x74inypas\x74\x65.com" +,"||\x74idyrea\x64.com" +,".t\x69stor\x79.\x63om" +,"||\x74kcs-co\x6clin\x73.c\x6fm" +,"||\x74kforu\x6d\56tk" +,"c\x6e\56tmagaz\x69n\x65.com" +,"|h\x74tp://t\x6d\x69.me" +,".t\x6eafli\x78.\x63om" +,"||\x74nafli\x78\56com" +,"to\x67ette\x72.\x63om" +,".t\x6fkyo-24\67.com" +,"to\x6byo-ho\x74\56com" +,"||\x74okyoc\x6e\56com" +,"to\x6eyya\x6e.n\x65t" +,".t\x6fodo\x63.c\x6fm" +,"to\x6fne\x6c.net" +,".t\x6fpnew\x73.\x69n" +,"||\x74opshar\x65.us" +,".t\x6fpshare\x77ar\x65.com" +,"||\x74opstyl\x65\64.com" +,"||\x74ops\x79.c\x6fm" +,"to\x70s\x79.com" +,"to\x72\x61.to" +,".t\x6frproje\x63\x74.org" +,"||\x74orproj\x65c\x74.org" +,"to\x72rentcr\x61z\x79.com" +,"||\x74orrent\x63raz\x79.c\x6fm" +,"||\x74orvp\x6e.\x63om" +,"to\x75ch9\71.c\x6fm" +,"||\x74outf\x72.\x63om" +,".t\x70\x69.or\x67.\x74w" +,"||\x74p\x69.or\x67\56tw" +,"||\x74ransgr\x65ssioni\x73\x6d.org" +,"||\x74raveli\x6eloca\x6c.\x63om" +,"tr\x65ndsma\x70\56com" +,"||\x74rendsm\x61\x70.com" +,".t\x72ialofc\x63\x70.org" +,"||\x74rialof\x63c\x70.org" +,"||\x74ripo\x64.\x63om" +,"@@||www.tripod.com" +,"tr\x74\x63.co\x6d.\x74w" +,"tr\x75lyergo\x6eomi\x63.c\x6fm" +,"||\x74rusted\x62\x69.com" +,".t\x72uth10\61\56c\x6f.tv" +,"|h\x74tp://t\x72uth10\61\56c\x6f.tv" +,"||\x74ruthc\x6e\56com" +,".t\x72uve\x6f.c\x6fm" +,".t\x73emtulk\x75.com" +,"ts\x71uar\x65.tv" +,"ts\x75nagaru\x6do\x6e.com" +,".t\x73ct\x76.net" +,"||\x74t106\71.\x63om" +,"||\x74tta\x6e.c\x6fm" +,".t\x74ta\x6e.com" +,"b\x62\56tt\x76.co\x6d.tw/bb" +,".t\x75anz\x74.c\x6fm" +,".t\x75b\x65.com" +,"tu\x62e\70.com" +,"||\x74ube\70.c\x6fm" +,"tu\x62eca\x6f.c\x6fm" +,"||\x74ubewol\x66.com" +,"tu\x69dan\x67.n\x65t" +,"tu\x69dan\x67.o\x72g" +,"bb\x73.tuitu\x69.info" +,"||\x74umutan\x7a\x69.com" +,".t\x75nei\x6e.c\x6fm" +,"|h\x74tp://t\x75nei\x6e.c\x6fm" +,"||\x74unnelb\x65a\x72.com" +,".t\x75rbobi\x74\56net" +,"|h\x74tp://t\x75rbobi\x74\56net" +,"||\x74urning\x74ors\x6f.c\x6fm" +,"||\x74uxtrai\x6ein\x67.com" +,"||\6301work\x73.org" +,"||\6365sing\x6ce\x73.co\x6d\56ar" +,"||\x61ll-tha\x74-is-in\x74eresti\x6e\x67.com" +,"||\x61rt-or-\x70or\x6e.com" +,"||\x6eew\x73.at\x65bit\x73.c\x6fm" +,"||\x74umbl\x72.\x61wflash\x65\x72.com" +,"||\x62adassj\x73.com" +,"||\x62asetim\x65sheigh\x74divide\x64by\62.com" +,"||\x62enjami\x6est\x65.in" +,"||\x62lo\x67.bi\x72dhouse\x61p\x70.com" +,"||\x62obulat\x65.com" +,"||\x62onjour\x6cesgeek\x73.com" +,"||\x62ookshe\x6cfpor\x6e.\x63om" +,"||\x62lo\x67.bo\x78ca\x72.io" +,"||\x62lo\x67.bi\x74l\x79.com" +,"||\x63hevron\x77p\67.com" +,"||\x63lients\x66romhel\x6c.net" +,"||\x63odebox\x61p\x70.com" +,"||\x63ooking\x74othego\x6fdlif\x65.\x63om" +,"||\x63ubicle\61\67.com" +,"||\x70hoto\x73.\x64ailym\x65\56com" +,"||\x64avidsl\x6f\x67.com" +,"||\x62lo\x67.da\x76idzieg\x6ce\x72.net" +,"||\x62lo\x67.da\x79oneap\x70\56com" +,"||\x64rewola\x6eof\x66.com" +,"||\x62lo\x67.dr\x69bbbl\x65.\x63om" +,"||\x63hao\x73.e\55spac\x79.\x63om" +,"||\x65amonnb\x72enna\x6e.\x63om" +,"||\x65veryda\x79-carr\x79\56com" +,"||\x65yespir\x69\x74.info" +,"||\x6cif\x65.fl\x794eve\x72.\x6de" +,"||\x66redwil\x73o\x6e.vc" +,"||\x66uckgf\x77\56com" +,"||\x67eekmad\x65.c\x6f.uk" +,"||\x67eneres\x69\x73.com" +,"||\x6eew\x73.gh\x6fster\x79.\x63om" +,"||\x67ivemes\x6fmethin\x67torea\x64\56com" +,"||\x62lo\x67.go\x77all\x61.c\x6fm" +,"||\x68eiy\x6f.i\x6efo" +,"||\x68ellone\x77yor\x6b.us" +,"||\x62lo\x67.ho\x74potat\x6f\56com" +,"||\x69almost\x6caug\x68.c\x6fm" +,"||\x62lo\x67.if\x74t\x74.com" +,"||\x62lo\x67.in\x73tagra\x6d\56com" +,"||\x62lo\x67.in\x73tapape\x72.com" +,"||\x69nteres\x74inglau\x67\x68.com" +,"||\x62lo\x67.ip\x68one-de\x76.org" +,"||\x6aaypark\x69nsonm\x64\56com" +,"||\x62lo\x67.jo\x65yrober\x74.org" +,"||\x6b\x74.kcom\x65.org" +,"||\x6d\x79.kes\x6f\56cn" +,"||\x62lo\x67.ki\x63kstart\x65\x72.com" +,"||\x62lo\x67.k\x6c\56am" +,"||\x62lo\x67.kl\x69\x70.me" +,"||\x74.ku\x6e.im" +,"||\x62lo\x67.li\x67htbo\x78.\x63om" +,"||\x6cittleb\x69gdetai\x6c\x73.com" +,"||\x6covequi\x63ksilve\x72.com" +,"||\x6cyricsq\x75ot\x65.com" +,"||\x6dadmenu\x6ebutton\x65\x64.com" +,"||\x6darc\x6f.o\x72g" +,"||\x6dinimal\x6da\x63.com" +,"||\x6dixedme\x64ialab\x73\56com" +,"||\x6dodfeti\x73\x68.com" +,"||\x62lo\x67.mo\x6egod\x62.o\x72g" +,"||\x6eavigea\x74er\x73.com" +,"||\x6condo\x6e.\x6eeighbo\x72hood\x72.\x63om" +,"||\x62lo\x67.pa\x74\x68.com" +,"||\x70arisle\x6do\x6e.com" +,"||\x62lo\x67.pi\x6bchu\x72.c\x6fm" +,"||\x62lo\x67.ro\x63kmel\x74.\x63om" +,"||\x62lo\x67.ro\x6danandr\x65\x67.com" +,"||\x73olozor\x72\x6f.tk" +,"||\x62lo\x67.sp\x61rrowma\x69lap\x70.c\x6fm" +,"||\x73tuffim\x72eadin\x67\56com" +,"||\x62lo\x67.su\x6dmif\x79.c\x6fm" +,"||\x74hedail\x79w\x68.at" +,"||\x74heinte\x72netwis\x68lis\x74.c\x6fm" +,"||\x74hisisw\x68yyouar\x65fa\x74.com" +,"||\x77w\x77.tif\x66anyarm\x65n\x74.com" +,"||\x74jholow\x61ychu\x6b.\x63om" +,"||\x74oms\x63.c\x6fm" +,"||\x62lo\x67.to\x70if\x79.com" +,"||\x74hehung\x72ydude\x73\56com" +,"||\x74umblwe\x65\x64.org" +,"||\x73tatu\x73.\x74whir\x6c.\x6frg" +,"||\x62lo\x67.us\x61.gov" +,"||\x70hot\x6f.u\x74o\x6d.us" +,"||\x76-stat\x65\56org" +,"||\x77ellpla\x63edpixe\x6c\x73.com" +,"||\x77hydidy\x6fubuyme\x74ha\x74.com" +,"||\x77ordbon\x65\x72.com" +,"||\x77ordsan\x64turd\x73.\x63om" +,"||\x77orstth\x69ngieve\x72at\x65.com" +,"||\x78musi\x63.\x66m" +,"||\x78uzhuoe\x72.com" +,"||\x62\x64.zh\x65.\x6ca" +,"||\x63oco\x61.z\x6fnbl\x65.n\x65t" +,".t\x76.com" +,"||\x77w\x77.t\x76.\x63om" +,"|h\x74tp://t\x76.com" +,"||\x74v-intr\x6f\x73.com" +,"tv\x61nt\x73.com" +,"fo\x72u\x6d.tv\x62\56com/" +,"tv\x62oxno\x77.\x63om" +,"tv\x69de\x72.com" +,"||\x74vunetw\x6frk\x73.com" +,"tw\x61.sh" +,"tw\x61pperke\x65pe\x72.com" +,"||\x74wapper\x6beepe\x72.\x63om" +,"||\x74wau\x64.io" +,".t\x77au\x64.io" +,".t\x77bb\x73.ne\x74.tw" +,"tw\x62b\x73.org" +,"tw\x62b\x73.tw" +,"||\x74wblogg\x65\x72.com" +,"tw\x65epma\x67.\x63om" +,".t\x77eepm\x6c.\x6frg" +,"||\x74weepm\x6c\56org" +,".t\x77eetbac\x6bu\x70.com" +,"||\x74weetba\x63ku\x70.com" +,"tw\x65etboar\x64.com" +,"||\x74weetbo\x61r\x64.com" +,".t\x77eetbon\x65\x72.biz" +,"||\x74weetbo\x6ee\x72.biz" +,".t\x77eetdec\x6b.com" +,"||\x74weetde\x63\x6b.com" +,"|h\x74tp://d\x65c\x6b.ly" +,"||\x74weet\x65.\x6eet" +,"\x6d.\x74weet\x65.\x6eet" +,"||\x6dt\x77.tl" +,"||\x74weeted\x74ime\x73.c\x6fm" +,"tw\x65etmem\x65\56com" +,"||\x74weetmy\x6cas\x74.fm" +,"tw\x65etphot\x6f.com" +,"||\x74weetph\x6ft\x6f.com" +,"||\x74weetra\x6e\x73.com" +,"tw\x65etre\x65.\x63om" +,"||\x74weetre\x65.com" +,"||\x74weetwa\x6cl\x79.com" +,"tw\x65etymai\x6c.com" +,"||\x74wft\x70.o\x72g" +,"tw\x69bas\x65.c\x6fm" +,".t\x77ibbl\x65.\x64e" +,"||\x74wibbl\x65\56de" +,"tw\x69bbo\x6e.c\x6fm" +,"||\x74wib\x73.c\x6fm" +,"tw\x69cs\x79.com" +,".t\x77ifa\x6e.c\x6fm" +,"|h\x74tp://t\x77ifa\x6e.c\x6fm" +,"tw\x69ff\x6f.com" +,"||\x74wiff\x6f.\x63om" +,"tw\x69lo\x67.org" +,"tw\x69mbo\x77.c\x6fm" +,"||\x74wimbo\x77\56com" +,"||\x74windex\x78.com" +,"tw\x69ppl\x65.jp" +,"||\x74wippl\x65\56jp" +,"||\x74wi\x70.me/" +,"tw\x69sta\x72.cc" +,"tw\x69sterno\x77.com" +,"tw\x69stor\x79.\x6eet" +,"tw\x69tbrows\x65\x72.net" +,"||\x74witcau\x73\x65.com" +,"||\x74witget\x68e\x72.com" +,"||\x74wiggi\x74\56org" +,"tw\x69tgo\x6f.c\x6fm" +,"tw\x69ti\x71.com" +,"||\x74witi\x71.\x63om" +,".t\x77itlong\x65\x72.com" +,"||\x74witlon\x67e\x72.com" +,"|h\x74tp://t\x6c.gd/" +,"tw\x69toaste\x72.com" +,"||\x74witoas\x74e\x72.com" +,"||\x74witonm\x73\x6e.com" +,".t\x77itpi\x63.\x63om" +,"||\x74witpi\x63\56com" +,"tw\x69trefer\x72a\x6c.com" +,".t\x77it2\x64.c\x6fm" +,"||\x74wit2\x64.\x63om" +,".t\x77itsta\x74\56com" +,"||\x74witsta\x74.com" +,"||\x64otheyf\x6fllowea\x63hothe\x72\56com" +,"||\x66irstfi\x76efollo\x77er\x73.com" +,"||\x72etweet\x65ffec\x74.\x63om" +,"||\x74weepli\x6b\x65.me" +,"||\x74weepgu\x69d\x65.com" +,"||\x74urbotw\x69tte\x72.c\x6fm" +,".t\x77itvi\x64.\x63om" +,"||\x74witvi\x64\56com" +,"19\71.5\71.14\70.20" +,"|h\x74tp://\x74\56co" +,"|h\x74tps://\x74.co" +,"|h\x74tp://t\x77\x74.tl" +,"tw\x69ttbo\x74.\x6eet" +,"tw\x69tte\x72.c\x6fm" +,"||\x74witte\x72\56com" +,"||\x74witte\x72\56jp" +,"||\x74wtt\x72.c\x6fm" +,"/^\x68ttps?:\x5c/\/[^\\57]+twit\x74er\x5c.co\x6d/" +,"||\x74witter\64\x6a.org" +,".t\x77itterc\x6funte\x72.\x63om" +,"||\x74witter\x63ounte\x72\56com" +,"tw\x69tterfe\x65\x64.com" +,".t\x77itterg\x61dge\x74.c\x6fm" +,"||\x74witter\x67adge\x74.\x63om" +,".t\x77itterk\x72.com" +,"||\x74witter\x6b\x72.com" +,"||\x74witter\x6dai\x6c.com" +,"tw\x69tterti\x6d.es" +,"||\x74witter\x74i\x6d.es" +,"tw\x69ttha\x74.\x63om" +,".t\x77itturl\x79.com" +,"||\x74wittur\x6c\x79.com" +,".t\x77itza\x70.\x63om" +,"tw\x69yi\x61.com" +,".t\x77re\x67.in\x66o" +,"||\x74wre\x67.i\x6efo" +,"||\x74wsta\x72.\x6eet" +,".t\x77tk\x72.com" +,"|h\x74tp://t\x77tk\x72.com" +,"||\x74wim\x67.c\x6fm" +,"tw\x74rlan\x64.\x63om" +,"tw\x75r\x6c.nl" +,".t\x77ya\x63.org" +,"||\x74wya\x63.o\x72g" +,".t\x79coo\x6c.c\x6fm" +,"||\x74ycoo\x6c.\x63om" +,"ty\x6eso\x65.org" +,"||\x74zangm\x73\56com" +,"||\x74ypepa\x64\56com" +,"@@||www.typepad.com" +,"@@||static.typepad.com" +,"||\x62lo\x67.ex\x70ofutur\x65\x73.com" +,"||\x6cegalte\x63\x68.la\x77.\x63om" +,"||\x77w\x77.loi\x63lemeu\x72\56com" +,"||\x6catimes\x62log\x73.l\x61time\x73.\x63om" +,"||\x62lo\x67.pa\x6c\x6d.com" +,"||\x62log\x73.t\x61mpaba\x79\56com" +,"||\x63ontest\x73.twili\x6f.com" +,"em\x62\x72.in" +,"||\x65mb\x72.in" +,"gu\x6fmi\x6e.us\57login" +,".s\x72c\x66.uca\x6d.org/s\x61lon/" +,"||\x75cdc199\70.org" +,"||\x75derz\x6f.\x69t" +,"||\x75d\x6e.com" +,".u\x64\x6e.com" +,"uf\x72eevp\x6e.\x63om" +,".u\x67\x6f.com" +,"uh\x72\x70.org" +,"ui\x67hurbi\x7a\56net" +,".u\x6cik\x65.net" +,"|h\x74tp://w\x77\x77.ukch\x69nes\x65.c\x6fm/www/\622/2009\5503/284\62.html" +,"uk\x6ciferad\x69\x6f.c\x6f.uk" +,"||\x75klifer\x61di\x6f.c\x6f\56uk" +,"ul\x74ravp\x6e.\x66r" +,"||\x75ltravp\x6e.fr" +,"ul\x74rax\x73.c\x6fm" +,"||\x75nbloc\x6b\56c\x6e.com" +,"un\x63yclome\x64i\x61.org" +,"un\x63yclope\x64i\x61.info" +,"||\x75nholyk\x6eigh\x74.c\x6fm" +,".u\x6e\x69.cc" +,"un\x69cod\x65.o\x72g" +,".u\x6eitedda\x69l\x79.co\x6d\56my/ind\x65\x78.php?" +,".u\x6eix10\60.\x63om" +,"||\x75nknown\x73pac\x65.o\x72g" +,"un\x70\x6f.org" +,"||\x75oc\x6e.org" +,"to\x72.updat\x65sta\x72.c\x6fm" +,".u\x70load4\x75\56info" +,".u\x70loade\x64\56to/file" +,"|h\x74tp://u\x70loade\x64\56to/file" +,".u\x70loadst\x61tio\x6e.c\x6fm/file" +,"ww\x77.urban\x6futfitt\x65r\x73.com" +,"my\x73har\x65.u\x72\x6c.co\x6d.\x74w/" +,"||\x75rlbor\x67\56com" +,"||\x75rlpars\x65\x72.com" +,"u\x73\56to" +,"||\x75sac\x6e.c\x6fm" +,"be\x74\x61.usej\x75m\x70.com" +,"||\x75sf\x6b.mil" +,"ea\x72thquak\x65.usg\x73.\x67ov/eqc\x65nter/r\x65centeq\x73ww/Map\x73/10/10\65_3\60.php" +,"||\x75sm\x63.mil" +,".u\x73trea\x6d.\x74v" +,"||\x75strea\x6d\56tv" +,"us\x75\x73.cc" +,".u\x75shar\x65.\x63om" +,"|h\x74tp://u\x75shar\x65.\x63om" +,".u\x77ant\x73.c\x6fm" +,".u\x77ant\x73.n\x65t" +,"||\x75yghurc\x6fngres\x73\56org" +,"uy\x67u\x72.org" +,"v7\60.us" +,"||\x76aayo\x6f.\x63om" +,"||\x76alue-d\x6fmai\x6e.c\x6fm" +,".v\x61n69\70.c\x6fm" +,".v\x61nem\x75.cn" +,".v\x61nilla-\x6a\x70.com" +,"va\x6esk\x79.com" +,"||\x76apur\x6c.\x63om" +,"||\x76cf-onl\x69n\x65.org" +,"||\x76cfbuil\x64e\x72.org" +,".v\x65lkaepo\x63h\x61.sk" +,"ve\x6f\x68.com" +,".v\x65rizo\x6e.\x6eet" +,"||\x76eryb\x73.\x63om" +,".v\x66\x74.co\x6d.\x74w" +,"vi\x64eoba\x6d.\x63om" +,"||\x76ideoba\x6d.com" +,".v\x69deom\x6f.\x63om" +,"||\x76idoem\x6f\56com" +,"||\x76ik\x69.com" +,"vi\x6de\x6f.com" +,"||\x76imgol\x66\56com" +,"||\x76impera\x74o\x72.org" +,"||\x76incn\x64.\x63om" +,"||\x76innie\x76\56com" +,"vi\x64e\x6f.tis\x63al\x69.it\57canali\57truveo" +,"||\x76mixcor\x65.com" +,"c\x6e\56vo\x61.mo\x62i" +,"t\x77\56vo\x61.mo\x62i" +,".v\x6fachine\x73eblo\x67.\x63om" +,"||\x76oachin\x65seblo\x67\56com" +,"vo\x61g\x64.com" +,"vo\x61canton\x65s\x65.com" +,"||\x76oacant\x6fnes\x65.c\x6fm" +,"vo\x61chines\x65.com" +,"||\x76oachin\x65s\x65.com" +,"vo\x61tibeta\x6e.com" +,"||\x76oatibe\x74a\x6e.com" +,"vo\x61new\x73.c\x6fm/chin\x65se" +,"vo\x63\x6e.tv" +,".v\x6f\x74.org" +,"||\x76o\x74.org" +,"ww\x77.vo\x79.c\x6fm" +,"||\x77w\x77.vpn\x63u\x70.com" +,"vp\x6eboo\x6b.c\x6fm" +,"vp\x6efir\x65.c\x6fm" +,"||\x76pngat\x65\56jp" +,".v\x70ngat\x65.\x6eet" +,"||\x76pngat\x65\56net" +,"||\x76pnpo\x70.\x63om" +,"||\x76pnpron\x65\x74.com" +,"||\x76tunne\x6c\56com" +,"li\x73t\x73.w\63.\x6frg/arc\x68ives/p\x75blic" +,"||\x77affle1\719\71.com" +,".j\x79z\x6a.waq\x6e.com" +,"||\x6ayz\x6a.wa\x71\x6e.com" +,".w\x61ha\x73.com" +,".w\x61igaob\x75\56com" +,"wa\x69keun\x67.\x6frg/php\x5fwind" +,".w\x61iwaie\x72\56com" +,"|h\x74tp://w\x61iwaie\x72\56com" +,"wa\x6clorno\x74\56org" +,"||\x77allpap\x65rcas\x61.\x63om" +,"||\x77w\x77.wan\55pres\x73.\x6frg" +,"||\x77anderi\x6eghors\x65\56net" +,"||\x77angaf\x75\56net" +,"||\x77angjin\x62\x6f.org" +,".w\x61ngjinb\x6f.org" +,"wa\x6eglixio\x6e\x67.com" +,"wa\x6egruosh\x75\x69.net" +,"ww\x77.wangr\x75owan\x67.\x6frg" +,"wa\x6et-dail\x79.com" +,"wa\x70edi\x61.m\x6fbi/zhs\x69mp" +,".m\x61kzho\x75.\x77arehou\x73e33\63.c\x6fm" +,"wa\x73hen\x67.n\x65t" +,".w\x61ttpa\x64.\x63om" +,".w\x65ar\x6e.com" +,"||\x77ear\x6e.c\x6fm" +,"||\x68udator\x69\x71.we\x62.\x69d" +,"||\x77eb2pro\x6aec\x74.net" +,"we\x62ban\x67.n\x65t" +,"we\x62lag\x75.c\x6fm" +,"we\x62s-t\x76.n\x65t" +,"we\x62shot\x73.\x63om" +,"we\x62sitepu\x6cs\x65.com\57help/t\x65sttool\x73.china\55tes\x74.h\x74ml" +,"we\x62worker\x64ail\x79.c\x6fm" +,"we\x65ewoo\x6f.\x6eet/hss\57hotspo\x74_cn" +,".w\x65ekma\x67.\x69nfo" +,".w\x65fon\x67.c\x6fm" +,"we\x69bolea\x6b\56com" +,"we\x69jingsh\x65n\x67.org" +,".w\x65imin\x67.\x69nfo" +,"we\x69quanwa\x6e\x67.org" +,"we\x6egewan\x67\56com" +,".w\x65ngewan\x67.org" +,"||\x77engewa\x6e\x67.org" +,".w\x65nhu\x69.ch" +,"||\x77enk\x75.c\x6fm" +,"we\x6exuecit\x79.com" +,".w\x65nyunch\x61\x6f.com" +,"||\x77enyunc\x68a\x6f.com" +,"we\x73tc\x61.com" +,"||\x77estc\x61.\x63om" +,"||\x77estern\x77olve\x73.\x63om" +,"hk\x67.westk\x69\x74.net" +,"ww\x77.wet12\63.com" +,"||\x77ep\x6e.in\x66o" +,"we\x74pussyg\x61me\x73.com" +,"we\x78iaob\x6f.\x6frg" +,"||\x77exiaob\x6f.org" +,"we\x7ahiyon\x67\56org" +,"||\x77ezon\x65.\x6eet" +,".w\x66oru\x6d.c\x6fm" +,"||\x77foru\x6d.\x63om/" +,".w\x68atbloc\x6be\x64.com" +,"||\x77hatblo\x63ke\x64.com" +,".w\x68ippeda\x73\x73.com" +,"wh\x79love\x72.\x63om" +,"||\x77hy\x78.org" +,"ev\x63h\x6b.wik\x69\x61.com/\x77iki/%E\65%A4%A7\45E7%B4%\700%E5%8\65%83%E6\4599%82%\x455%A0%B1" +,"c\x6e\56uncycl\x6fpedi\x61.\x77iki\x61.c\x6fm" +,"z\x68\56uncycl\x6fpedi\x61.\x77iki\x61.c\x6fm" +,"||\x77ikilea\x6b\x73.ch" +,"||\x77ikilea\x6b\x73.de" +,"||\x77ikilea\x6b\x73.eu" +,"||\x77ikilea\x6b\x73.lu" +,".w\x69kileak\x73.org" +,"||\x77ikilea\x6b\x73.org" +,"||\x77ikilea\x6b\x73.pl" +,"||\x63ollate\x72almurd\x65\x72.com" +,"||\x63ollate\x72almurd\x65\x72.org" +,"wi\x6bilivre\x73.info/\x77iki/%E\71%9B%B6\45E5%85%\x41B%E5%A\x45%AA%E7\45AB%A0" +,"||\x77ikimap\x69\x61.org" +,"||\x73ecur\x65.\x77ikimed\x69\x61.org" +,"||\x77ikimed\x69\x61.or\x67.\x6do" +,"z\x68\56wikine\x77\x73.org" +,"||\x77ikiwik\x69.jp" +,"||\x73port\x73.\x77illiam\x68il\x6c.com" +,"||\x77ill\x77.n\x65t" +,"||\x77indows\x70honem\x65\56com" +,"wi\x6ewhispe\x72\x73.info" +,"||\x77iredby\x74e\x73.com" +,"||\x77iredpe\x6e.com" +,".w\x69sevi\x64.\x63om" +,"||\x77isevi\x64\56com" +,".w\x69topi\x61.\x6eet" +,".w\x6f.tc" +,"||\x77oese\x72.\x63om" +,"wo\x6cfa\x78.com" +,"||\x77olfa\x78.\x63om" +,".w\x6fmensri\x67htsofc\x68in\x61.org" +,"||\x77omensr\x69ghtsof\x63hin\x61.o\x72g" +,"wo\x6fpi\x65.jp" +,"||\x77oopi\x65.\x6ap" +,"wo\x6fpi\x65.tv" +,"||\x77oopi\x65.\x74v" +,"||\x77orkatr\x75n\x61.com" +,".w\x6frldca\x74\56org" +,"wo\x72ldjour\x6ea\x6c.com" +,".w\x6frdpres\x73.com" +,"||\x77ordpre\x73\x73.com" +,"wo\x78inghui\x67u\x6f.com" +,"wo\x77-lif\x65.\x6eet" +,".w\x70oforu\x6d\56com" +,"||\x77poforu\x6d.com" +,".w\x71lh\x77.com" +,".w\x71y\x64.org" +,"||\x77qy\x64.org" +,"wr\x65tc\x68.cc" +,".w\x73\x6a.com" +,".w\x74fpeopl\x65.com" +,".w\x75al\x61.com" +,"wu\x65rkaix\x69\56com" +,"wu\x66\x69.or\x67.\x74w" +,"wu\x6ai\x65.net" +,"wu\x6aieliul\x61\x6e.com" +,"||\x77ujieli\x75la\x6e.com" +,"wu\x6bangru\x69\56net" +,"ww\x69t\x76.com" +,"wz\x79bo\x79.im\57post/1\660" +,"|h\x74tp://w\x77\x77.x-be\x72r\x79.com\57goagent" +,"||\x78-ar\x74.c\x6fm" +,"||\x78-wal\x6c.\x6frg" +,"x1\7149\x78.com" +,"x3\665\x78.com" +,"xa\x6eg\x61.com" +,".x\x62ookc\x6e.\x63om" +,"||\x78bookc\x6e\56com" +,"\x78.\x78cit\x79.jp" +,".x\x63riti\x63.\x63om" +,"de\x73tin\x79.x\x66ile\x73.t\x6f/ubbth\x72eads" +,".x\x66\x6d.p\x70.ru" +,"xg\x6dy\x64.com" +,"xh\64\x6e.cn/b\x6cog" +,"xh\x61mste\x72.\x63om" +,"on\x65.xthos\x74.info" +,".x\x69aochun\x63nj\x70.com" +,"\x73.\x78iao\x64.in" +,".x\x69aohexi\x65.com" +,"||\x78iaom\x61.\x6frg" +,"||\x78iaohex\x69\x65.com" +,"xi\x65zhu\x61.c\x6fm" +,".x\x69n\x67.com" +,"||\x78in\x67.com" +,".x\x69nmia\x6f.\x63o\x6d.hk" +,"||\x78inmia\x6f\56co\x6d.hk" +,"xi\x6eshen\x67.\x6eet" +,"xi\x6eshiju\x65\56com" +,"xi\x6ehuane\x74\56org" +,"xi\x7aang-zh\x69y\x65.org" +,"xj\x70.cc" +,"||\x78j\x70.cc" +,"||\x78ml-tra\x69ning-g\x75id\x65.com" +,"xm\x6fvie\x73.c\x6fm" +,"||\x78nx\x78.com" +,"xp\x64\x6f.net" +,"||\x78pu\x64.org" +,"||\x6b\62.xre\x61\56com" +,"||\x78tub\x65.c\x6fm" +,"bl\x6f\x67.xuit\x65.net" +,"vl\x6f\x67.xuit\x65.net" +,"xu\x7ahiyon\x67\56net" +,"||\x78ucha\x6f.\x6frg" +,"xu\x63ha\x6f.net" +,"||\x78ucha\x6f.\x6eet" +,"xv\x65dio\x73.c\x6fm" +,".x\x76ideo\x73.\x63om" +,".x\x78bb\x78.com" +,"||\x78xx\x78.co\x6d.au" +,"xy\x73.org" +,"xy\x73blog\x73.\x6frg" +,"xy\x796\71.com" +,"xy\x796\71.info" +,"pa\x67\x65.bi\x64.\x79aho\x6f.c\x6fm" +,"bl\x6fg\x73.yah\x6f\x6f.c\x6f.jp" +,"bu\x79.yaho\x6f\56co\x6d.tw\57gdsale" +,"h\x6b\56yaho\x6f.\x63om" +,"h\x6b\56knowle\x64g\x65.yah\x6f\x6f.com" +,"h\x6b\56myblo\x67\56yaho\x6f.\x63om" +,"h\x6b\56new\x73.y\x61ho\x6f.com" +,"h\x6b\56r\x64.yah\x6f\x6f.com" +,"h\x6b\56searc\x68\56yaho\x6f.\x63om/sea\x72ch" +,"h\x6b\56vide\x6f.\x6eew\x73.ya\x68o\x6f.com\57video" +,"me\x6d\x65.yaho\x6f.com" +,"t\x77\56yaho\x6f.\x63om" +,"t\x77\56myblo\x67\56yaho\x6f.\x63om" +,"t\x77\56new\x73.y\x61ho\x6f.com" +,"pu\x6cs\x65.yah\x6f\x6f.com" +,"up\x63omin\x67.\x79aho\x6f.c\x6fm" +,"vi\x64e\x6f.yah\x6f\x6f.com" +,"||\x79aho\x6f.c\x6f\x6d.hk" +,"ya\x6d.com" +,"||\x79a\x6d.com" +,"ya\x73n\x69.c\x6f.\x75k" +,"||\x79asukun\x69.o\x72.jp" +,".y\x64\x79.com" +,"||\x79eelo\x75.\x63om" +,"ye\x65y\x69.com" +,"ye\x67l\x65.net" +,"||\x79egl\x65.n\x65t" +,"yf\x72o\x67.com" +,"||\x79hc\x77.net" +,".y\x69.org" +,".y\x69di\x6f.com" +,"||\x79idi\x6f.c\x6fm" +,"yi\x6cubb\x73.c\x6fm" +,"x\x61\56yim\x67.c\x6fm" +,".y\x69pu\x62.com" +,"||\x79ipu\x62.c\x6fm" +,".y\x6fgiche\x6e\56org" +,"||\x79ogiche\x6e.org" +,"yo\x6e\x67.hu" +,".y\x6frkbb\x73.\x63a" +,"||\x79oux\x75.i\x6efo" +,".y\x79i\x69.org" +,"||\x79yi\x69.org" +,".y\x7az\x6b.com" +,"||\x79zz\x6b.com" +,".y\x6fujiz\x7a.\x63om" +,"||\x79oujiz\x7a\56com" +,"yo\x75make\x72.\x63om" +,"yo\x75pa\x69.org" +,"||\x79oupa\x69.\x6frg" +,".y\x6fur-fre\x65do\x6d.net" +,".y\x6fusendi\x74.com" +,"||\x79ousend\x69\x74.com" +,"yo\x75thba\x6f.\x63om" +,".y\x6futhnet\x72adi\x6f.o\x72g/tmit\57forum" +,"bl\x6f\x67.yout\x68wan\x74.c\x6f\x6d.tw" +,"sh\x61r\x65.you\x74hwan\x74.\x63o\x6d.tw" +,"to\x70i\x63.you\x74hwan\x74.\x63o\x6d.tw" +,".y\x6fupor\x6e.\x63om" +,"||\x79oupor\x6e\56com" +,"||\x79out\x75.be" +,".y\x6futub\x65.\x63om" +,"||\x79outub\x65\56com" +,"||\x79outube\55nocook\x69\x65.com" +,".y\x6futubec\x6e.com" +,"yo\x75versio\x6e.com" +,"||\x79ouvers\x69o\x6e.com" +,"bl\x6f\x67.youx\x75.info/\62010/03\5714/wes\x74-chamb\x65r" +,"||\x79tim\x67.c\x6fm" +,"yt\x68\x74.net" +,"yu\x61nmin\x67.\x6eet" +,"||\x79uncha\x6f\56net" +,"||\x79vesgel\x65y\x6e.com" +,"yx\65\61.net" +,"||\x79ymay\x61.\x63om" +,"za\x63eboo\x6b.\x63om" +,"||\x7aanne\x6c.\x63om" +,"||\x74ap1\61.c\x6fm" +,"lu\x6eta\x6e.za\x6fba\x6f.com" +,".z\x61oba\x6f.c\x6f\x6d.sg" +,"||\x7aaoba\x6f.\x63o\x6d.sg" +,".z\x61ozo\x6e.c\x6fm" +,"||\x7aaria\x73.\x63om" +,"ww\x77.zauru\x73.or\x67.uk" +,".z\x64ne\x74.co\x6d.tw/ne\x77s/soft\x77are/0,\62000085\6678,201\611187,00" +,".z\x65ngjiny\x61\x6e.org" +,"||\x7aeutc\x68.\x63om" +,"ww\x77.zfree\x74.com/p\x6fst/use\x6aump-br\x6fwn\x73.ht\x6dl" +,"zg\x7acj\x6a.net" +,".z\x68anbi\x6e.\x6eet" +,"||\x7ahanbi\x6e\56net" +,"zh\x65nghu\x69.\x6frg" +,"zh\x65nlib\x75.\x69nfo" +,"||\x7ahenlib\x75.info" +,".z\x68inengl\x75yo\x75.com" +,"||\x7ahonggu\x6ftes\x65.n\x65t" +,"||\x7ahongme\x6e\x67.org" +,"||\x7ahreade\x72.com" +,"zh\x75ichagu\x6fj\x69.org" +,"||\x7ahuicha\x67uoj\x69.o\x72g" +,".z\x69dd\x75.co\x6d/downl\x6fad" +,"||\x7aillion\x6b.com" +,"zi\x6ei\x6f.com" +,"||\x7aini\x6f.c\x6fm" +,"||\x7aipli\x62.\x63om" +,".z\x6bai\x70.com" +,"||\x7akai\x70.c\x6fm" +,"||\x7ali\x62.ne\x74/" +,"zm\x77.cn" +,"zo\x6dob\x6f.net" +,".z\x6fnaeuro\x70\x61.com" +,"||\x7aonaeur\x6fp\x61.com" +,"||\x7aootoo\x6c\56com" +,".z\x6fozl\x65.n\x65t" +,"wr\x69te\x72.zo\x68\x6f.com" +,".z\x73har\x65.n\x65t/down\x6coad" +,".z\x73rha\x6f.c\x6fm" +,".z\x75\x6f.la" +,"||\x7au\x6f.la" +,".z\x75ol\x61.com" +,"||\x7auol\x61.c\x6fm" +,"||\x7averef\x66\56com" +,"zy\x7ac\71.com" +,"fr\x65enet" +,"q=\x66reedom" +,"q%\63Dfreed\x6fm" +,"re\x6demberi\x6eg_tian\x61nmen_2\60_years" +,"se\x61rch*sa\x66eweb" +,"q=\x74riangle" +,"q%\63DTrian\x67le" +,"ul\x74rareach" +,"ul\x74rasurf" +,"zh\x65ngjian" +,"%E\67%BD%A2\45E8%AF%\x42E" +,".g\x6fogl\x65.*\45E9%B2%\70D%E5%B\x44%A4" +,".g\x6fogl\x65.*\45E9%AE%\711%E5%B\x44%A4" +,"se\x61rch*%E\65%8D%9A\45E8%AE%\x41F" +,"se\x61rch*%E\65%A4%A7\45E7%BA%\x41A%E5%8\65%83" +,"se\x61rch*%E\71%92%93\45E9%B1%\x42C%E5%B\62%9B" +,"%E\65%8A%A8\45E6%80%\701%E7%B\x44%91" +,"se\x61rch*%E\65%A4%9A\45E7%B6%\x41D" +,"se\x61rch*%E\65%A4%9A\45E7%BB%\x424" +,"se\x61rch*fr\x65egate" +,"se\x61rch*%E\66%B3%95\45E4%BC%\71A" +,"%E\66%B3%95\45E8%BD%\x41E%E5%A\64%A7%E6\45B3%95" +,"%E\66%B3%95\45E8%BC%\x41A%E5%8\x41%9F" +,"%E\66%B3%95\45E8%BD%\x41E%E5%8\x41%9F" +,"%E\70%83%A1\45E6%B5%\x427%E5%B\63%B0" +,"%E\70%83%A1\45E6%B5%\x427%E6%B\70%85" +,"se\x61rch*%E\70%8A%B1\45E8%8A%\x421%E5%8\65%AC%E5\45AD%90" +,"se\x61rch*%B\x43%CD%D4\45AA" +,"se\x61rch*%B\x43%D3%C3\45DC%B4%\x46A%C0%ED" +,"se\x61rch*%E\65%8A%A0\45E5%AF%\706%E4%B\x42%A3%E7\4590%86" +,"se\x61rch*%E\66%B1%9F\45E6%B5%\701%E6%B\60%93" +,"se\x61rch*%E\67%9C%8B\45E4%B8%\x41D%E5%9\x42%BD" +,"se\x61rch*%E\65%85%AD\45E5%9B%\71B" +,"%E\65%88%98\45E6%99%\713%E6%B\63%A2" +,"%E\67%BE%8E\45E5%9B%\x42D%E4%B\71%8B%E9\459F%B3" +,"%E\66%B0%91\45E8%BF%\71B%E5%8\65%9A" +,"%E\66%B0%91\45E8%BF%\710" +,"se\x61rch*%E\70%89%B2\45E6%83%\705" +,"%E\67%8E%8B\45E4%B8%\x429" +,"se\x61rch*%E\67%BD%91\45E7%89%\x429" +,"se\x61rch*%E\67%8E%8B\45E5%B8%\70C%E5%9\63%B2" +,"se\x61rch*%E\71%AD%8F\45E4%BA%\x41C%E7%9\64%9F" +,"se\x61rch*%E\66%96%87\45E5%AD%\717%E7%8\x42%B1" +,"se\x61rch*%E\66%88%91\45E7%9A%\704%E5%A\65%8B%E6\4596%97" +,"se\x61rch*%E\66%97%A0\45E7%95%\70C" +,"se\x61rch*%E\71%82%AA\45E6%81%\x426" +,"se\x61rch*%E\66%B4%97\45E8%84%\711" +,"se\x61rch*%E\66%96%B0\45E5%94%\710%E4%B\x41%BA" +,"se\x61rch*%E\66%96%B0\45E8%AF%\x41D%E4%B\70%9D" +,"se\x61rch*%E\65%AD%A6\45E8%BF%\710" +,"se\x61rch*%E\64%B8%AD\45E5%8A%\71F" +,"se\x61rch*%E\64%B8%AD\45E5%9B%\x42D%E8%A\x45%BA%E5\459D%9B" +,"se\x61rch*%E\64%B8%AD\45E5%AE%\x413%E9%8\63%A8" +,"|h\x74tps://\x75ploa\x64.\x77ikimed\x69\x61.org" +,"|h\x74tps://\52.wikip\x65di\x61.org" +,".w\x69kipedi\x61.org/w\x69ki/Dal\x61i_Lama" +,".w\x69kipedi\x61.org/w\x69ki/Dal\x61i-Lama" +,"a\x72\56wikipe\x64i\x61.org\52%D8%AF\45D8%A7%\x449%84%D\70%A7%D9\458A_%D9\4584%D8%\x417%D9%8\65%D8%A7" +,"zh\55yu\x65.wi\x6bipedi\x61\56org/wi\x6bi/%E5%\70A%89%E\66%9B%89\45E6%B3%\x412" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/A\x6eti-com\x6dunism" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/B\x6fok_bur\x6eing" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/C\x65nsorsh\x69p_in_t\x68e_Peop\x6ce%27s_\x52epubli\x63_of_Ch\x69na" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/C\x68arter_\608" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/C\x68en_Gua\x6egcheng" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/D\x65ep_pac\x6bet_ins\x70ection" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/F\x72eegate" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/G\x6flden_S\x68ield_P\x72oject" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/G\x72eat_Fi\x72ewall_\x6ff_China" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/H\x6fng_Kong" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/H\x75ang_Qi" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/I\x6eternet\x5fcensor\x73hip" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/J\x61va_Ano\x6e_Proxy" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/L\x69u_Xiao\x62o" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/S\x68i_Tao" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/T\x61nk_man" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/T\x69ananme\x6e_Papers" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/T\x69ananme\x6e_Squar\x65_prote\x73ts_of_\61989" +,"e\x6e\56wikipe\x64i\x61.org\57wiki/T\x69betan_\x69ndepen\x64ence_m\x6fvement" +,"z\x68\56wikipe\x64i\x61.org\57wiki/W\x69kipedi\x61:%E9%A\60%81%E9\459D%A2%\x455%AD%9\70%E5%BB\45A2%E8%\x418%8E%E\70%AB%96" +,"z\x68\56\x6d.wiki\x70edi\x61.o\x72g" +,"z\x68\56wikiso\x75rc\x65.org" +,"z\x68\56wikipe\x64i\x61.org\522012%E\65%B9%B4\45E4%B8%\x41D%E5%8\x44%8E%E4\45BA%BA%\x456%B0%9\61%E5%85\45B1%E5%\712%8C%E\65%9B%BD\45E8%85%\710%E8%B\64%A5%E6\45A1%88%\x454%BB%B6" +,"z\x68\56wikipe\x64i\x61.org\52512%E5\45A4%A7%\x455%9C%B\60%E9%9C\4587" +,"z\x68\56wikipe\x64i\x61.org\5208%E5%\x41E%AA%E\67%AB%A0" +,"z\x68\56wikipe\x64i\x61.org\521959%E\65%B9%B4" +,"z\x68\56wikipe\x64i\x61.org\521989%E\65%B9%B4" +,"z\x68\56wikipe\x64i\x61.org\52610%E8\45BE%A6%\x455%85%A\x43%E5%AE\45A4" +,"z\x68\56wikipe\x64i\x61.org\52Anti-C\x4eN" +,"z\x68\56wikipe\x64i\x61.org\52%E9%98\45BF%E5%\x42A%95%E\65%B3%BD" +,"z\x68\56wikipe\x64i\x61.org\52%E9%98\45BF%E6%\x422%9B%C\62%B7%E9\4598%BF%\x456%97%B\x41%E6%99\458B%E7%\x42E%8E" +,"z\x68\56wikipe\x64i\x61.org\52%E8%89\45BE%E6%\71C%AA%E\66%9C%AA" +,"z\x68\56wikipe\x64i\x61.org\52%E5%85\45AB%E4%\x429%9D%E\65%AD%A6\45E8%BF%\710" +,"z\x68\56wikipe\x64i\x61.org\52%E5%85\45AB%E6%\700%9D%E\65%B7%B4" +,"z\x68\56wikipe\x64i\x61.org\52%E7%8F\45AD%E7%\x416%85" +,"z\x68\56wikipe\x64i\x61.org\52%E9%B2\458D%E5%\x42D%A4" +,"z\x68\56wikipe\x64i\x61.org\52%E9%AE\4591%E5%\x42D%A4" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8C\4597%E4%\x42A%AC%E\71%AB%98\45E6%A0%\x411%E5%A\x44%A6%E7\4594%9F%\x458%87%A\x41%E6%B2\45BB%E8%\701%94%E\65%90%88\45E4%BC%\71A" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8C\4597%E4%\x42A%AC%E\64%B9%8B\45E6%98%\x415" +,"z\x68\56wikipe\x64i\x61.org\52%E8%96\4584%E7%\706%99%E\66%9D%A5" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8D\459A%E8%\x41E%AF" +,"z\x68\56wikipe\x64i\x61.org\52%E5%B8\4583%E6%\70B%89%E\66%A0%BC\45E4%B9%\70B%E6%9\70%A5" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9F\45B4%E7%\70E%B2" +,"z\x68\56wikipe\x64i\x61.org\52%E4%BB\4593%E5%\x414%AE%E\65%98%89\45E6%8E%\x41A" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9B\45B9%E9%\715%B7%E\71%9D%92" +,"z\x68\56wikipe\x64i\x61.org\52%E9%99\4588%E5%\705%89%E\70%AF%9A" +,"z\x68\56wikipe\x64i\x61.org\52%E6%A5\459A%E5%\x428%83%E\65%AF%BA" +,"z\x68\56wikipe\x64i\x61.org\52%e5%a4\45a7%e5%\70f%82%e\70%80%83\x5f(%e7%b\x64%91%e7\45bb%9c%\x656%9d%8\62%e5%bf\4597)" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A7%E7%\x424%80%E\65%85%83" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A7%E7%\x42A%AA%E\65%85%83\45E6%97%\x426%E6%8\x41%A5" +,"z\x68\56wikipe\x64i\x61.org\52%E8%BE\45BE%E8%\x425%96%E\65%96%87\45E5%98%\71B" +,"z\x68\56wikipe\x64i\x61.org\52%E8%BE\45BE%E5%\705%B0%E\70%90%A8\45E6%8B%\709" +,"z\x68\56wikipe\x64i\x61.org\52%E9%81\4594%E8%\718%AD%E\70%96%A9\45E6%8B%\709" +,"z\x68\56wikipe\x64i\x61.org\52%E8%BE\45BE%E5%\x420%94%E\67%BD%95\45E6%B4%\x42B%E4%B\x44%9B" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A7%E8%\x425%A6%E\65%9B%BD\45E9%99%\705" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A7%E8%\x425%A6%E\65%9C%8B\45E9%9A%\71B" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A7%E5%\x421%A0%E\66%9D%80\45E5%88%\717%E8%A\61%A8" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A7%E8%\717%8F%E\65%AF%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A7%E6%\718%AD%E\65%AF%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45B9%E5%\x412%9E%E\65%98%89\45E6%8E%\x41A" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\4581%E5%\x41D%90%E\71%9C%96" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\459C%E9%\x413%8E-2\61%E4%B8\45AD%E7%\x418%8B%E\65%BC%B9\45E9%81%\713%E5%A\x46%BC%E5\45BC%B9 " +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\45B1%E9%\x412%A8-3\61%E6%B4\45B2%E9%\71A%9B%E\65%BD%88\45E9%81%\713%E5%B\60%8E%E5\45BD%88" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\45B1%E7%\x41A%81%E\65%8E%A5" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\459C%E7%\x41A%81%E\65%8E%A5\45E6%96%\x41F%E5%9\x44%A6" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\459A%E7%\x42B%B4" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\459A%E7%\x426%AD" +,"z\x68\56wikipe\x64i\x61.org\52%E4%BF\4584%E7%\x42E%85%E\66%96%AF" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8F\458D%E5%\70D%8E%E\65%8A%BF\45E5%8A%\71B" +,"z\x68\56wikipe\x64i\x61.org\52%E9%98\45B2%E7%\701%AB%E\71%95%BF\45E5%9F%\70E" +,"z\x68\56wikipe\x64i\x61.org\52%E6%96\45B9%E5%\70A%B1%E\64%B9%8B" +,"z\x68\56wikipe\x64i\x61.org\52%E6%96\45B9%E5%\70B%B5%E\64%B9%8B" +,"z\x68\56wikipe\x64i\x61.org\52%E6%96\45B9%E8%\708%9F%E\65%AD%90" +,"z\x68\56wikipe\x64i\x61.org\52%E6%84\45A4%E9%\71D%92" +,"z\x68\56wikipe\x64i\x61.org\52%E5%B0\4581%E4%\x42B%8E%E\65%BE%B7" +,"z\x68\56wikipe\x64i\x61.org\52%E5%B0\4581%E5%\x42E%9E%E\65%BE%B7" +,"z\x68\56wikipe\x64i\x61.org\52%E5%86\45AF%E6%\x41D%A3%E\70%99%8E" +,"z\x68\56wikipe\x64i\x61.org\52%E5%82\4585%E9%\713%81%E\65%B1%B1" +,"z\x68\56wikipe\x64i\x61.org\52%E5%99\45B6%E5%\700%AB%E\70%B5%A4\45E5%B7%\x424" +,"z\x68\56wikipe\x64i\x61.org\52%E9%AB\4598%E6%\719%BA%E\66%99%9F" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9B\45B4%E7%\719%BB%E\67%A2%BA\45E5%90%\709%E5%B\60%BC%E7\4591%AA" +,"z\x68\56wikipe\x64i\x61.org\52%E5%99\45B6%E4%\x428%BE%E\66%B4%BE" +,"z\x68\56wikipe\x64i\x61.org\52%E6%94\45B9%E9%\71D%A9%E\65%8E%86\45E7%A8%\70B" +,"z\x68\56wikipe\x64i\x61.org\52%E7%94\4598%E4%\x428%B9%E\65%AF%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E9%AB\4598%E8%\x411%8C%E\65%81%A5" +,"z\x68\56wikipe\x64i\x61.org\52%E5%99\45B6%E5%\x42D%93%E\66%B4%BE" +,"z\x68\56wikipe\x64i\x61.org\52%E6%A0\45BC%E9%\x422%81%E\66%B4%BE" +,"z\x68\56wikipe\x64i\x61.org\52GFW" +,"z\x68\56wikipe\x64i\x61.org\52%E5%85\45AC%E5%\705%B1%E\67%9F%A5\45E8%AF%\706%E5%8\70%86%E5\45AD%90" +,"z\x68\56wikipe\x64i\x61.org\52Google\45E5%AE%\x411%E6%9\x46%A5" +,"z\x68\56wikipe\x64i\x61.org\57zh-cn/\45E8%B0%\x427%E6%A\x44%8C%E9\4580%80%\x455%87%B\x41%E4%B8\45AD%E5%\71B%BD%E\64%BA%8B\45E4%BB%\x426" +,"z\x68\56wikipe\x64i\x61.org\57wiki/%\x458%B0%B\67%E6%AD\458C%E9%\700%80%E\65%87%BA\45E4%B8%\x41D%E5%9\x42%BD%E4\45BA%8B%\x454%BB%B6" +,"z\x68\56wikipe\x64i\x61.org\57wiki/%\x455%B9%B\x46%E5%B7\459E%E5%\x428%82%E\66%96%B0\45E5%A1%\718%E4%B\x41%8B%E4\45BB%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E5%9B\45BD%E4%\x42F%9D" +,"z\x68\56wikipe\x64i\x61.org\52%E9%83\45AD%E4%\x42C%AF%E\71%9B%84" +,"z\x68\56wikipe\x64i\x61.org\52%E5%9B\45BD%E9%\719%85%E\67%89%B9\45E8%B5%\x416%E7%B\x42%84%E7\45BB%87" +,"z\x68\56wikipe\x64i\x61.org\52%E5%9B\45BD%E5%\706%85%E\65%AE%89\45E5%85%\x418%E4%B\x46%9D%E5\458D%AB%\x456%94%A\x46%E9%98\459F" +,"z\x68\56wikipe\x64i\x61.org\52%E5%9C\458B%E5%\70B%99%E\71%99%A2\45E9%98%\x422%E7%A\x46%84%E5\4592%8C%\x458%99%9\65%E7%90\4586%E9%\702%AA%E\66%95%99\45E5%95%\70F%E9%A\61%8C%E8\45BE%A6%\x455%85%A\x43%E5%AE\45A4" +,"z\x68\56wikipe\x64i\x61.org\52%E9%9F\45A9%E4%\x428%9C%E\66%96%B9" +,"z\x68\56wikipe\x64i\x61.org\52%E8%B4\45BA%E5%\71B%BD%E\65%BC%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E4%BD\4595%E4%\x42F%8A%E\64%BB%81" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B2\45B3%E6%\x41E%87" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B4\45AA%E5%\713%B2%E\65%8B%9D" +,"z\x68\56wikipe\x64i\x61.org\52%E4%BE\45AF%E5%\x42E%B7%E\65%BB%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E4%BE\45AF%E8%\x425%9B%E\65%9B%A0\45E6%B1%\71F" +,"z\x68\56wikipe\x64i\x61.org\52%E4%BE\45AF%E5%\x42E%B7%E\65%81%A5" +,"z\x68\56wikipe\x64i\x61.org\52%E8%83\45A1%E4%\x42D%B3_%\6281973%\x455%B9%B\64%29" +,"z\x68\56wikipe\x64i\x61.org\52%E8%83\45A1%E4%\x42D%B3_%\628%E7%A\64%BE%E6\45B4%BB%\x455%8B%9\65%E5%AE\45B6%29" +,"z\x68\56wikipe\x64i\x61.org\52%E8%83\45A1%E4%\x42D%B3_%\628%E7%A\64%BE%E6\459C%83%\x456%B4%B\x42%E5%8B\4595%E5%\x41E%B6%29" +,"z\x68\56wikipe\x64i\x61.org\52%E8%83\45A1%E9%\714%A6%E\66%B6%9B" +,"z\x68\56wikipe\x64i\x61.org\52%E8%83\45A1%E5%\x429%B3_%\628%E4%B\x44%9C%E5\45AE%B6%\629" +,"z\x68\56wikipe\x64i\x61.org\52%E5%91\45BC%E5%\71C%96%E\65%85%8B\45E5%9C%\716" +,"z\x68\56wikipe\x64i\x61.org\52%E8%83\45A1%E6%\x428%A9%E\64%BD%93\45E5%88%\x426" +,"z\x68\56wikipe\x64i\x61.org\52%E8%83\45A1%E8%\700%80%E\71%82%A6" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8D\458E%E5%\71B%BD%E\71%94%8B" +,"z\x68\56wikipe\x64i\x61.org\52%E8%8F\45AF%E5%\71C%8B%E\71%8B%92" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8D\458E%E5%\x414%8F%E\66%96%87\45E6%91%\718" +,"z\x68\56wikipe\x64i\x61.org\52%E8%8A\45B1%E5%\71B%AD%E\70%BD%AF\45E4%BB%\x426" +,"z\x68\56wikipe\x64i\x61.org\52%E8%8A\45B1%E5%\71B%AD%E\67%BD%91" +,"z\x68\56wikipe\x64i\x61.org\52%E9%BB\4584%E7%\710%A6" +,"z\x68\56wikipe\x64i\x61.org\52%E9%BB\4583%E7%\710%A6" +,"z\x68\56wikipe\x64i\x61.org\52%E9%BB\4583%E9%\71B%80%E\70%A1%8C\45E5%8B%\715" +,"z\x68\56wikipe\x64i\x61.org\52%E9%BB\4584%E4%\x428%87%E\71%87%8C" +,"z\x68\56wikipe\x64i\x61.org\52%E9%BB\4583%E6%\71B%89%E\66%95%8F" +,"z\x68\56wikipe\x64i\x61.org\52%E5%9B\459E%E8%\709%AF%E\67%8E%89" +,"z\x68\56wikipe\x64i\x61.org\52%E9%9C\458D%E8%\70B%B1%E\66%9D%B1" +,"z\x68\56wikipe\x64i\x61.org\52%E5%AF\4582%E8%\x41D%B7" +,"z\x68\56wikipe\x64i\x61.org\52%E6%BF\4580%E6%\x425%81%E\64%B8%AD\45E5%9B%\x42D" +,"z\x68\56wikipe\x64i\x61.org\52%E5%98\4589%E9%\71D%96" +,"z\x68\56wikipe\x64i\x61.org\52%E5%AE\45B6%E4%\x429%90%E\67%A6%8F" +,"z\x68\56wikipe\x64i\x61.org\52%E8%B3\4588%E6%\705%B6%E\66%9E%97" +,"z\x68\56wikipe\x64i\x61.org\52%E8%B4\45BE%E5%\x42A%86%E\66%9E%97" +,"z\x68\56wikipe\x64i\x61.org\52%E5%BB\45BA%E5%\71B%BD%E\71%97%A8\45E4%BA%\70B%E4%B\x42%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A7\459C%E7%\x42B%B4%E\65%B9%B3" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B1\459F%E6%\x423%BD%E\66%B0%91" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B1\459F%E6%\x42E%A4%E\66%B0%91" +,"z\x68\56wikipe\x64i\x61.org\52%E9%87\4591%E7%\71B%BE%E\65%B7%A5\45E7%A8%\70B" +,"z\x68\56wikipe\x64i\x61.org\52%BD%F0\45B6%DC%\x429%A4%B\63%CC" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B9\459D%E8%\x41F%84%E\65%85%B1\45E4%BA%\x417%E5%8\65%9A" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B9\459D%E8%\x419%95%E\65%85%B1\45E7%94%\x412%E9%B\x42%A8" +,"z\x68\56wikipe\x64i\x61.org\52%E5%B7\45A8%E6%\x425%AA2%\x455%9E%8\x42%E6%BD\459C%E5%\x420%84%E\65%BC%B9\45E9%81%\713%E5%A\x46%BC%E5\45BC%B9" +,"z\x68\56wikipe\x64i\x61.org\52%E8%A6\45BA%E5%\71B%8A%E\66%B4%BE" +,"z\x68\56wikipe\x64i\x61.org\52%E7%BB\459D%E9%\x413%9F" +,"z\x68\56wikipe\x64i\x61.org\52%E6%8B\4589%E5%\70D%9C%E\66%A5%9E\45E5%AF%\x42A" +,"z\x68\56wikipe\x64i\x61.org\52%E6%8B\4589%E8%\710%A8" +,"z\x68\56wikipe\x64i\x61.org\52%E9%BB\458E%E5%\x41E%89%E\65%8F%8B" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\458E%E9%\715%BF%E\66%98%A5" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\458E%E9%\715%B7%E\66%98%A5" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\458E%E6%\x424%AA%E\65%BF%97" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\458E%E5%\705%8B%E\65%BC%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\458E%E9%\x425%AC" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\458E%E9%\x429%8F" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\458E%E7%\711%9E%E\67%8E%AF" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\458E%E7%\711%9E%E\67%92%B0" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\458E%E6%\705%8E%E\64%B9%8B" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8E\4586%E5%\70F%B2%E\67%9A%84\45E4%BC%\x414%E5%8\x46%A3" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\458E%E8%\70B%B1%E\66%B5%A9" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\458E%E6%\x42A%90%E\66%BD%AE" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\458E%E5%\70D%93%E\64%BA%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E5%BB\4596%E4%\x42A%A6%E\66%AD%A6" +,"z\x68\56wikipe\x64i\x61.org\52%E8%93\45AE%E8%\70A%B1%E\67%94%9F" +,"z\x68\56wikipe\x64i\x61.org\52%E6%A2\4581%E5%\71B%BD%E\71%9B%84" +,"z\x68\56wikipe\x64i\x61.org\52%E6%A2\4581%E5%\71C%8B%E\71%9B%84" +,"z\x68\56wikipe\x64i\x61.org\52%E9%9B\45B6%E5%\705%AB%E\65%AE%AA\45E7%AB%\x410" +,"z\x68\56wikipe\x64i\x61.org\52%E4%BB\45A4%E8%\x420%B7" +,"z\x68\56wikipe\x64i\x61.org\52%E4%BB\45A4%E8%\x41E%A1%E\65%88%92" +,"z\x68\56wikipe\x64i\x61.org\52%E5%88\4598%E5%\x41E%BE%E\71%9B%81" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8A\4589%E5%\709%9B_(\45E6%B0%\711%E9%8\61%8B%E4\45BA%BA%\x455%A3%A\x42)" +,"z\x68\56wikipe\x64i\x61.org\52%E5%88\4598%E6%\705%A7%E\65%8D%BF" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8A\4589%E6%\705%A7%E\65%8D%BF" +,"z\x68\56wikipe\x64i\x61.org\52%E5%88\4598%E6%\x427%87" +,"z\x68\56wikipe\x64i\x61.org\52%E5%85\45AD%E5%\71B%9B" +,"zh\55yu\x65.wi\x6bipedi\x61\56org*%E\65%85%AD\45E5%9B%\71B%E4%B\x41%8B%E4\45BB%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8A\4589%E6%\71B%89%E\66%B3%A2" +,"z\x68\56wikipe\x64i\x61.org\52%E5%88\4598%E5%\x42B%B6%E\64%B8%9C" +,"z\x68\56wikipe\x64i\x61.org\52%E5%88\4598%E4%\x42A%91%E\65%B1%B1" +,"z\x68\56wikipe\x64i\x61.org\52%E7%BE\4585%E5%\x429%B9" +,"z\x68\56wikipe\x64i\x61.org\52%E7%BE\4585%E5%\x429%B2" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B4\459B%E6%\x411%91%E\66%A3%AE\45E6%A0%\x42C" +,"z\x68\56wikipe\x64i\x61.org\52%E7%B6\45A0%E5%\x413%A9%C\62%B7%E8\458A%B1%\x455%AD%A\63%E8%AD\45B7%E8%\708%AA" +,"z\x68\56wikipe\x64i\x61.org\52%E7%BB\45BF%E5%\71D%9D%C\62%B7%E8\458A%B1%\x455%AD%A\63%E6%8A\45A4%E8%\708%AA" +,"z\x68\56wikipe\x64i\x61.org\57wiki/%\x657%bb%b\x66%e5%9d\459d%e5%\x618%98" +,"z\x68\56wikipe\x64i\x61.org\52%E9%A6\45AC%E4%\x428%89%E\65%AE%B6\45E5%A5%\x423%E5%A\x44%90%E5\458B%9E%\x456%95%9\71%E6%89\4580" +,"z\x68\56wikipe\x64i\x61.org\52%E9%A6\45AC%E8%\70B%B1%E\64%B9%9D" +,"z\x68\56wikipe\x64i\x61.org\52%E7%BE\458E%E5%\71B%BD%E\65%9B%BD\45E5%AE%\x426%E6%B\60%91%E4\45B8%BB%\x455%9F%B\x41%E9%87\4591%E4%\x42C%9A" +,"z\x68\56wikipe\x64i\x61.org\52%E6%95\458F%E6%\704%9F%E\64%BA%BA\45E5%A3%\x41B" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B0\4591%E9%\716%93%E\64%BA%BA\45E6%AC%\70A%E9%9\71%A3%E7\45B7%9A" +,"z\x68\56wikipe\x64i\x61.org\52%E6%95\458F%E7%\70F%A0%E\66%9E%97\45E5%AF%\x42A" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B0\4591%E4%\x428%BB%E\65%A5%B3\45E7%A5%\71E" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B0\4591%E4%\x428%BB%E\71%BB%A8\x5f(%E9%A\66%99%E6\45B8%AF)" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B0\4591%E4%\x428%BB%E\66%AD%8C\45E8%81%\x422%E7%8\x44%BB%E4\45B8%AD%\x458%8F%AF" +,"z\x68\56wikipe\x64i\x61.org\52%E8%8C\4589%E8%\70E%89%E\70%8A%B1\45E9%9D%\x419%E5%9\61%BD" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8D\4597%E6%\716%B9%E\71%83%BD\45E5%B8%\702%E6%8\x41%A5*" +,"z\x68\56wikipe\x64i\x61.org\57wiki/%\x455%86%8\65%E8%92\4599%E5%\70F%A4%E\66%8A%97\45E8%AE%\x41E" +,"z\x68\56wikipe\x64i\x61.org\57zh-hk/\45E5%86%\705%E8%9\62%99%E5\458F%A4%\x456%8A%9\67%E8%AE\45AE" +,"z\x68\56wikipe\x64i\x61.org\57zh-tw/\45E5%86%\705%E8%9\62%99%E5\458F%A4%\x456%8A%9\67%E8%AE\45AE" +,"z\x68\56wikipe\x64i\x61.org\52%E5%86\4585%E8%\712%99%E\65%8F%A4\45E4%BA%\x42A%E6%B\60%91%E5\4585%9A" +,"z\x68\56wikipe\x64i\x61.org\52%E5%85\45A7%E8%\712%99%E\65%8F%A4\45E4%BA%\x42A%E6%B\60%91%E9\45BB%A8" +,"z\x68\56wikipe\x64i\x61.org\52%E5%AE\4581%E7%\70E%9B%E\66%B4%BE" +,"z\x68\56wikipe\x64i\x61.org\52%E7%89\459B%E5%\70D%9A%E\67%BD%91" +,"z\x68\56wikipe\x64i\x61.org\52%E8%AF\45BA%E8%\x424%9D%E\65%B0%94\45E5%92%\70C%E5%B\71%B3%E5\45A5%96" +,"z\x68\56wikipe\x64i\x61.org\52%E6%8C\45AA%E5%\x418%81" +,"z\x68\56wikipe\x64i\x61.org\52%E7%9B\4598%E5%\70F%A4%E\64%B9%90\45E9%98%\71F" +,"z\x68\56wikipe\x64i\x61.org\52%E5%BD\45AD%E4%\x428%BD%E\65%AA%9B" +,"z\x68\56wikipe\x64i\x61.org\52%E7%A0\45B4%E7%\x42D%91" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B5\45A6%E5%\x42F%97%E\65%BC%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\4583%E4%\x428%80%E\71%81%8A\45E8%A1%\70C" +,"z\x68\56wikipe\x64i\x61.org\52%E7%A7\45A6%E5%\71F%8E%E\67%9B%91\45E7%8B%\x421" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B8\4585%E6%\71C%9D" +,"z\x68\56wikipe\x64i\x61.org\52%E5%85\45A8%E7%\710%83%E\70%97%8F\45E4%BA%\x42A%E7%8\71%B9%E5\4588%AB%\x455%A4%A\67%E4%BC\459A" +,"z\x68\56wikipe\x64i\x61.org\52%C8%BC\45C9%D5%\x436%BF" +,"z\x68\56wikipe\x64i\x61.org\52%E7%87\4583%E7%\703%A7%E\67%93%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E7%83\45AD%E6%\x41F%94%E\65%A8%85" +,"z\x68\56wikipe\x64i\x61.org\52%E7%86\45B1%E6%\x41F%94%E\65%A9%AD" +,"z\x68\56wikipe\x64i\x61.org\52%E7%91\459E%E5%\705%B8" +,"z\x68\56wikipe\x64i\x61.org\52%E8%90\45A8%E8%\x42F%A6%E\66%B4%BE" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\4589%E5%\x429%B4%E\70%87%AA\45E7%84%\x426%E7%8\61%BE%E5\45AE%B3" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\4589%E9%\700%80" +,"z\x68\56wikipe\x64i\x61.org\52%E8%89\45B2%E6%\70B%89%E\65%AF%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E5%9C\45A3%E9%\71B%84%E\67%94%98\45E5%9C%\x420" +,"z\x68\56wikipe\x64i\x61.org\52%E7%9B\459B%E9%\71B%AA" +,"z\x68\56wikipe\x64i\x61.org\57zh-cn/\45E4%B8%\716%E7%9\65%8C%E5\4590%84%\x456%94%B\x46%E6%9D\4583%E5%\70F%97%E\66%89%BF\45E8%AE%\x414%E7%8\x41%B6%E5\4586%B5%\x455%88%9\67%E8%A1\45A8" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\4596%E7%\715%8C%E\67%BB%8F\45E6%B5%\70E%E5%A\x46%BC%E6\458A%A5" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\4596%E7%\715%8C%E\67%BB%B4\45E5%90%\x42E%E5%B\60%94%E5\45A4%A7%\x454%BC%9\x41!--Shi\40Jie We\x69 Wu Er\40Qing N\x69an Dai\40Biao D\x61 Hui" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\4596%E7%\715%8C%E\67%BB%B4\45E5%90%\x42E%E5%B\60%94%E9\459D%92%\x455%B9%B\64%E4%BB\45A3%E8%\x411%A8%E\65%A4%A7\45E4%BC%\71A" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\4596%E7%\715%8C%E\67%B6%AD\45E5%90%\x42E%E7%8\70%BE%E4\45BB%A3%\x458%A1%A\70%E5%A4\45A7%E6%\71C%83" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8D\4581%E5%\71B%9B%E\64%B8%96\45E8%BE%\x42E%E8%B\65%96" +,"z\x68\56wikipe\x64i\x61.org\52%E5%B8\4588%E6%\x426%9B" +,"z\x68\56wikipe\x64i\x61.org\52%E5%B8\45AB%E6%\x42F%A4" +,"z\x68\56wikipe\x64i\x61.org\52%E9%87\458A%E6%\718%9F%E\64%BA%91" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8F\45B8%E5%\x42E%92%E\65%8D%8E" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8F\45B8%E5%\x42E%92%E\70%8F%AF" +,"z\x68\56wikipe\x64i\x61.org\52%E5%9B\459B%E4%\x42A%94%E\70%A1%8C\45E5%8B%\715" +,"z\x68\56wikipe\x64i\x61.org\52%E5%AE\458B%E5%\x42D%AC%E\65%BD%AC" +,"z\x68\56wikipe\x64i\x61.org\52%E5%AE\458B%E4%\x42B%BB%E\67%A9%B7" +,"z\x68\56wikipe\x64i\x61.org\52%E5%AE\458B%E4%\x42B%BB%E\67%AA%AE" +,"z\x68\56wikipe\x64i\x61.org\52%E8%8B\458F%E5%\x41E%B6%E\65%B1%AF\45E4%BA%\70B%E4%B\x42%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E5%AD\4599%E6%\716%87%E\65%B9%BF" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A1\4594%E5%\x420%94%E\65%AF%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E5%8F\45B0%E7%\701%A3%E\67%8D%A8\45E7%AB%\70B%E5%B\x42%BA%E5\459C%8B%\x458%81%A\x46%E7%9B\459F" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45AA%E5%\x41D%90%E\65%85%9A" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45AA%E5%\x41D%90%E\65%85%9A" +,"z\x68\56wikipe\x64i\x61.org\57zh-cn/\45E5%A4%\x41A%E5%A\x44%90%E5\4585%9A" +,"z\x68\56wikipe\x64i\x61.org\52%E8%B0\45AD%E4%\x42D%9C%E\64%BA%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E5%94\4590%E6%\71F%8F%E\66%A1%A5" +,"z\x68\56wikipe\x64i\x61.org\57zh/%E9\4599%B6%\x459%A9%B\67%E9%A9\45B9" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A9%E5%\x41E%89%E\71%97%A8\45E5%B9%\x42F%E5%9\x43%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A9%E5%\x41E%89%E\71%97%A8\45E6%AF%\70D%E4%B\x41%B2%E8\45BF%90%\x455%8A%A8" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A9%E5%\x41E%89%E\71%96%80\45E4%BA%\70B%E4%B\x42%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A9%E5%\x41E%89%E\71%97%A8\45E6%96%\707%E4%B\x42%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A9%E5%\x41E%89%E\71%96%80\45E6%96%\707%E4%B\x42%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A9%E5%\x41E%89%E\71%97%A8\45E8%87%\x41A%E7%8\64%9A%E4\45BA%8B%\x454%BB%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\45A9%E8%\711%AC" +,"z\x68\56wikipe\x64i\x61.org\57zh-cn/\x54or" +,"z\x68\56wikipe\x64i\x61.org\57zh-hk/\x54witter" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\4587%E9%\707%8C" +,"z\x68\56wikipe\x64i\x61.org\52%E7%8E\458B%E5%\706%9B%E\66%B6%9B" +,"z\x68\56wikipe\x64i\x61.org\52%E7%8E\458B%E6%\x418%82%E\66%B3%89" +,"z\x68\56wikipe\x64i\x61.org\52%E7%8E\458B%E7%\x41B%8B%E\65%86%9B" +,"z\x68\56wikipe\x64i\x61.org\52%E7%8E\458B%E5%\x422%90%E\65%B1%B1" +,"z\x68\56wikipe\x64i\x61.org\52%E7%8E\458B%E5%\70D%83%E\66%BA%90" +,"z\x68\56wikipe\x64i\x61.org\52%E7%8E\458B%E7%\x42B%B4%E\66%9E%97" +,"z\x68\56wikipe\x64i\x61.org\52%E7%8E\458B%E6%\71C%89%E\66%89%8D" +,"z\x68\56wikipe\x64i\x61.org\52%E9%AD\458F%E4%\x42A%AC%E\67%94%9F" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B1\45B6%E5%\x427%9D%E\65%A4%A7\45E5%9C%\x420%E9%9\x43%87" +,"z\x68\56wikipe\x64i\x61.org\52%E7%8E\458B%E7%\702%B3%E\67%AB%A0" +,"z\x68\56wikipe\x64i\x61.org\52%E7%8E\458B%E4%\x429%90%E\66%B3%89" +,"z\x68\56wikipe\x64i\x61.org\52%E7%8E\458B%E5%\70A%9B%E\71%9B%84" +,"z\x68\56wikipe\x64i\x61.org\52%E7%BD\4591%E7%\x42B%9C%E\70%AF%84\45E8%AE%\x42A%E5%9\61%98" +,"z\x68\56wikipe\x64i\x61.org\57wiki/%\x457%8E%8\x42%E5%A7\4593" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B1\45AA%E6%\x424%8B" +,"z\x68\56wikipe\x64i\x61.org\52%E7%8E\458B%E5%\705%86%E\65%9B%BD" +,"z\x68\56wikipe\x64i\x61.org\52%E5%94\45AF%E8%\709%B2" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A8\4581%E8%\x417%86%E\70%85%90\45E8%B4%\x415%E6%A\61%88" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B1\45B6%E5%\x427%9D%E\65%9C%B0\45E9%9C%\707" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B8\45A9%E5%\x41E%B6%E\65%AE%9D" +,"z\x68\56wikipe\x64i\x61.org\52%E6%B8\45A9%E4%\x42A%91%E\66%9D%BE" +,"z\x68\56wikipe\x64i\x61.org\57wiki/%\x456%96%8\67%E5%AD\4597%E7%\70B%B1" +,"z\x68\56wikipe\x64i\x61.org\57wiki/%\x456%96%8\67%E5%AD\4597%E7%\70D%84" +,"z\x68\56wikipe\x64i\x61.org\52%E5%90\45B4%E9%\702%A6%E\65%9B%BD" +,"z\x68\56wikipe\x64i\x61.org\52%E6%88\4591%E7%\71A%84%E\65%A5%8B\45E6%96%\717" +,"z\x68\56wikipe\x64i\x61.org\52%E5%90\45BE%E5%\x420%94%E\65%BC%80\45E5%B8%\70C" +,"z\x68\56wikipe\x64i\x61.org\52%E5%90\45BE%E7%\708%BE%E\71%96%8B\45E5%B8%\70C" +,"z\x68\56wikipe\x64i\x61.org\52%E6%97\45A0%E5%\71B%BD%E\67%95%8C\45E8%AE%\x420%E8%8\60%85" +,"z\x68\56wikipe\x64i\x61.org\52%E5%90\45B4%E5%\x42C%98%E\70%BE%BE" +,"z\x68\56wikipe\x64i\x61.org\52%E6%97\45A0%E7%\715%8C" +,"z\x68\56wikipe\x64i\x61.org\52%E7%83\458F%E5%\71D%8E%E\64%BA%8B\45E4%BB%\x426" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B9\458C%E9%\x422%81%E\66%9C%A8\45E9%BD%\710%E4%B\70%83%C2\45B7%E4%\x42A%94%E\66%9A%B4\45E5%8A%\71B%E4%B\x41%8B%E4\45BB%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E4%BA\4594%E6%\x41F%9B%E\70%9B%8B\45E4%BA%\70B%E4%B\x42%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E8%A5\45BF%E5%\70D%95%E\66%B0%91\45E4%B8%\x42B%E5%A\62%99" +,"z\x68\56wikipe\x64i\x61.org\52%E7%BF\4592%E8%\x42F%91%E\65%B9%B3" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B9\45A0%E8%\x42F%91%E\65%B9%B3" +,"z\x68\56wikipe\x64i\x61.org\52%E8%A5\45BF%E5%\70E%A2%E\70%AE%A1\45E5%88%\712" +,"z\x68\56wikipe\x64i\x61.org\52%E8%A5\45BF%E8%\717%8F" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B9\45A0%E4%\x42B%B2%E\65%8B%8B" +,"z\x68\56wikipe\x64i\x61.org\52%E5%A4\458F%E7%\711%AA%E\65%B7%B4" +,"z\x68\56wikipe\x64i\x61.org\52%E9%A6\4599%E6%\x428%AF%E\67%8D%A8\45E7%AB%\70B%E9%8\61%8B%E5\458B%95" +,"z\x68\56wikipe\x64i\x61.org\52%E9%A6\4599%E6%\x428%AF%E\66%B0%91\45E4%B8%\x42B%E9%B\x42%A8" +,"z\x68\56wikipe\x64i\x61.org\52%E9%A6\4599%E6%\x428%AF*%\x456%B0%9\61%E4%B8\45BB%E6%\x424%BE" +,"z\x68\56wikipe\x64i\x61.org\52%E5%B0\458F%E6%\718%AD%E\65%AF%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E8%B0\45A2%E5%\x42D%A6%E\71%A3%9E" +,"z\x68\56wikipe\x64i\x61.org\52%E8%BE\459B%E7%\701%8F%E\65%B9%B4" +,"z\x68\56wikipe\x64i\x61.org\52%E6%96\45B0%E7%\716%86%E\67%8B%AC\45E7%AB%\70B" +,"z\x68\56wikipe\x64i\x61.org\52%E6%96\45B0%E7%\716%86%E\67%8D%A8\45E7%AB%\70B" +,"z\x68\56wikipe\x64i\x61.org\52%E6%96\45B0%E5%\714%90%E\64%BA%BA\45E9%9B%\x42B%E8%A\66%96%E5\458F%B0" +,"z\x68\56wikipe\x64i\x61.org\52%E6%96\45B0%E9%\717%BB%E\70%87%AA\45E7%94%\x421%E6%8\x45%A0%E5\45A4%BA%\x458%80%85" +,"z\x68\56wikipe\x64i\x61.org\52%E6%98\459F%E4%\x42A%91%E\66%B3%95\45E5%B8%\708" +,"z\x68\56wikipe\x64i\x61.org\52%E7%86\458A%E7%\704%B1" +,"z\x68\56wikipe\x64i\x61.org\52%E5%BE\4590%E6%\709%8D%E\65%8E%9A" +,"z\x68\56wikipe\x64i\x61.org\52%E8%AE\45B8%E5%\x41E%B6%E\65%B1%AF" +,"z\x68\56wikipe\x64i\x61.org\52%E9%9B\45AA%E5%\x421%B1%E\67%8D%85\45E5%AD%\710%E6%9\67%97" +,"z\x68\56wikipe\x64i\x61.org\52%E8%A9\45A2%E5%\715%8F%E\70%99%95" +,"z\x68\56wikipe\x64i\x61.org\52%E5%9A\45B4%E5%\x41E%B6%E\65%85%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45A5%E5%\x41E%B6%E\65%85%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E9%98\458E%E6%\718%8E%E\65%A4%8D" +,"z\x68\56wikipe\x64i\x61.org\52%E6%9D\45A8%E4%\x42D%B3%E\70%A2%AD\45E8%AD%\x416%E6%A\61%88" +,"z\x68\56wikipe\x64i\x61.org\52%E6%A5\458A%E5%\x42B%BA%E\65%88%A9" +,"z\x68\56wikipe\x64i\x61.org\52%E8%9A\4581%E5%\70A%9B%E\67%A5%9E" +,"z\x68\56wikipe\x64i\x61.org\52%E9%9B\458D%E5%\712%8C%E\65%AE%AB" +,"z\x68\56wikipe\x64i\x61.org\52%E5%B9\45BD%E7%\701%B5%E\67%BD%91" +,"z\x68\56wikipe\x64i\x61.org\52%E4%BD\4599%E6%\71D%B0" +,"z\x68\56wikipe\x64i\x61.org\52%E4%BF\459E%E4%\x428%BD%E\70%90%8D" +,"z\x68\56wikipe\x64i\x61.org\52%E4%BF\459E%E6%\x41D%A3%E\65%A3%B0" +,"z\x68\56wikipe\x64i\x61.org\52%E8%A2\4581%E7%\x42A%A2%E\65%86%B0" +,"z\x68\56wikipe\x64i\x61.org\52%E8%A2\4581%E7%\x424%85%E\65%86%B0" +,"z\x68\56wikipe\x64i\x61.org\52%E8%97\458F%E4%\x42C%A0%E\64%BD%9B\45E6%95%\719" +,"z\x68\56wikipe\x64i\x61.org\52%E6%89\458E%E4%\x42B%80%E\64%BC%A6\45E5%B8%\703%E5%A\x46%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E5%BC\45A0%E5%\x42E%B7%E\66%B1%9F" +,"z\x68\56wikipe\x64i\x61.org\52%E5%BC\45A0%E9%\x41B%98%E\64%B8%BD" +,"z\x68\56wikipe\x64i\x61.org\52%E7%AB\45A0%E5%\718%89%E\65%91%BC\45E5%9B%\x42E%E5%8\65%8B%E5\459B%BE" +,"z\x68\56wikipe\x64i\x61.org\52%E5%BC\45A0%E7%\x41B%8B%E\66%98%8C" +,"z\x68\56wikipe\x64i\x61.org\52%E5%BC\45A0%E5%\71F%B9%E\70%8E%89" +,"z\x68\56wikipe\x64i\x61.org\52%E5%BC\45B5%E6%\716%87%E\65%85%89" +,"z\x68\56wikipe\x64i\x61.org\52%E7%AB\45A0%E8%\x419%92%E\65%92%8C" +,"z\x68\56wikipe\x64i\x61.org\52%E7%AB\45A0%E8%\x41F%92%E\65%92%8C" +,"z\x68\56wikipe\x64i\x61.org\52%E5%BC\45A0%E9%\712%B0" +,"z\x68\56wikipe\x64i\x61.org\52%E5%BC\45B5%E9%\708%BA" +,"z\x68\56wikipe\x64i\x61.org\52%E8%B5\45B5%E7%\x424%AB%E\71%98%B3" +,"z\x68\56wikipe\x64i\x61.org\52%E8%B6\4599%E7%\x424%AB%E\71%99%BD" +,"z\x68\56wikipe\x64i\x61.org\52%E5%93\45B2%E5%\x428%83%E\65%B0%8A\45E4%B8%\x429%E5%B\67%B4%E5\4591%BC%\x455%9B%B\x45%E5%85\458B%E5%\71B%BE" +,"z\x68\56wikipe\x64i\x61.org\52%E7%9C\459F%E5%\716%84%E\65%BF%8D" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\705%B1%E\64%B8%AD\45E5%A4%\x41E%E5%A\x45%A3%E4\45BC%A0%\x459%83%A8" +,"z\x68\56wikipe\x64i\x61.org\57wiki/%\x454%B8%A\x44%E5%9B\45BD" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71C%8B%E\66%8C%81\45E4%B8%\70D%E5%9\60%8C%E6\4594%BF%\x458%A6%8\x42%E8%80\4585%E5%\710%8D%E\65%96%AE" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71B%BD%E\65%A4%A7\45E9%99%\706%E5%B\60%81%E9\4594%81%\x457%BB%B\64%E5%9F\45BA%E5%\x41A%92%E\64%BD%93\45E4%BA%\70B%E4%B\x42%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71B%BD%E\65%A4%A7\45E9%99%\706%E5%B\60%81%E9\4594%81%\x457%BB%B\64%E5%9F\45BA%E7%\719%BE%E\67%A7%91\45E4%BA%\70B%E4%B\x42%B6" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71C%8B%E\65%A4%A7\45E9%99%\x428%E7%B\66%B2%E8\45B7%AF%\x455%B0%8\61%E9%8E\4596" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71B%BD%E\67%94%B5\45E8%A7%\706%E5%A\x45%A1%E6\459F%A5" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71B%BD%E\66%B3%9B\45E8%93%\71D%E8%8\61%94%E7\459B%9F" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71B%BD%E\65%85%B1\45E4%BA%\x417%E5%8\65%9A" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71C%8B%E\65%85%B1\45E7%94%\x412%E9%BB" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71B%BD%E\65%9F%BA\45E7%9D%\x413%E6%9\65%99%E5\458D%8F%\x454%BC%9A" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71B%BD%E\65%AE%B6\45E5%BA%\x41D%E6%9\65%99%E4\45BC%9A" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71C%8B%E\70%81%AF\45E9%82%\x416%E4%B\70%BB%E7\45BE%A9" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71B%BD%E\66%B0%91\45E4%B8%\x42B%E5%8\65%9A" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71B%BD%E\66%B0%91\45E4%B8%\x42B%E8%B\x46%90%E5\458A%A8" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71C%8B%E\66%B0%91\45E4%B8%\x42B%E9%8\61%8B%E5\458B%95" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71C%8B%E\70%8C%89\45E8%8E%\709%E8%8\x41%B1%E9\459D%A9%\x455%91%BD" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD*%E5\459B%BD%\x454%BA%B\x41%E6%9D\4583" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71B%BD%E\67%BD%91\45E7%BB%\71C%E8%B\x44%AF%E4\45BB%B6%\x458%BF%8\67%E6%BB\45A4%E5%\705%B3%E\71%94%AE\45E5%AD%\717%E5%8\70%97%E8\45A1%A8" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71B%BD%E\67%BD%91\45E7%BB%\71C%E5%A\x45%A1%E6\459F%A5" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\71C%8B%E\67%B6%B2\45E8%B7%\x41F%E5%A\x46%A9%E6\459F%A5" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E8%\70F%AF%E\64%BA%BA\45E6%B0%\711%E5%8\65%B1%E5\4592%8C%\x455%9C%8\x42%E4%BA\45BA%E6%\x41C%8A" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E8%\70F%AF%E\64%BA%BA\45E6%B0%\711%E5%8\65%B1%E5\4592%8C%\x455%9C%8\x42%E5%AF\45A9%E6%\71F%A5%E\70%BE%AD\45E5%BD%\719%E5%8\70%97%E8\45A1%A8" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\70D%8E%E\64%BA%BA\45E6%B0%\711%E5%8\65%B1%E5\4592%8C%\x455%9B%B\x44%E5%AE\45AA%E6%\x423%95" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E8%\70F%AF%E\66%B0%91\45E5%9C%\70B%E5%9\x43%8B%E6\45B0%91%\x455%A4%A\67%E6%9C\4583" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E8%\70F%AF%E\66%B0%91\45E5%9C%\70B%E6%B\62%BB%E8\4597%8F%\x456%AD%B\67%E5%8F\45B2" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E8%\70F%AF%E\66%B0%91\45E5%9C%\70B%E7%B\70%BD%E7\45B5%B1%\x455%BA%9C" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\70D%8E%E\64%BA%BA\45E6%B0%\711%E5%8\65%B1%E5\4592%8C%\x455%9B%B\x44%E7%BD\4591%E7%\x42B%9C%E\65%AE%A1\45E6%9F%\x415" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E8%\70F%AF%E\64%BA%BA\45E6%B0%\711%E5%8\65%B1%E5\4592%8C%\x455%9C%8\x42%E7%B6\45B2%E7%\x425%A1%E\65%AF%A9\45E6%9F%\x415" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E8%\70F%AF%E\64%BA%BA\45E6%B0%\711%E5%8\65%B1%E5\4592%8C%\x455%9C%8\x42%E6%B0\4591%E4%\x428%BB%E\71%81%8B\45E5%8B%\715" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E8%\70F%AF%E\64%BA%BA\45E6%B0%\711%E5%8\65%B1%E5\4592%8C%\x455%9C%8\x42%E5%AE\4597%E6%\715%99" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E6%\716%87%E\67%BB%B4\45E5%9F%\x42A%E7%9\71%BE%E7\45A7%91" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\x414%AE%E\70%AD%A6\45E8%A1%\71B%E5%B\61%80" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E5%\x414%AE%E\66%96%87\45E5%8C%\716%E9%9\x44%A9%E5\4591%BD%\x455%B0%8\x46%E7%BB\4584" +,"z\x68\56wikipe\x64i\x61.org\52%E4%B8\45AD%E8%\70B%B1%E\67%BA%8C\45E8%A8%\702%E8%9\67%8F%E5\458D%B0%\x456%A2%9\x44%E7%B4\4584" +,"z\x68\56wikipe\x64i\x61.org\52%E5%91\45A8%E6%\x42B%A8" +,"z\x68\56wikipe\x64i\x61.org\52%E5%91\45A8%E6%\x420%B8%E\65%BA%B7" +,"z\x68\56wikipe\x64i\x61.org\52%E8%BD\45AC%E6%\x423%95%E\70%BD%AE" +,"z\x68\56wikipe\x64i\x61.org\52%E8%87\45AA%E7%\714%B1%E\71%97%A8" +,"z\x68\56wikipe\x64i\x61.org\52%E8%87\45AA%E7%\714%B1%E\64%BA%9A\45E6%B4%\x422" +,"z\x68\56wikipe\x64i\x61.org\52%E5%AE\4597%E5%\716%80%E\65%B7%B4" +,"z\x68\56wikibo\x6fk\x73.org\52%E7%AA\4581%E7%\x410%B4%E\67%BD%91\45E7%BB%\71C%E5%A\x45%A1%E6\459F%A5" +,"go\x76.tw" +,"@@||tax.nat.gov.tw" +,"@@||moe.gov.tw" +,"@@||cwb.gov.tw" +,"@@||npm.gov.tw" +,"@@||yatsen.gov.tw" +,"@@||aec.gov.tw" +,"@@||mvdis.gov.tw" +,"@@||stdtime.gov.tw" +,"@@||nmmba.gov.tw" +,"@@||ntdmh.gov.tw" +,"@@||grb.gov.tw" +,"@@||tpde.aide.gov.tw" +,"@@||matsu-news.gov.tw" +,"@@||nerhl.gov.tw" +,"@@||dapu-house.gov.tw" +,"@@||vghtc.gov.tw" +,"@@||aide.gov.tw" +,"@@||hchcc.gov.tw" +,"@@||ntuh.gov.tw" +,"@@||nhri.gov.tw" +,"@@||nstm.gov.tw" +,"@@||ntsec.gov.tw" +,"@@||ner.gov.tw" +,"@@||nmtl.gov.tw" +,"@@||ntl.gov.tw" +,"@@||pet.gov.tw" +,"@@||khcc.gov.tw" +,"@@||nmmba.gov.tw" +,"@@||khms.gov.tw" +,"@@||wanfang.gov.tw" +,"@@||nict.gov.tw" +,"@@||arte.gov.tw" +,"@@||nmh.gov.tw" +,"@@||nmp.gov.tw" +,"@@||tphcc.gov.tw" +,"@@||iner.gov.tw" +,"@@||tncsec.gov.tw" +,"@@||nspo.gov.tw" +,"@@||aide.gov.tw" +,"@@||ncree.gov.tw" +,"@@||vghks.gov.tw" +,"@@||tchb.gov.tw" +,"@@||pabp.gov.tw" +,"@@||itrc.gov.tw" +,"@@||df.gov.tw" +,"@@||womenbusiness.nyc.gov.tw" +,"@@||gsn-cert.nat.gov.tw" +,"@@||kk.gov.tw" +,"@@||thbstc.gov.tw" +,"@@||comnews.gio.gov.tw" +,"@@||comnews.gio.gov.tw" +,"@@||klccab.gov.tw" +,"@@||yvtc.gov.tw" +,"@@||aftygh.gov.tw" +,"@@||klra.gov.tw" +,"@@||lungtanhr.gov.tw" +,"@@||taoyuan.gov.tw" +,"@@||hcc.gov.tw" +,"@@||nvri.gov.tw" +,"@@||nmvttc.gov.tw" +,"@@||kmh.gov.tw" +,"@@||patehr.gov.tw" +,"@@||nerch.gov.tw" +,"@@||kmseh.gov.tw" +,"@@||nertt.gov.tw" +,"@@||cycab.gov.tw" +,"@@||chukuang.gov.tw" +,"@@||gysd.nyc.gov.tw" +,"@@||cp-house.gov.tw" +,"@@||vghtpe.gov.tw" +,"@@||etraining.gov.tw" +,"@@||stag.gov.tw" +,"@@||bdhr.gov.tw" +,"@@||tcsac.gov.tw" +,"@@||imagesblog.gio.gov.tw" +,"@@||arte.gov.tw" +,"@@||dmtip.gov.tw" +,"@@||chccc.gov.tw" +,"@@||hengchuen.gov.tw" +,"@@||hsinchu-cc.gov.tw" +,"@@||921.gov.tw" +,"@@||ncdr.nat.gov.tw" +,"@@||4pppc.gov.tw" +,"@@||klsio.gov.tw" +,"@@||nici.nat.gov.tw" +,"@@||cromotc.nat.gov.tw" +,"@@||taitung-house.gov.tw" +,"17\64.14\62.1\60\65.153" +,"6\71\566\65.1\71.\6160" +,"@@||aliyun.com" +,"@@||baidu.com" +,"@@||jike.com" +,"@@||panguso.com" +,"@@||qq.com" +,"@@||sina.cn" +,"@@||sina.com.cn" +,"@@||sogou.com" +,"@@||so.com" +,"@@||soso.com" +,"@@||yahoo.cn" +,"@@||youdao.com" +,"@@||zhongsou.com" +,"@@|https://autoproxy.org" +,"@@|http://ime.baidu.jp" +,"||\x6cayerva\x75l\x74.com" +,"||\x67oagen\x74\56co" +,"||\x76erisig\x6e.com" +,"||\x6cist-ma\x6eag\x65.com" +,"||\x74ransla\x74e-ta\x62.\x63om" +,"||\x67ooglec\x6fd\x65.com" +,"||\x63loudfr\x6fn\x74.net" +,"||\x67o\x6f.gl" +]; + +var cnIpRange = [ +{},{0x10001:1,0x10002:1,0x10003:1,0x10008:4,0x1000c:4,0x10020:16,0x10030:16,0x10100:1,0x10102:1,0x10103:1,0x10104:4,0x10108:4,0x1010c:4,0x10110:16,0x10120:16,0x10130:16,0x10200:1,0x10201:1,0x10202:1,0x10204:1,0x10205:1,0x10206:1,0x10207:1,0x10208:1,0x10209:1,0x1020a:1,0x1020b:1,0x1020c:4,0x10210:16,0x10220:16,0x10230:16,0x10240:64,0x10300:256,0x10401:1,0x10402:1,0x10403:1,0x10404:1,0x10405:1,0x10406:1,0x10407:1,0x10408:4,0x1040c:4,0x10410:16,0x10420:16,0x10430:16,0x10440:64,0x10800:256,0x10a00:4,0x10a04:4,0x10a08:1,0x10a09:1,0x10a0b:1,0x10a0c:4,0x10a10:16,0x10a20:16,0x10a30:16,0x10a40:64,0x10c00:1024,0x11800:1024,0x11c00:1024,0x12d00:256,0x13000:256,0x13100:256,0x13200:256,0x13300:256,0x13800:1024,0x13c00:1024,0x14400:1024,0x15000:1024,0x15400:1024,0x15800:1024,0x15c00:256,0x15d00:256,0x15e00:256,0x15f00:256,0x17400:1024,0x1b400:1024,0x1b800:256,0x1b900:256,0x1bc00:1024,0x1c000:1024,0x1c400:1024,0x1ca00:256,0x1cb00:256,0x1cc00:1024},{},{},{},{},{},{},{},{},{},{},{},{},{0xe0000:4,0xe0004:4,0xe000c:4,0xe0100:4,0xe1000:1024,0xe1400:1024,0xe1800:1024,0xe1c00:1024,0xe6680:4,0xe669c:4,0xe6700:256,0xe6800:1024,0xe6c00:1024,0xe7000:1024,0xe7400:1024,0xe7800:1024,0xe7c00:1024,0xe8200:256,0xe8300:256,0xe8600:256,0xe8700:256,0xe9000:1024,0xe9400:1024,0xe9800:1024,0xe9c00:1024,0xec03c:4,0xec04c:4,0xec400:256,0xec500:256,0xecc00:256,0xecd00:256,0xed000:1024,0xed400:1024,0xed800:1024,0xedc00:1024},{},{},{},{},{},{},{},{},{},{},{},{},{0x1b0800:1024,0x1b0c00:1024,0x1b1000:1024,0x1b1400:1024,0x1b1800:1024,0x1b1c00:1024,0x1b22e8:4,0x1b22ec:4,0x1b2400:1024,0x1b2800:1024,0x1b2c00:1024,0x1b3228:4,0x1b322c:4,0x1b3280:64,0x1b32c0:64,0x1b3648:4,0x1b364c:4,0x1b3698:4,0x1b369c:4,0x1b36c0:64,0x1b62d0:16,0x1b62e0:16,0x1b62f0:16,0x1b6380:64,0x1b63c0:64,0x1b6700:256,0x1b6a80:64,0x1b6acc:4,0x1b6d20:16,0x1b6d30:16,0x1b7000:64,0x1b7050:16,0x1b7180:64,0x1b7300:64,0x1b7340:64,0x1b742c:4,0x1b7948:4,0x1b794c:4,0x1b7978:4,0x1b797c:4,0x1b8000:256,0x1b8100:256,0x1b83dc:4,0x1b9000:256,0x1b9400:1024,0x1b9800:1024,0x1b9c00:1024,0x1bb800:1024,0x1bbc00:1024,0x1bc000:1024,0x1bc400:1024,0x1bc800:1024,0x1bcc00:1024,0x1bd000:1024,0x1bd400:1024,0x1bd800:1024,0x1bdc00:1024,0x1be000:1024},{},{},{},{},{},{},{},{},{0x240000:4,0x240008:4,0x24000c:4,0x240010:16,0x240020:16,0x240030:16,0x240040:64,0x240080:64,0x2400c0:64,0x240100:256,0x240400:1024,0x241000:1024,0x241400:1024,0x241800:1024,0x241c00:1024,0x242000:1024,0x242400:256,0x242500:16,0x242510:16,0x242524:1,0x242525:1,0x242527:1,0x242528:4,0x24252c:4,0x242530:16,0x242800:1024,0x242c00:1024,0x243000:256,0x243100:256,0x243300:256,0x243800:1024,0x243c00:1024,0x246000:1024,0x246400:1024,0x246800:1024,0x246c00:1024,0x247000:1024,0x247400:1024,0x247800:1024,0x247c00:1024,0x248000:1024,0x248400:1024,0x248800:1024,0x248c00:1024,0x249000:1024,0x249400:1024,0x249800:1024,0x249c00:1024,0x24a000:1024,0x24a400:1024,0x24a800:1024,0x24ac00:1024,0x24b000:1024,0x24b400:1024,0x24b800:1024,0x24bc00:1024,0x24c000:1024,0x24c400:1024,0x24c800:1024,0x24cc00:1024,0x24d000:1024,0x24d400:1024,0x24d800:1024,0x24dc00:1024,0x24f800:1024,0x24fe00:256},{},{},{0x270000:1,0x270002:1,0x270003:1,0x270004:4,0x270008:4,0x27000c:4,0x270010:16,0x270020:16,0x270030:16,0x270040:64,0x270080:64,0x2700c0:64,0x274000:1024,0x274400:1024,0x274800:1024,0x274c00:1024,0x275000:1024,0x275400:1024,0x275800:1024,0x275c00:1024,0x278000:1024,0x278400:1024,0x278800:1024,0x278c00:1024,0x279000:1024,0x279400:1024,0x279800:1024,0x279c00:1024,0x27a000:1024,0x27a400:1024,0x27a800:1024,0x27ac00:1024,0x27b000:1024,0x27b400:1024,0x27b800:1024,0x27bc00:1024},{},{},{0x2a0000:4,0x2a0008:4,0x2a000c:4,0x2a0010:4,0x2a0014:4,0x2a0018:4,0x2a0020:16,0x2a0030:16,0x2a0080:64,0x2a00c0:64,0x2a0100:16,0x2a0110:16,0x2a0120:16,0x2a0130:4,0x2a0134:4,0x2a0138:4,0x2a0180:64,0x2a01c0:64,0x2a0400:1024,0x2a3000:256,0x2a3100:256,0x2a3200:256,0x2a3300:256,0x2a3400:1024,0x2a3800:1024,0x2a3e00:64,0x2a3e40:64,0x2a3e80:16,0x2a3e90:16,0x2a3ea0:16,0x2a3eb4:4,0x2a3eb8:4,0x2a3ebc:4,0x2a3f00:256,0x2a5000:256,0x2a5100:256,0x2a5340:16,0x2a5350:4,0x2a5358:4,0x2a535c:4,0x2a5360:16,0x2a5370:16,0x2a5380:64,0x2a53c0:64,0x2a5400:1024,0x2a5800:1024,0x2a5c00:1024,0x2a6040:16,0x2a6050:16,0x2a6060:4,0x2a6064:4,0x2a606c:4,0x2a6070:16,0x2a6080:64,0x2a60c0:64,0x2a6100:256,0x2a6300:64,0x2a6340:16,0x2a6350:16,0x2a6360:16,0x2a6370:4,0x2a6378:4,0x2a637c:4,0x2a6400:1024,0x2a7800:256,0x2a7900:256,0x2a7a00:256,0x2a7b00:16,0x2a7b10:16,0x2a7b24:4,0x2a7b28:4,0x2a7b2c:4,0x2a7b30:16,0x2a7b40:64,0x2a7b80:64,0x2a7bc0:64,0x2a8000:1024,0x2a8400:1024,0x2a8800:1024,0x2a8c00:1024,0x2a9c00:16,0x2a9c10:16,0x2a9c24:4,0x2a9c28:4,0x2a9c2c:4,0x2a9c30:16,0x2a9c40:64,0x2a9c80:64,0x2a9cc0:64,0x2a9d00:256,0x2a9e00:256,0x2a9f00:256,0x2aa000:1024,0x2aa400:1024,0x2aa800:1024,0x2aac00:1024,0x2ab000:1024,0x2ab400:1024,0x2ab800:256,0x2ab900:256,0x2aba00:256,0x2abb00:64,0x2abb40:16,0x2abb50:16,0x2abb60:16,0x2abb70:4,0x2abb74:4,0x2abb78:4,0x2abb80:64,0x2abbc0:64,0x2ac000:256,0x2ac100:256,0x2ac200:4,0x2ac204:4,0x2ac208:4,0x2ac20c:4,0x2ac210:16,0x2ac220:16,0x2ac230:16,0x2ac240:64,0x2ac280:64,0x2ac2c0:64,0x2ac300:256,0x2ac400:1024,0x2ac900:64,0x2ac940:64,0x2aca00:256,0x2acb00:256,0x2acc00:1024,0x2ad000:1024,0x2ad400:1024,0x2ad800:1024,0x2adc00:1024,0x2ae000:1024,0x2ae400:1024,0x2ae800:1024,0x2aec00:1024,0x2af000:64,0x2af040:64,0x2af080:64,0x2af0c0:64,0x2af200:256,0x2af300:256,0x2af400:1024,0x2af800:1024,0x2afc00:1024},{0x2bec00:4,0x2bec04:4,0x2bec08:4,0x2bec0c:4,0x2bec10:4,0x2bec14:4,0x2bec18:4,0x2bec1c:4,0x2bec20:4,0x2bec24:4,0x2bec28:4,0x2bec2c:4,0x2bec30:4,0x2bec34:4,0x2bec38:4,0x2bec3c:4,0x2bec40:4,0x2bec44:4,0x2bec48:4,0x2bec4c:4,0x2bec50:4,0x2bec54:4,0x2bec58:4,0x2bec5c:4,0x2bec60:4,0x2bec64:4,0x2bec68:4,0x2bec6c:4,0x2bec70:4,0x2bec74:4,0x2bec78:4,0x2bec7c:4,0x2bec80:4,0x2bec84:4,0x2bec88:4,0x2bec8c:4,0x2bec90:4,0x2bec94:4,0x2bec98:4,0x2bec9c:4,0x2beca0:4,0x2beca4:4,0x2beca8:4,0x2becac:4,0x2becb0:4,0x2becb4:4,0x2becb8:4,0x2becbc:4,0x2becc0:4,0x2becc4:4,0x2becc8:4,0x2beccc:4,0x2becd0:4,0x2becd4:4,0x2becd8:4,0x2becdc:4,0x2bece0:4,0x2bece4:4,0x2bece8:4,0x2becec:4,0x2becf0:4,0x2becf4:4,0x2becf8:4,0x2becfc:4,0x2bed00:4,0x2bed04:4,0x2bed08:4,0x2bed0c:4,0x2bed10:4,0x2bed14:4,0x2bed18:4,0x2bed1c:4,0x2bed20:4,0x2bed24:4,0x2bed28:4,0x2bed2c:4,0x2bed30:4,0x2bed34:4,0x2bed38:4,0x2bed3c:4,0x2bed40:4,0x2bed44:4,0x2bed48:4,0x2bed4c:4,0x2bed50:4,0x2bed54:4,0x2bed58:4,0x2bed5c:4,0x2bed60:4,0x2bed64:4,0x2bed68:4,0x2bed6c:4,0x2bed70:4,0x2bed74:4,0x2bed78:4,0x2bed7c:4,0x2bed80:4,0x2bed84:4,0x2bed88:4,0x2bed8c:4,0x2bed90:4,0x2bed94:4,0x2bed98:4,0x2bed9c:4,0x2beda0:4,0x2beda4:4,0x2beda8:4,0x2bedac:4,0x2bedb0:4,0x2bedb4:4,0x2bedb8:4,0x2bedbc:4,0x2bedc0:4,0x2bedc4:4,0x2bedc8:4,0x2bedcc:4,0x2bedd0:4,0x2bedd4:4,0x2bedd8:4,0x2beddc:4,0x2bede0:4,0x2bede4:4,0x2bede8:4,0x2bedec:4,0x2bedf0:4,0x2bedf4:4,0x2bedf8:4,0x2bedfc:4,0x2bee00:4,0x2bee04:4,0x2bee08:4,0x2bee0c:4,0x2bee10:4,0x2bee14:4,0x2bee18:4,0x2bee1c:4,0x2bee20:4,0x2bee24:4,0x2bee28:4,0x2bee2c:4,0x2bee30:4,0x2bee34:4,0x2bee38:4,0x2bee3c:4,0x2bee40:4,0x2bee44:4,0x2bee48:4,0x2bee4c:4,0x2bee50:4,0x2bee54:4,0x2bee58:4,0x2bee5c:4,0x2bee60:4,0x2bee64:4,0x2bee68:4,0x2bee6c:4,0x2bee70:4,0x2bee74:4,0x2bee78:4,0x2bee7c:4,0x2bee80:4,0x2bee84:4,0x2bee88:4,0x2bee8c:4,0x2bee90:4,0x2bee94:4,0x2bee98:4,0x2bee9c:4,0x2beea0:4,0x2beea4:4,0x2beea8:4,0x2beeac:4,0x2beeb0:4,0x2beeb4:4,0x2beeb8:4,0x2beebc:4,0x2beec0:4,0x2beec4:4,0x2beec8:4,0x2beecc:4,0x2beed0:4,0x2beed4:4,0x2beed8:4,0x2beedc:4,0x2beee0:4,0x2beee4:4,0x2beee8:4,0x2beeec:4,0x2beef0:4,0x2beef4:4,0x2beef8:4,0x2beefc:4,0x2bef00:4,0x2bef04:4,0x2bef08:4,0x2bef0c:4,0x2bef10:4,0x2bef14:4,0x2bef18:4,0x2bef1c:4,0x2bef20:4,0x2bef24:4,0x2bef28:4,0x2bef2c:4,0x2bef30:4,0x2bf000:4,0x2bf030:4,0x2bf038:4,0x2bf03c:4,0x2bf044:4,0x2bf048:4,0x2bf04c:4,0x2bf054:4,0x2bf07c:4,0x2bf080:4,0x2bf084:4,0x2bf088:4,0x2bf09c:4,0x2bf0a0:4,0x2bf0a4:4,0x2bf0a8:4,0x2bf0ac:4,0x2bf0b0:4,0x2bf0b4:4,0x2bf0b8:4,0x2bf0bc:4,0x2bf0c0:4,0x2bf0c4:4,0x2bf0c8:4,0x2bf0cc:4,0x2bf0d0:4,0x2bf0d4:4,0x2bf0d8:4,0x2bf0dc:4,0x2bf0ec:4,0x2bf0f0:4,0x2bf0f4:4,0x2bf0f8:4,0x2bf0fc:4,0x2bf100:4,0x2bf104:4,0x2bf108:4,0x2bf10c:4,0x2bf110:4,0x2bf114:4,0x2bf130:4,0x2bf14c:4,0x2bf150:4,0x2bf154:4,0x2bf158:4,0x2bf15c:4,0x2bf170:4,0x2bf1a8:4,0x2bf1ac:4,0x2bf1b0:4,0x2bf1b4:4,0x2bf1b8:4,0x2bf1c4:4,0x2bf1d0:4,0x2bf1d4:4,0x2bf1d8:4,0x2bf1dc:4,0x2bf1e0:4,0x2bf1e4:4,0x2bf1e8:4,0x2bf1ec:4,0x2bf1f0:4,0x2bf1f8:4,0x2bf1fc:4,0x2bf208:4,0x2bf20c:4,0x2bf210:4,0x2bf214:4,0x2bf218:4,0x2bf21c:4,0x2bf220:4,0x2bf22c:4,0x2bf230:4,0x2bf234:4,0x2bf238:4,0x2bf23c:4,0x2bf240:4,0x2bf248:4,0x2bf24c:4,0x2bf250:4,0x2bf254:4,0x2bf258:4,0x2bf25c:4,0x2bf260:4,0x2bf290:4,0x2bf294:4,0x2bf298:4,0x2bf29c:4,0x2bf2a0:4,0x2bf2a4:4,0x2bf2a8:4,0x2bf2b4:4,0x2bf2bc:4,0x2bf2c0:4,0x2bf2c4:4,0x2bf2cc:4,0x2bf2d8:4,0x2bf2dc:4,0x2bf2fc:4,0x2bf304:4,0x2bf308:4,0x2bf30c:4,0x2bf310:4,0x2bf318:4,0x2bf358:4,0x2bf380:4,0x2bf388:4,0x2bf390:4,0x2bf394:4,0x2bf39c:4,0x2bf3a8:4,0x2bf3b4:4,0x2bf3bc:4,0x2bf3e4:4,0x2bf3e8:4,0x2bf3f4:4,0x2bf600:4,0x2bf604:4,0x2bf608:4,0x2bf60c:4,0x2bf610:4,0x2bf614:4,0x2bf618:4,0x2bf61c:4,0x2bf620:4,0x2bf624:4,0x2bf628:4,0x2bf62c:4,0x2bf630:4,0x2bf634:4,0x2bf638:4,0x2bf63c:4,0x2bf640:4,0x2bf644:4,0x2bf648:4,0x2bf64c:4,0x2bf650:4,0x2bf654:4,0x2bf658:4,0x2bf65c:4,0x2bf660:4,0x2bf704:4,0x2bf708:4,0x2bf72c:4,0x2bf730:4,0x2bf744:4,0x2bf74c:4,0x2bf754:4,0x2bf758:4,0x2bf75c:4,0x2bf760:4,0x2bf764:4,0x2bf76c:4,0x2bf770:4,0x2bf794:4,0x2bf798:4,0x2bf7b0:4,0x2bf7b4:4,0x2bf7b8:4,0x2bf7bc:4,0x2bf7c4:4,0x2bf7c8:4,0x2bf7cc:4,0x2bf7d0:4,0x2bf7d4:4,0x2bf7d8:4,0x2bf7dc:4,0x2bf7e0:4,0x2bf7e4:4,0x2bf7e8:4,0x2bf7ec:4,0x2bf7f0:4,0x2bf7f4:4,0x2bf7f8:4,0x2bf7fc:4,0x2bf800:4,0x2bf804:4,0x2bf814:4,0x2bf81c:4,0x2bf830:4,0x2bf838:4,0x2bf84c:4,0x2bf850:4,0x2bf854:4,0x2bf858:4,0x2bf85c:4,0x2bf860:4,0x2bf864:4,0x2bf868:4,0x2bf86c:4,0x2bf870:4,0x2bf874:4,0x2bf878:4,0x2bf87c:4,0x2bf880:4,0x2bf884:4,0x2bf888:4,0x2bf88c:4,0x2bf890:4,0x2bf894:4,0x2bf8b0:4,0x2bf8b4:4,0x2bf8b8:4,0x2bf8bc:4,0x2bf8c0:4,0x2bf8c4:4,0x2bf8c8:4,0x2bf8cc:4,0x2bf8d0:4,0x2bf8e4:4,0x2bf8e8:4,0x2bf8f4:4,0x2bf900:4,0x2bf904:4,0x2bf908:4,0x2bf918:4,0x2bf978:4,0x2bf984:4,0x2bf988:4,0x2bf990:4,0x2bf994:4,0x2bf998:4,0x2bf99c:4,0x2bf9a0:4,0x2bf9a4:4,0x2bf9a8:4,0x2bf9c0:4,0x2bf9ec:4,0x2bfc28:4,0x2bfc30:4,0x2bfc38:4,0x2bfce0:4,0x2bfe00:4,0x2bfe04:4,0x2bfe08:4,0x2bfe18:4,0x2bfe24:4,0x2bfe2c:4,0x2bfe34:4,0x2bfe40:4,0x2bfe48:4,0x2bfe54:4,0x2bfe58:4,0x2bfe5c:4,0x2bfe64:4,0x2bfe68:4,0x2bfe70:4,0x2bfe74:4,0x2bfe80:4,0x2bfe88:4,0x2bfe8c:4,0x2bfe90:4,0x2bfe94:4,0x2bfe98:4,0x2bfe9c:4,0x2bfea8:4,0x2bfeac:4,0x2bfeb4:4,0x2bfeb8:4,0x2bfebc:4,0x2bfec0:4,0x2bfec4:4,0x2bfec8:4,0x2bfed0:4,0x2bfedc:4,0x2bfee0:4,0x2bfee4:4,0x2bfee8:4,0x2bfeec:4,0x2bfef0:4,0x2bfef8:4,0x2bfefc:4,0x2bff00:4,0x2bff04:4,0x2bff08:4,0x2bff10:4,0x2bff30:4,0x2bff3c:4,0x2bff40:4,0x2bff44:4,0x2bff48:4,0x2bff4c:4,0x2bff54:4,0x2bff60:4,0x2bff6c:4,0x2bff90:4,0x2bffa8:4,0x2bffb0:4,0x2bffb8:4,0x2bffc0:4,0x2bffc8:4,0x2bffcc:4,0x2bffd0:4,0x2bffd4:4,0x2bffe0:4,0x2bffe4:4,0x2bffe8:4,0x2bfff4:4},{},{0x2d4070:1,0x2d4071:1},{},{},{},{0x310400:1024,0x313300:256,0x313400:1024,0x314000:1024,0x314400:1024,0x314800:1024,0x314c00:1024,0x315000:1024,0x315400:1024,0x315800:1024,0x315c00:1024,0x317000:1024,0x317400:1024,0x317800:1024,0x318000:1,0x318002:1,0x318003:1,0x318c00:256,0x318d00:256,0x319800:1024,0x31d000:256,0x31d100:256,0x31d200:256,0x31d300:256,0x31dc00:1024,0x31e800:1024,0x31ef00:64,0x31efc0:64,0x31f6e0:16,0x31f6f0:16},{},{},{},{},{0x36de00:256,0x36df00:256},{},{},{},{0x3a0e00:256,0x3a0f00:256,0x3a1000:256,0x3a1100:64,0x3a1140:64,0x3a1180:64,0x3a11c0:64,0x3a1200:256,0x3a1300:256,0x3a1400:256,0x3a1500:256,0x3a1600:256,0x3a1700:256,0x3a1800:256,0x3a1900:256,0x3a1e00:256,0x3a1f00:256,0x3a2000:1024,0x3a2400:1024,0x3a2800:256,0x3a2900:256,0x3a2a00:256,0x3a2b00:256,0x3a2c00:1024,0x3a3000:1024,0x3a3400:1024,0x3a3800:256,0x3a3900:256,0x3a3a00:256,0x3a3b00:64,0x3a3b40:64,0x3a3b80:64,0x3a3bc0:64,0x3a3c00:1024,0x3a41e8:4,0x3a41ec:4,0x3a4200:256,0x3a4300:256,0x3a4480:64,0x3a44c0:64,0x3a5200:64,0x3a5240:64,0x3a5300:64,0x3a5340:64,0x3a5380:64,0x3a53c0:64,0x3a5740:64,0x3a6380:64,0x3a63c0:64,0x3a6400:256,0x3a6500:256,0x3a7400:1024,0x3a8000:1024,0x3a8400:1024,0x3a9000:256,0x3a9a00:256,0x3a9b00:256,0x3ac000:256,0x3ac100:256,0x3ac200:256,0x3ac300:256,0x3ac400:256,0x3ac500:256,0x3ac600:256,0x3ac700:256,0x3ac800:1024,0x3acc00:1024,0x3ad000:1024,0x3ad400:1024,0x3ad800:1024,0x3adc00:1024,0x3af000:256,0x3af100:256,0x3af200:256,0x3af300:256,0x3af400:256,0x3af500:256,0x3af600:256,0x3af700:256,0x3af800:1024,0x3afc00:1024},{0x3b2000:1024,0x3b2400:1024,0x3b2800:256,0x3b2900:256,0x3b2a00:256,0x3b2b00:256,0x3b2c00:1024,0x3b3000:256,0x3b3100:64,0x3b3140:64,0x3b3180:64,0x3b31c0:64,0x3b3200:256,0x3b3300:64,0x3b3340:64,0x3b3380:64,0x3b33c0:64,0x3b3400:1024,0x3b3800:1024,0x3b3c00:256,0x3b3d00:256,0x3b3e00:256,0x3b3f00:256,0x3b4000:1024,0x3b4400:1024,0x3b4800:256,0x3b4900:256,0x3b4a00:256,0x3b4b00:256,0x3b4c00:256,0x3b4d00:256,0x3b4e00:256,0x3b4f00:256,0x3b5000:256,0x3b5100:256,0x3b5200:256,0x3b5300:256,0x3b6b00:64,0x3b6b40:64,0x3b6b80:64,0x3b6bc0:64,0x3b6c00:256,0x3b6d00:256,0x3b6e00:256,0x3b6f00:256,0x3b9700:64,0x3b9740:64,0x3b9b00:256,0x3bac00:256,0x3bad00:256,0x3bae00:256,0x3baf00:256,0x3bbf00:64,0x3bbf40:64,0x3bbff0:16,0x3bc000:1024,0x3bc400:1024,0x3bc800:1024,0x3bcc00:1024,0x3bd000:1024,0x3bd400:1024,0x3bd800:1024,0x3bdc00:1024,0x3be000:1024,0x3be400:1024,0x3be800:1024,0x3bec00:1024,0x3bf000:1024,0x3bf400:1024,0x3bf800:1024,0x3bfc00:1024},{0x3c0000:1024,0x3c0400:1024,0x3c0800:256,0x3c0900:256,0x3c0a00:256,0x3c0b00:256,0x3c0c00:256,0x3c0d00:64,0x3c0d40:64,0x3c0d80:64,0x3c0dc0:64,0x3c0e00:256,0x3c0f00:256,0x3c1000:1024,0x3c1400:1024,0x3c1800:1024,0x3c1c00:256,0x3c1d00:256,0x3c1e00:256,0x3c1f00:256,0x3c3700:256,0x3c3f00:256,0x3ca000:256,0x3ca100:256,0x3ca200:256,0x3ca300:256,0x3ca400:256,0x3ca500:256,0x3ca600:256,0x3ca700:256,0x3ca800:1024,0x3cac00:1024,0x3cb000:1024,0x3cb400:1024,0x3cb800:1024,0x3cbc00:1024,0x3cc200:256,0x3cc300:256,0x3cc800:1024,0x3ccc00:256,0x3ccd00:256,0x3cce00:256,0x3ccf00:256,0x3cd000:1024,0x3cd400:1024,0x3cd800:256,0x3cd900:256,0x3cda00:256,0x3cdb00:256,0x3cdc00:1024,0x3ce800:256,0x3ce900:256,0x3ceb00:256,0x3cf580:64,0x3cf5c0:64,0x3cf700:256,0x3cfc00:256,0x3cfd80:64,0x3cfdc0:64,0x3cff00:256},{0x3d0450:4,0x3d0454:4,0x3d0458:4,0x3d045c:4,0x3d04b0:16,0x3d08a0:16,0x3d1c00:16,0x3d1c10:16,0x3d1c20:16,0x3d1c30:16,0x3d1c40:64,0x3d1d80:64,0x3d1dc0:16,0x3d1dd0:16,0x3d1de0:16,0x3d1df0:16,0x3d2d80:64,0x3d2de0:16,0x3d2f80:64,0x3d3000:1024,0x3d3400:256,0x3d3500:256,0x3d3600:256,0x3d3700:256,0x3d57c0:64,0x3d8000:256,0x3d8100:256,0x3d8200:256,0x3d8300:256,0x3d8400:256,0x3d8500:64,0x3d8540:64,0x3d8580:64,0x3d85c0:64,0x3d8600:64,0x3d8640:16,0x3d8650:16,0x3d8660:16,0x3d8670:16,0x3d8680:64,0x3d86c0:64,0x3d8700:256,0x3d8800:64,0x3d8840:64,0x3d8880:64,0x3d88c0:64,0x3d8900:64,0x3d8940:64,0x3d8980:64,0x3d89c0:64,0x3d8a00:64,0x3d8a40:64,0x3d8a80:64,0x3d8ac0:64,0x3d8b00:64,0x3d8b40:64,0x3d8b80:64,0x3d8bc0:64,0x3d8c00:1024,0x3d9000:1024,0x3d9400:256,0x3d9500:256,0x3d9600:256,0x3d9700:256,0x3d9800:256,0x3d9900:256,0x3d9a00:256,0x3d9b00:256,0x3d9c00:256,0x3d9d00:256,0x3d9e00:64,0x3d9e40:64,0x3d9e80:64,0x3d9ec0:64,0x3d9f00:64,0x3d9f40:64,0x3d9f80:64,0x3d9fc0:64,0x3da000:256,0x3da100:64,0x3da140:64,0x3da180:64,0x3da1c0:64,0x3da200:256,0x3da300:256,0x3da400:256,0x3da500:256,0x3da600:256,0x3da700:256,0x3da800:256,0x3da900:256,0x3daa00:256,0x3dab00:256,0x3dac00:1024,0x3db000:256,0x3db100:256,0x3db200:256,0x3db300:256,0x3db400:64,0x3db440:64,0x3db480:64,0x3db4c0:64,0x3db500:256,0x3db600:256,0x3db700:256,0x3db800:1024,0x3dbc00:256,0x3dbd00:64,0x3dbd40:64,0x3dbd80:64,0x3dbdc0:64,0x3dbe00:256,0x3dbf00:256,0x3de800:1024,0x3dec00:256,0x3ded00:256,0x3df000:1024},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{0x650000:4,0x650100:4,0x6502ac:4,0x650400:1024,0x651000:1024,0x651400:1024,0x651800:1024,0x651c00:1024,0x652000:1024,0x652400:1024,0x652800:1024,0x652c00:1024,0x653000:256,0x653100:256,0x653238:4,0x653400:256,0x653564:4,0x653600:256,0x6537e0:4,0x6537e4:4,0x654000:1024,0x654400:1024,0x654800:1024,0x654c00:256,0x654d00:256,0x654e00:4,0x654e20:16,0x654e30:16,0x655000:1024,0x655400:1024,0x655800:1024,0x655c00:1024,0x656000:4,0x656004:4,0x656008:4,0x656010:16,0x656080:64,0x6560c0:64,0x656360:16,0x656370:16,0x656540:16,0x656550:16,0x656564:1,0x656566:1,0x656567:1,0x656568:4,0x65656c:4,0x656570:16,0x656640:16,0x656650:16,0x656664:1,0x656665:1,0x656666:1,0x656668:4,0x65666c:4,0x656670:16,0x656800:1024,0x656e40:16,0x656e50:16,0x656e60:16,0x656e74:4,0x656e78:4,0x656e7c:4,0x657800:1024,0x657c00:256,0x657d00:256,0x657e00:256,0x658000:4,0x658008:4,0x65800c:4,0x658010:16,0x658020:16,0x658030:16,0x658100:256,0x658200:256,0x658300:256,0x658400:1024,0x659000:1024,0x659400:1024,0x659800:1024,0x659c00:1024,0x65c000:1024,0x65c400:1024,0x65c800:256,0x65c900:256,0x65cb80:16,0x65cb90:16,0x65cba0:4,0x65cba4:4,0x65cbac:4,0x65cbb0:16,0x65cc00:1024,0x65e000:1024,0x65e400:1024,0x65e800:256,0x65e900:256,0x65ea40:4,0x65ea44:4,0x65ea4c:4,0x65ea50:16,0x65ea60:16,0x65ea70:16,0x65ec00:1024,0x65f000:1024,0x65f400:1024,0x65f800:256,0x65f900:256,0x65fb00:4,0x65fb08:4,0x65fb0c:4,0x65fb10:16,0x65fb20:16,0x65fb30:16,0x65fb40:64,0x65fb80:64,0x65fbc0:64,0x65fc00:256,0x65fd00:256,0x65fe00:256},{},{0x670108:4,0x670114:4,0x670118:4,0x670148:4,0x670158:4,0x6701a8:4,0x67026c:4,0x67029c:4,0x6702a4:4,0x6702c8:4,0x6702cc:4,0x6702d0:4,0x6702d4:4,0x670354:4,0x670358:4,0x67035c:4,0x670360:4,0x670364:4,0x670368:4,0x67036c:4,0x670370:4,0x670374:4,0x670378:4,0x67037c:4,0x670380:4,0x670384:4,0x670388:4,0x67038c:4,0x670394:4,0x670398:4,0x67039c:4,0x670438:4,0x6704a8:4,0x6704b8:4,0x670524:4,0x670534:4,0x670538:4,0x6705fc:4,0x67064c:4,0x6706dc:4,0x670704:4,0x67071c:4,0x6707d4:4,0x6707d8:4,0x6707dc:4,0x670804:4,0x670808:4,0x670820:4,0x670834:4,0x67086c:4,0x67089c:4,0x6708c8:4,0x6708cc:4,0x6708dc:4,0x670998:4,0x6709f8:4,0x6709fc:4,0x670a00:4,0x670a10:4,0x670a54:4,0x670a6f:1,0x670a8c:4,0x670bb4:4,0x670c20:4,0x670c44:4,0x670c88:4,0x670cb8:4,0x670ce8:4,0x670d7c:4,0x670d90:4,0x670dc4:4,0x670df4:4,0x670e54:4,0x670e70:4,0x670e84:4,0x670e88:4,0x670e9c:4,0x670ef0:4,0x670f04:4,0x670f08:4,0x670f10:4,0x670f60:4,0x670fc8:4,0x671034:4,0x671050:4,0x671054:4,0x671058:4,0x67106c:4,0x67107c:4,0x671128:4,0x671178:4,0x6711a0:4,0x6711cc:4,0x6711e4:4,0x6712c0:4,0x6712d0:4,0x6712d4:4,0x6712e0:4,0x67130c:4,0x671328:4,0x67132c:4,0x671340:4,0x671344:4,0x671348:4,0x6713e8:4,0x67140c:4,0x671420:4,0x671470:4,0x671480:4,0x6714a0:4,0x6714f8:4,0x671570:4,0x671574:4,0x671588:4,0x67158c:4,0x6715b0:4,0x6715d0:4,0x6715f0:4,0x671600:4,0x671604:4,0x671608:4,0x67160c:4,0x671610:4,0x671614:4,0x671618:4,0x67161c:4,0x671620:4,0x671624:4,0x671628:4,0x67162c:4,0x671630:4,0x671634:4,0x671638:4,0x67163c:4,0x671640:4,0x671644:4,0x671648:4,0x67164c:4,0x671650:4,0x671654:4,0x671658:4,0x67165c:4,0x671664:4,0x671668:4,0x67166c:4,0x671670:4,0x671674:4,0x671678:4,0x67167c:4,0x6716bc:4,0x6716e4:4,0x6716fc:4,0x671708:4,0x671738:4,0x6717a0:4,0x6717a4:4,0x6717b0:4,0x6717e4:4,0x671874:4,0x671880:4,0x671890:4,0x6718b0:4,0x6718b8:4,0x6718dc:4,0x6718e4:4,0x6718f8:4,0x6718fc:4,0x671908:1,0x671909:1,0x671914:4,0x671918:4,0x67191c:4,0x671920:4,0x671924:4,0x671928:4,0x671930:4,0x671940:4,0x671944:4,0x671994:4,0x67199c:4,0x6719d8:4,0x671a00:4,0x671a40:4,0x671a9c:4,0x671aa0:4,0x671ae4:4,0x671af0:4,0x671b04:4,0x671b0c:4,0x671b18:4,0x671b38:4,0x671b60:4,0x671bd0:4,0x671bf0:4,0x671c04:4,0x671c08:4,0x671ccc:4,0x671d10:4,0x671d80:4,0x671d84:4,0x671d88:4,0x671e14:4,0x671e60:4,0x671e94:4,0x671ec8:4,0x671ed8:4,0x671ee4:4,0x671eec:4,0x671f00:4,0x671f30:4,0x671f34:4,0x671f38:4,0x671f3c:4,0x671f40:4,0x671f44:4,0x671f48:4,0x671f94:4,0x671fa0:4,0x671fa8:4,0x671fc8:4,0x672000:4,0x672004:4,0x672008:4,0x67200c:4,0x672010:4,0x672014:4,0x672018:4,0x67201c:4,0x672020:4,0x672024:4,0x672028:4,0x67202c:4,0x672030:4,0x672034:4,0x672038:4,0x67203c:4,0x672040:4,0x672044:4,0x672048:4,0x67204c:4,0x672050:4,0x672054:4,0x672058:4,0x67205c:4,0x672060:4,0x672064:4,0x672068:4,0x67206c:4,0x672070:4,0x672074:4,0x672078:4,0x67207c:4,0x672080:4,0x672084:4,0x672088:4,0x67208c:4,0x672090:4,0x672094:4,0x672098:4,0x67209c:4,0x6720a0:4,0x6720a4:4,0x6720a8:4,0x6720ac:4,0x6720b0:4,0x6720b4:4,0x6720b8:4,0x6720bc:4,0x6720c0:4,0x6720c4:4,0x6720c8:4,0x6720cc:4,0x6720d0:4,0x6720d4:4,0x6720d8:4,0x6720dc:4,0x6720e0:4,0x6720e4:4,0x6720e8:4,0x6720ec:4,0x6720f0:4,0x6720f4:4,0x6720f8:4,0x6720fc:4,0x672100:4,0x672104:4,0x672108:4,0x67210c:4,0x672110:4,0x672114:4,0x672118:4,0x67211c:4,0x672120:4,0x672124:4,0x672128:4,0x67212c:4,0x672130:4,0x672134:4,0x672138:4,0x67213c:4,0x672140:4,0x672144:4,0x672148:4,0x67214c:4,0x672150:4,0x672154:4,0x672158:4,0x67215c:4,0x672160:4,0x672164:4,0x672168:4,0x67216c:4,0x672170:4,0x672174:4,0x672178:4,0x67217c:4,0x672180:4,0x672184:4,0x672188:4,0x67218c:4,0x672190:4,0x672194:4,0x672198:4,0x67219c:4,0x6721a0:4,0x6721a4:4,0x6721a8:4,0x6721ac:4,0x6721b0:4,0x6721b4:4,0x6721b8:4,0x6721bc:4,0x6721c0:4,0x6721c4:4,0x6721c8:4,0x6721cc:4,0x6721d0:4,0x6721d4:4,0x6721d8:4,0x6721dc:4,0x6721e0:4,0x6721e4:4,0x6721e8:4,0x6721ec:4,0x6721f0:4,0x6721f4:4,0x6721f8:4,0x6721fc:4,0x672200:4,0x672204:4,0x672208:4,0x67220c:4,0x672210:4,0x672214:4,0x672218:4,0x67221c:4,0x672220:4,0x672224:4,0x672228:4,0x67222c:4,0x672230:4,0x672234:4,0x672238:4,0x67223c:4,0x672240:4,0x672244:4,0x672248:4,0x67224c:4,0x672250:4,0x672254:4,0x672258:4,0x67225c:4,0x672260:4,0x672264:4,0x672268:4,0x67226c:4,0x672270:4,0x672274:4,0x672278:4,0x67227c:4,0x672280:4,0x672284:4,0x672288:4,0x67228c:4,0x672290:4,0x672294:4,0x672298:4,0x67229c:4,0x6722a0:4,0x6722a4:4,0x6722a8:4,0x6722ac:4,0x6722b0:4,0x6722b4:4,0x6722b8:4,0x6722bc:4,0x6722c0:4,0x6722c4:4,0x6722c8:4,0x6722cc:4,0x6722d0:4,0x6722d4:4,0x6722d8:4,0x6722dc:4,0x6722e0:4,0x6722e4:4,0x6722e8:4,0x6722ec:4,0x6722f0:4,0x6722f4:4,0x6722f8:4,0x6722fc:4,0x672300:4,0x672304:4,0x672308:4,0x67230c:4,0x672310:4,0x672314:4,0x672318:4,0x67231c:4,0x672320:4,0x672324:4,0x672328:4,0x67232c:4,0x672330:4,0x672414:4,0x67241c:4,0x672424:4,0x672438:4,0x67243c:4,0x672440:4,0x672448:4,0x672460:4,0x672484:4,0x672488:4,0x6724a0:4,0x6724a4:4,0x6724a8:4,0x6724ac:4,0x6724b0:4,0x6724b4:4,0x6724b8:4,0x6724bc:4,0x6724c0:4,0x6724c4:4,0x6724c8:4,0x6724cc:4,0x6724d0:4,0x6724d4:4,0x6724d8:4,0x6724dc:4,0x6724e0:4,0x6724e4:4,0x6724e8:4,0x6724ec:4,0x6724f0:4,0x6724f4:4,0x672500:4,0x67250c:4,0x672510:4,0x672518:4,0x67252c:4,0x672534:4,0x672538:4,0x672548:4,0x672564:4,0x672568:4,0x67257c:4,0x672588:4,0x67258c:4,0x672590:4,0x672594:4,0x672598:4,0x67259c:4,0x6725a0:4,0x6725a4:4,0x6725ac:4,0x6725b0:4,0x6725d0:4,0x6725d4:4,0x6725d8:4,0x6725dc:4,0x6725f8:4,0x6725fc:4,0x672600:4,0x672620:4,0x672628:4,0x67262c:4,0x672638:4,0x67264c:4,0x672654:4,0x67265c:4,0x672660:4,0x672674:4,0x672684:4,0x67268c:4,0x6726dc:4,0x6726e0:4,0x6726e4:4,0x6726e8:4,0x6726fc:4,0x672710:4,0x672740:4,0x672758:4,0x672764:4,0x672768:4,0x67276c:4,0x672790:4,0x6727a0:4,0x6727a4:4,0x6727a8:4,0x6727ac:4,0x6727b0:4,0x6727b4:4,0x6727b8:4,0x6727bc:4,0x6727c8:4,0x6727cc:4,0x6727d0:4,0x6727d4:4,0x6727d8:4,0x6727dc:4,0x6727e0:4,0x6727e4:4,0x6727e8:4,0x67280c:4,0x672810:4,0x672814:4,0x672818:4,0x67281c:4,0x672820:4,0x672824:4,0x672828:4,0x67282c:4,0x672858:4,0x672864:4,0x672870:4,0x6728c0:4,0x6728d4:4,0x6728dc:4,0x6728e4:4,0x6728e8:4,0x6728ec:4,0x6728f0:4,0x6728f4:4,0x6728f8:4,0x6728fc:4,0x672900:4,0x672910:4,0x672934:4,0x672974:4,0x67e028:4,0x67e02c:4,0x67e03c:4,0x67e050:4,0x67e0dc:4,0x67e0e0:4,0x67e0e4:4,0x67e0e8:4,0x67e154:4,0x67e210:4,0x67e228:4,0x67e238:4,0x67e23c:4,0x67e250:4,0x67e274:4,0x67e284:4,0x67e29c:4,0x67e2b4:4,0x67e2c4:4,0x67e330:4,0x67e348:4,0x67e34c:4,0x67e350:4,0x67e364:4,0x67e378:4,0x67e384:4,0x67e388:4,0x67e3c4:4,0x67e3cc:4,0x67e3d4:4,0x67e3e4:4,0x67e40c:4,0x67e41c:4,0x67e444:4,0x67e458:4,0x67e480:4,0x67e4a0:4,0x67e4b0:4,0x67e4cc:4,0x67e4d0:4,0x67e4e4:4,0x67e4e8:4,0x67e514:4,0x67e588:4,0x67e594:4,0x67e5ac:4,0x67e5d4:4,0x67e5d8:4,0x67e5dc:4,0x67e5e4:4,0x67e5ec:4,0x67e5f0:4,0x67e600:4,0x67e61c:4,0x67e628:4,0x67e62c:4,0x67e660:4,0x67e6c4:4,0x67e6c8:4,0x67e6cc:4,0x67e6d4:4,0x67e6ec:4,0x67e710:4,0x67e714:4,0x67e740:4,0x67e744:4,0x67e790:4,0x67e7b4:4,0x67e7b8:4,0x67e7f4:4,0x67e804:4,0x67e890:4,0x67e8d4:4,0x67e904:4,0x67e92c:4,0x67e934:4,0x67e968:4,0x67e980:4,0x67e988:4,0x67e9e4:4,0x67ea00:4,0x67ea14:4,0x67ea38:4,0x67ea7c:4,0x67ea80:4,0x67eaac:4,0x67eab4:4,0x67eb10:4,0x67eb30:4,0x67eb38:4,0x67eb3c:4,0x67eb50:4,0x67eb54:4,0x67eb80:4,0x67eb84:4,0x67eb88:4,0x67eb8c:4,0x67eb90:4,0x67eb94:4,0x67ebb8:4,0x67ebc0:4,0x67ebc8:4,0x67ebdc:4,0x67ebe0:4,0x67ebe4:4,0x67ebe8:4,0x67ebec:4,0x67ebf0:4,0x67ebf4:4,0x67ebf8:4,0x67ebfc:4,0x67ec00:4,0x67ec04:4,0x67ec08:4,0x67ec0c:4,0x67ec10:4,0x67ec14:4,0x67ec18:4,0x67ec1c:4,0x67ec20:4,0x67ec24:4,0x67ec28:4,0x67ec2c:4,0x67ec30:4,0x67ec34:4,0x67ec38:4,0x67ec3c:4,0x67ec40:4,0x67ec44:4,0x67ec48:4,0x67ec4c:4,0x67ec50:4,0x67ec54:4,0x67ec58:4,0x67ec5c:4,0x67ec60:4,0x67ed00:4,0x67ed04:4,0x67ed08:4,0x67ed0c:4,0x67ed18:4,0x67ed1c:4,0x67ed44:4,0x67ed58:4,0x67ed98:4,0x67edb0:4,0x67edb4:4,0x67edb8:4,0x67edbc:4,0x67edc0:4,0x67edc4:4,0x67edc8:4,0x67edcc:4,0x67edd0:4,0x67edd4:4,0x67edd8:4,0x67eddc:4,0x67ede0:4,0x67ede4:4,0x67ede8:4,0x67edec:4,0x67edf0:4,0x67edf4:4,0x67edf8:4,0x67edfc:4,0x67ee00:4,0x67ee04:4,0x67ee10:4,0x67ee14:4,0x67ee18:4,0x67ee1c:4,0x67ee20:4,0x67ee24:4,0x67ee28:4,0x67ee2c:4,0x67ee30:4,0x67ee34:4,0x67ee38:4,0x67ee58:4,0x67ee5c:4,0x67ee60:4,0x67ee84:4,0x67ee8c:4,0x67ee90:4,0x67eea0:4,0x67eea4:4,0x67eea8:4,0x67eeac:4,0x67eeb0:4,0x67eeb4:4,0x67eeb8:4,0x67eebc:4,0x67eec4:4,0x67eecc:4,0x67eefc:4,0x67ef00:4,0x67ef28:4,0x67ef2c:4,0x67ef44:4,0x67ef60:4,0x67ef98:4,0x67ef9c:4,0x67efb0:4,0x67efb4:4,0x67efb8:4,0x67efc0:4,0x67efc4:4,0x67efcc:4,0x67efd0:4,0x67efe0:4,0x67eff4:4,0x67f010:4,0x67f024:4,0x67f048:4,0x67f054:4,0x67f07c:4,0x67f09c:4,0x67f0ac:4,0x67f0f4:4,0x67f10c:4,0x67f148:4,0x67f15c:4,0x67f160:4,0x67f1a0:4,0x67f1b8:4,0x67f1bc:4,0x67f1dc:4,0x67f208:4,0x67f240:4,0x67f280:4,0x67f284:4,0x67f2a0:4,0x67f2a8:4,0x67f2ac:4,0x67f2b0:4,0x67f2c8:4,0x67f2d4:4,0x67f2dc:4,0x67f2f0:4,0x67f318:4,0x67f388:4,0x67f3fc:4,0x67f410:4,0x67f43a:1,0x67f43b:1,0x67f43c:4,0x67f440:4,0x67f444:4,0x67f448:4,0x67f44c:4,0x67f450:4,0x67f454:4,0x67f4a4:4,0x67f4e8:4,0x67f4fc:4,0x67f517:1,0x67f534:4,0x67f53c:4,0x67f550:4,0x67f57c:4,0x67f580:4,0x67f608:4,0x67f60c:4,0x67f678:4,0x67f67c:4,0x67f684:4,0x67f698:4,0x67f69c:4,0x67f7a8:4,0x67f7ac:4,0x67f7b0:4,0x67f7c8:4,0x67f7d4:4,0x67f800:1,0x67f801:1,0x67f840:4,0x67f864:4,0x67f87c:4,0x67f898:4,0x67f8a8:4,0x67f8c0:4,0x67f8d4:4,0x67f8e0:4,0x67f8e4:4,0x67f90c:4,0x67f934:4,0x67f980:4,0x67f988:4,0x67f990:4,0x67f9a4:4,0x67f9a8:4,0x67f9ac:4,0x67f9b0:4,0x67f9bc:4,0x67f9c0:4,0x67f9f4:4,0x67f9fc:4,0x67fa20:4,0x67fa68:4,0x67fa7c:4,0x67fab4:4,0x67fac0:4,0x67fad8:4,0x67fae0:4,0x67faec:4,0x67faf8:4,0x67fafc:4,0x67fb20:4,0x67fb54:4,0x67fb60:4,0x67fb7c:4,0x67fb80:4,0x67fba0:4,0x67fbcc:4,0x67fbec:4,0x67fbf0:4,0x67fc1c:4,0x67fc24:4,0x67fc40:4,0x67fc68:4,0x67fcac:4,0x67fccc:4,0x67fcd0:4,0x67fce8:4,0x67fcf8:4,0x67fd04:4,0x67fd3c:4,0x67fdcc:4,0x67fddc:4,0x67fde0:4,0x67fde8:4,0x67fe08:4,0x67fe14:4,0x67fe40:4,0x67fe44:4,0x67fe48:4,0x67fe4c:4,0x67fe70:4,0x67fe94:4,0x67feb0:4,0x67febc:4,0x67fec4:1,0x67fedc:4,0x67ff44:4,0x67ff58:4,0x67ff5c:4,0x67ff88:4,0x67ff8c:4,0x67ffb8:4,0x67ffc8:4,0x67ffd0:4,0x67ffd4:4,0x67ffe4:4},{},{},{0x6a0000:1,0x6a0002:1,0x6a0003:1,0x6a0004:4,0x6a0008:4,0x6a000c:4,0x6a0010:16,0x6a0040:64,0x6a0200:256,0x6a0300:256,0x6a0400:1024,0x6a0800:256,0x6a0900:256,0x6a0b00:256,0x6a0c00:1024,0x6a1000:1024,0x6a1400:1024,0x6a1800:1024,0x6a1c00:1024,0x6a2000:1024,0x6a2400:1024,0x6a2800:1024,0x6a2c00:1024,0x6a3000:256,0x6a3100:256,0x6a3200:256,0x6a3400:1024,0x6a3800:1024,0x6a3c00:1024,0x6a4a00:256,0x6a4b00:256,0x6a5000:1024,0x6a5400:1024,0x6a5800:1024,0x6a5c00:1024,0x6a6c00:1024,0x6a7000:1024,0x6a7400:1024,0x6a7800:1024,0x6a7c00:1024,0x6ae000:1024,0x6ae400:1024,0x6ae800:1024,0x6aec00:1024},{},{},{},{0x6e0600:256,0x6e0700:256,0x6e1000:1024,0x6e2800:1024,0x6e2c90:16,0x6e3000:256,0x6e3300:256,0x6e3400:256,0x6e3500:256,0x6e3800:1024,0x6e3c00:1024,0x6e4000:256,0x6e4100:256,0x6e4800:256,0x6e4900:256,0x6e4b00:64,0x6e4b40:64,0x6e4b80:16,0x6e4b90:16,0x6e4ba0:16,0x6e4bb0:16,0x6e4bc0:64,0x6e4c00:16,0x6e4c10:16,0x6e4c20:16,0x6e4c30:16,0x6e4c9c:4,0x6e4cb8:4,0x6e4cc0:64,0x6e4d00:64,0x6e4d40:64,0x6e5000:1024,0x6e5400:1024,0x6e5800:1024,0x6e5d20:16,0x6e5d30:16,0x6e5e00:256,0x6e5f00:256,0x6e6000:1024,0x6e6400:1024,0x6e6800:1024,0x6e6c00:1024,0x6e7000:1024,0x6e7400:1024,0x6e7800:1024,0x6e7c00:1024,0x6e9800:1024,0x6e9c00:256,0x6e9d00:256,0x6ea520:16,0x6ea530:16,0x6ea600:256,0x6ea700:256,0x6eacc0:64,0x6ead00:16,0x6ead10:16,0x6ead20:16,0x6ead40:16,0x6ead50:16,0x6ead60:16,0x6ead70:16,0x6eadc0:16,0x6eadd0:16,0x6eb000:1024,0x6eb400:1024,0x6eb800:1024,0x6ebc00:1024,0x6ec000:1024,0x6ec400:1024,0x6ec800:1024,0x6ecc00:1024,0x6ed000:1024,0x6ed400:1024,0x6ed800:1024,0x6edc00:1024,0x6ee400:1024,0x6ee820:16,0x6ee830:16,0x6eec00:256,0x6eed00:256,0x6ef000:1024,0x6ef400:1024,0x6ef800:1024,0x6efc00:1024},{0x6f0000:1024,0x6f0400:1024,0x6f0800:1024,0x6f0c00:1024,0x6f1000:1024,0x6f1400:1024,0x6f1800:1024,0x6f1c00:1024,0x6f2000:1024,0x6f2400:1024,0x6f2800:1024,0x6f2c00:1024,0x6f3000:1024,0x6f3400:1024,0x6f3800:1024,0x6f3c00:1024,0x6f4200:256,0x6f43c0:16,0x6f4440:16,0x6f4450:16,0x6f4800:1024,0x6f4c00:1024,0x6f5500:256,0x6f5bc0:16,0x6f5bd0:16,0x6f7000:256,0x6f7100:256,0x6f7200:256,0x6f7300:256,0x6f7400:256,0x6f7500:256,0x6f76c8:4,0x6f76cc:4,0x6f7740:64,0x6f7780:16,0x6f7790:16,0x6f7800:1024,0x6f7c00:256,0x6f7e00:256,0x6f7f00:256,0x6f8000:1024,0x6f8400:1024,0x6f8800:1024,0x6f8c00:1024,0x6f9000:1024,0x6f9400:1024,0x6f9800:1024,0x6f9c00:1024,0x6fa000:1024,0x6fa400:1024,0x6faa00:256,0x6fac00:1024,0x6fb000:1024,0x6fb400:1024,0x6fba00:256,0x6fbb00:256,0x6fc000:1024,0x6fc400:1024,0x6fc800:1024,0x6fcc00:1024,0x6fd000:1024,0x6fd400:1024,0x6fdd80:64,0x6fddc0:64,0x6fde00:256,0x6fdff0:4,0x6fdff8:4,0x6fe000:1024,0x6fe400:1024,0x6feb60:16,0x6feb70:16,0x6feb9c:4,0x6feba0:16,0x6febb0:16},{0x700000:1024,0x700400:1024,0x700800:1024,0x700c00:1024,0x701000:1024,0x701400:1024,0x701800:1024,0x701c00:1024,0x702000:1024,0x702400:1024,0x702800:1024,0x702c00:1024,0x703000:1024,0x703400:1024,0x703800:1024,0x703c00:1024,0x704000:256,0x704100:256,0x704200:256,0x704300:256,0x704900:256,0x704a00:256,0x704b00:256,0x705000:1024,0x705400:1024,0x705800:1024,0x705c00:1024,0x706000:256,0x706100:256,0x706200:256,0x706300:256,0x706400:1024,0x706d80:64,0x706dc0:64,0x706f00:256,0x707000:1024,0x707400:256,0x707500:256,0x707a00:256,0x707b00:256,0x707c00:1024,0x708000:1024,0x708400:256,0x708930:4,0x708934:4,0x70c000:1024,0x70e000:1024,0x70e400:1024,0x70e800:1024,0x70ec00:1024,0x70f000:1024,0x70f400:1024,0x70f800:1024,0x70fc00:1024},{0x710000:1024,0x710400:1024,0x710800:256,0x710900:256,0x710bc0:16,0x710bd0:16,0x710c00:1024,0x711000:256,0x711100:256,0x711200:256,0x711800:1024,0x711f00:256,0x712c00:1024,0x713000:1024,0x7134a0:16,0x7134b0:16,0x713600:256,0x713700:256,0x713800:256,0x713900:256,0x713a00:256,0x713b00:64,0x713b40:64,0x713be0:4,0x713e00:256,0x713f00:256,0x714000:1024,0x714400:1024,0x714800:1024,0x714c00:1024,0x715000:1024,0x715400:1024,0x715800:1024,0x715c00:1024,0x716000:1024,0x716400:1024,0x716800:1024,0x716c00:1024,0x717000:1024,0x717400:1024,0x717800:1024,0x717c00:1024,0x718000:256,0x718100:256,0x718260:16,0x718270:4,0x718274:4,0x718400:1024,0x718800:1024,0x718c00:1024,0x71c200:256,0x71c300:256,0x71c564:4,0x71c800:256,0x71c900:256,0x71ca00:256,0x71cc00:1024,0x71d060:16,0x71d070:16,0x71d080:64,0x71d0c0:64,0x71d100:256,0x71d400:64,0x71d464:4,0x71d4b8:4,0x71d4bc:4,0x71d500:64,0x71d540:64,0x71d600:256,0x71d700:256,0x71da00:256,0x71db00:256,0x71dc00:1024,0x71e000:1024,0x71e400:1024,0x71e800:1024,0x71ec00:1024,0x71f000:1024,0x71f400:1024,0x71f800:1024},{0x721c00:256,0x723600:256,0x723700:256,0x723c00:1024,0x724000:1024,0x724400:256,0x724f40:64,0x725000:1024,0x725400:1024,0x725800:1024,0x725c00:1024,0x726000:1024,0x726400:1024,0x726800:1024,0x726e00:16,0x726e40:64,0x726f00:16,0x726f10:16,0x726fa0:16,0x726fb0:16,0x727000:1024,0x727400:256,0x727500:256,0x727600:256,0x727700:64,0x727740:64,0x727780:64,0x7277c0:4,0x7277c4:4,0x7277c8:4,0x7277cc:4,0x7277d0:16,0x7277e0:16,0x7277f0:16,0x728400:256,0x728700:256,0x728a00:256,0x728b00:256,0x728d40:4,0x728d44:4,0x728d80:64,0x72c400:256,0x72c500:256,0x72c6f8:4,0x72c6fc:4,0x72d000:1024,0x72d400:256,0x72d500:256,0x72d600:256,0x72d700:256,0x72d800:1024,0x72dc00:1024,0x72e000:1024,0x72e400:1024,0x72e800:1024,0x72ec00:1024,0x72f000:1024,0x72f400:1024,0x72f800:1024,0x72fc00:1024},{0x731800:1024,0x731c00:256,0x731d00:256,0x732000:1024,0x732c00:256,0x732d00:256,0x732e00:256,0x732f00:256,0x733000:1024,0x733400:1024,0x733800:1024,0x733c00:1024,0x734540:16,0x735400:64,0x7354c0:16,0x7354d0:16,0x7355c0:64,0x736400:1024,0x736800:1024,0x737800:1024,0x737c10:16,0x739400:1024,0x739800:256,0x739900:256,0x739a00:256,0x739b00:256,0x739c00:256,0x739d00:256,0x739e00:256,0x739f00:256,0x73a640:16,0x73a650:16,0x73a800:1024,0x73ac00:1024,0x73b400:1024,0x73be00:256,0x73bf00:256,0x73c000:1024,0x73c400:1024,0x73c800:1024,0x73cc00:1024,0x73d000:1024,0x73d400:1024,0x73d800:1024,0x73dc00:1024,0x73e000:1024,0x73e400:1024,0x73e800:1024,0x73ec00:1024},{0x740008:4,0x74000c:4,0x740018:4,0x74001c:4,0x740100:256,0x740200:256,0x740300:256,0x740400:1024,0x740800:1024,0x740d00:256,0x741000:1024,0x741400:1024,0x741800:1024,0x741c00:1024,0x743200:16,0x743400:1024,0x743800:256,0x743900:256,0x743a80:16,0x743ad0:16,0x743c00:1024,0x744200:64,0x744240:64,0x744500:256,0x744600:64,0x744640:64,0x744c00:256,0x744d00:256,0x744e00:256,0x744f00:256,0x745500:256,0x745990:16,0x745a50:16,0x745ab8:4,0x745abc:4,0x745f00:256,0x747000:1024,0x747400:256,0x747500:256,0x748000:1024,0x748400:1024,0x748800:1024,0x748c00:1024,0x749000:1024,0x749400:1024,0x749800:1024,0x749c00:1024,0x74a000:1024,0x74a400:1024,0x74a800:1024,0x74ac00:1024,0x74b000:1024,0x74b400:1024,0x74b800:1024,0x74bc00:1024,0x74c000:256,0x74c110:16,0x74c120:16,0x74c130:16,0x74c1b0:4,0x74c1b4:4,0x74c200:256,0x74c300:256,0x74c400:256,0x74c600:256,0x74c700:64,0x74c740:64,0x74c780:16,0x74c790:16,0x74cc00:256,0x74cd00:256,0x74cf00:256,0x74d000:1024,0x74d4a0:16,0x74d540:64,0x74d580:64,0x74d5c0:64,0x74d620:16,0x74d630:16,0x74d640:16,0x74d680:64,0x74d6c0:64,0x74d700:256,0x74d800:1024,0x74e000:1024,0x74e400:1024,0x74e800:1024,0x74ec00:1024,0x74f200:256,0x74f300:256,0x74f400:256,0x74f500:256,0x74f600:256,0x74f700:256,0x74f800:256,0x74f900:256,0x74fb40:64,0x74fc00:256,0x74fd00:256,0x74fe80:64,0x74fec0:64,0x74ff80:64,0x74ffc0:64},{0x750800:1024,0x750c00:1024,0x751500:256,0x751600:256,0x751700:256,0x751800:1024,0x751c00:1024,0x752000:1024,0x752400:1024,0x752800:1024,0x752c00:256,0x752d00:256,0x753000:1024,0x753530:16,0x7535b0:16,0x753900:256,0x753a00:64,0x753a40:64,0x753b00:256,0x753c00:1024,0x754000:1024,0x754400:1024,0x754800:256,0x754900:256,0x754a40:16,0x754a50:16,0x754a80:64,0x754ac0:64,0x754b00:256,0x754c00:1024,0x755000:1024,0x755400:1024,0x755800:1024,0x755c00:1024,0x756400:256,0x756500:256,0x756710:16,0x756728:4,0x75672c:4,0x756748:4,0x75674c:4,0x756780:16,0x7568a8:4,0x7568ac:4,0x756a00:256,0x756b00:256,0x757000:1024,0x757400:1024,0x757840:64,0x757880:64,0x7578c0:64,0x757900:64,0x757940:64,0x757980:64,0x7579c0:4,0x7579c4:4,0x757a80:64,0x757ac0:64,0x757c00:1024,0x758000:1024,0x758400:1024,0x758800:1024,0x758c00:1024,0x759000:1024,0x759400:1024,0x759800:1024,0x759c00:1024,0x75a000:1024,0x75a400:1024,0x75a800:1024,0x75ac00:1024,0x75b000:1024,0x75b400:1024,0x75b800:1024,0x75bc00:1024},{0x761800:256,0x761900:256,0x761a00:256,0x761c00:256,0x761d00:256,0x761e00:256,0x761f00:256,0x764000:256,0x764100:256,0x764200:256,0x764370:16,0x764800:1024,0x764c00:1024,0x765000:256,0x765100:256,0x765400:256,0x765500:256,0x765820:16,0x765830:16,0x765840:64,0x765880:64,0x7658c0:64,0x765900:256,0x765bf0:16,0x766610:16,0x766620:4,0x766624:4,0x767000:1024,0x767400:1024,0x767800:1024,0x767c00:256,0x767d00:256,0x767e00:256,0x767f80:16,0x767f90:16,0x768400:1024,0x769000:1024,0x76b200:256,0x76b400:1024,0x76b800:256,0x76ba00:256,0x76bb00:256,0x76bc00:256,0x76be00:256,0x76bf00:256,0x76c000:256,0x76c100:256,0x76c200:64,0x76c240:64,0x76c280:64,0x76c2c0:64,0x76c300:64,0x76c340:64,0x76c380:64,0x76c3c0:64,0x76c400:1024,0x76ca00:256,0x76cb00:256,0x76cc00:1024,0x76d400:256,0x76d500:256,0x76e000:1024,0x76e400:256,0x76e500:256,0x76e600:256,0x76ef00:256,0x76f200:256,0x76f400:1024,0x76f800:1024,0x76fc00:1024},{0x770000:256,0x770100:256,0x770200:16,0x770210:16,0x770280:64,0x7702c0:64,0x770300:256,0x770400:1024,0x770800:256,0x770a00:64,0x770a40:64,0x770f88:4,0x770f8c:4,0x771000:256,0x7712c0:16,0x7712d0:4,0x7712d4:4,0x7712e0:16,0x7712f0:16,0x771300:256,0x771400:1024,0x771b40:64,0x771b80:16,0x771b90:16,0x771ba0:16,0x771bb0:16,0x771bc0:64,0x771c00:256,0x771d00:256,0x771e30:16,0x771fc0:16,0x771fd0:16,0x772000:1024,0x772400:256,0x772500:64,0x772540:64,0x772580:64,0x7725c0:64,0x772600:64,0x772640:64,0x772680:64,0x7726c0:16,0x7726d0:16,0x7726e0:16,0x7726f0:16,0x772700:256,0x772800:64,0x772840:16,0x772880:64,0x7728c0:64,0x772900:256,0x772a00:16,0x772a10:16,0x772a80:4,0x772a84:4,0x772a88:4,0x772a8c:4,0x772ae0:16,0x772af0:16,0x772c00:256,0x772d00:256,0x773000:1024,0x773400:1024,0x773900:256,0x773a00:256,0x773b80:64,0x773bc0:64,0x773c00:256,0x773d00:256,0x773e00:256,0x773f20:16,0x773f30:16,0x774bd0:16,0x774e00:256,0x774f00:256,0x775000:256,0x7752d0:16,0x775400:1024,0x775800:1024,0x776000:1024,0x776400:1024,0x776c00:256,0x776d00:256,0x777000:1024,0x777400:1024,0x777800:1024,0x777c00:1024,0x778000:1024,0x778400:1024,0x778800:1024,0x778c00:1024,0x779000:1024,0x7794a0:16,0x7794b0:16,0x7797c0:64,0x77a0c8:4,0x77a0cc:4,0x77a180:64,0x77a1c0:64,0x77a200:256,0x77a300:256,0x77a400:1024,0x77b000:1024,0x77b400:1024,0x77b800:1024,0x77bc00:1024,0x77e800:256,0x77e900:256,0x77eb80:64,0x77f800:1024,0x77fc60:4,0x77fc64:4,0x77fcf0:16,0x77fd00:256,0x77fe00:256,0x77ff00:256},{0x780000:1024,0x780400:1024,0x780800:1024,0x780c00:1024,0x781800:1024,0x781e00:256,0x781f00:256,0x782000:1024,0x782400:1024,0x782800:1024,0x782c00:1024,0x783000:256,0x783100:256,0x783400:256,0x783500:256,0x783600:256,0x783700:256,0x784000:1024,0x784400:1024,0x784820:16,0x784830:16,0x784880:64,0x7848c0:64,0x784c00:1024,0x785000:1024,0x785400:1024,0x785808:4,0x78580c:4,0x785a00:256,0x785b00:256,0x785c00:256,0x785e00:256,0x785f00:256,0x788000:1024,0x788400:64,0x788440:64,0x788480:64,0x7884c0:64,0x788500:256,0x788600:256,0x788700:256,0x788880:64,0x788900:64,0x788940:64,0x788f80:16,0x788f90:16,0x78c000:1024,0x78c400:1024,0x78c800:1024,0x78cc00:1024,0x78d000:1024,0x78d400:1024,0x78d800:1024,0x78dc00:1024,0x78e000:1024,0x78e400:1024,0x78e800:1024,0x78ec00:1024,0x78f000:1024,0x78f400:1024,0x78f800:1024,0x78fc00:1024},{0x790008:4,0x79000c:4,0x790010:16,0x790400:256,0x790500:256,0x790800:1024,0x790c00:1024,0x791000:1024,0x791400:1024,0x791800:1024,0x791c00:256,0x791d00:256,0x791e00:256,0x791f00:256,0x792000:1024,0x792400:256,0x792500:256,0x792600:256,0x792700:256,0x792800:1024,0x792e00:64,0x792e80:64,0x792ec0:64,0x792f00:256,0x793000:256,0x793100:256,0x793208:4,0x79320c:4,0x793300:256,0x7934a0:16,0x7934b0:16,0x7934d0:16,0x7934e0:16,0x7934f0:16,0x7936b0:4,0x7936b4:4,0x793700:64,0x793800:256,0x793900:256,0x793a00:64,0x793a40:64,0x793a88:4,0x793a8c:4,0x793a90:16,0x793aa0:4,0x793aa4:4,0x793b00:256,0x793c00:1024,0x794400:1024,0x794c00:256,0x794d00:256,0x794f80:64,0x795900:256,0x796480:64,0x7964c0:64,0x796500:64,0x7965d0:16,0x79c000:256,0x79c100:256,0x79c200:256,0x79c300:256,0x79c400:1024,0x79c8c0:4,0x79c8c4:4,0x79c900:256,0x79cc00:1024,0x79e000:1024,0x79e400:1024,0x79e800:1024,0x79ec00:1024,0x79f800:1024,0x79ff00:256},{0x7a0040:64,0x7a0080:64,0x7a00c0:64,0x7a0400:1024,0x7a0800:256,0x7a0900:256,0x7a0a00:64,0x7a0a40:64,0x7a0a80:64,0x7a0ac0:64,0x7a0b00:64,0x7a0b40:64,0x7a0c00:256,0x7a0d00:256,0x7a0e00:256,0x7a3000:256,0x7a3100:64,0x7a3300:256,0x7a4000:1024,0x7a4400:1024,0x7a4800:1024,0x7a4c00:1024,0x7a5000:1024,0x7a5400:1024,0x7a5800:1024,0x7a5c00:1024,0x7a6000:256,0x7a6100:256,0x7a6600:16,0x7a6640:16,0x7a6650:16,0x7a7000:1024,0x7a7700:256,0x7a8078:4,0x7a807c:4,0x7a8800:1024,0x7a8c00:1024,0x7a9080:64,0x7a90c0:64,0x7a98c0:64,0x7a9c00:1024,0x7abc00:1024,0x7ac000:1024,0x7ac600:256,0x7ac840:64,0x7ac930:16,0x7acc00:1024,0x7ae000:1024,0x7ae400:1024,0x7ae800:1024,0x7aec00:1024,0x7af000:1024,0x7af400:1024,0x7af818:4,0x7af81c:4,0x7af830:16,0x7aff40:4,0x7aff44:4},{0x7b0080:64,0x7b0400:1024,0x7b0800:1024,0x7b0c00:1024,0x7b3180:64,0x7b31c0:64,0x7b32a0:16,0x7b32b0:16,0x7b3400:1024,0x7b3800:256,0x7b3900:256,0x7b3a00:256,0x7b3b00:256,0x7b3c00:256,0x7b3d00:256,0x7b3e00:256,0x7b4000:1024,0x7b4400:1024,0x7b4800:1024,0x7b4c00:1024,0x7b5000:1024,0x7b5400:1024,0x7b5800:1024,0x7b5c00:1024,0x7b6000:256,0x7b6100:256,0x7b6200:64,0x7b6240:64,0x7b6380:64,0x7b63c0:64,0x7b6400:16,0x7b6410:16,0x7b6500:256,0x7b6700:64,0x7b6740:64,0x7b6c80:16,0x7b6cd0:16,0x7b7000:1024,0x7b7400:1024,0x7b7800:1024,0x7b7c00:1024,0x7b8000:1024,0x7b8400:1024,0x7b8850:16,0x7b8900:256,0x7b8a00:256,0x7b8b00:256,0x7b9000:1024,0x7b9400:256,0x7b9500:256,0x7b9600:256,0x7b9700:256,0x7b9800:1024,0x7b9c00:1024,0x7ba000:1024,0x7ba400:1024,0x7ba800:1024,0x7bac00:256,0x7bad00:256,0x7bae00:256,0x7baf00:256,0x7bb03c:4,0x7bb050:16,0x7bb100:256,0x7bb200:256,0x7bb300:256,0x7bb400:1024,0x7bb800:1024,0x7bbc00:1024,0x7bc400:256,0x7bc500:256,0x7bc780:64,0x7bc7c0:64,0x7bce00:256,0x7bcf00:256,0x7be800:1024,0x7bf200:64,0x7bf240:64,0x7bf400:1024,0x7bf900:256,0x7bfd00:256},{0x7c0640:64,0x7c0e00:256,0x7c0f00:256,0x7c1000:256,0x7c1100:256,0x7c1400:256,0x7c1500:16,0x7c1510:16,0x7c1520:16,0x7c1530:16,0x7c1540:64,0x7c1580:64,0x7c15c0:64,0x7c1600:256,0x7c1700:256,0x7c1cc0:64,0x7c1d00:64,0x7c1d40:64,0x7c1f00:256,0x7c2870:16,0x7c2880:64,0x7c28c0:16,0x7c28d0:16,0x7c2a00:64,0x7c2a40:64,0x7c2a80:64,0x7c2ac0:64,0x7c2f00:64,0x7c4000:256,0x7c4100:256,0x7c4200:64,0x7c4240:64,0x7c4300:256,0x7c4400:1024,0x7c4800:256,0x7c4900:256,0x7c4a00:256,0x7c4b00:256,0x7c4c00:1024,0x7c5800:256,0x7c5900:64,0x7c5940:64,0x7c5980:64,0x7c59c0:64,0x7c5a00:256,0x7c5b00:256,0x7c5c00:1024,0x7c6c08:4,0x7c6c0c:4,0x7c6c28:4,0x7c6c2c:4,0x7c6d60:4,0x7c6d64:4,0x7c7000:256,0x7c7100:256,0x7c7200:256,0x7c7300:256,0x7c7400:256,0x7c7500:256,0x7c7600:256,0x7c7700:256,0x7c7e00:256,0x7c7f00:256,0x7c8000:1024,0x7c8400:1024,0x7c9380:64,0x7c93c0:64,0x7c9700:256,0x7c9800:256,0x7c9c00:256,0x7ca000:256,0x7ca100:256,0x7ca200:256,0x7ca300:256,0x7ca400:1024,0x7cac00:256,0x7cad00:256,0x7cae00:256,0x7caf00:256,0x7cc000:256,0x7cc100:256,0x7cc400:256,0x7cc800:1024,0x7ccc00:1024,0x7cdc00:1024,0x7ce000:256,0x7ce100:256,0x7ce200:256,0x7ce300:256,0x7ce400:1024,0x7ce800:256,0x7ce900:256,0x7cea00:256,0x7ceb00:256,0x7cec00:1024,0x7cf000:64,0x7cf040:64,0x7cf080:64,0x7cf200:256,0x7cf3c0:64,0x7cf800:64,0x7cf840:64,0x7cf900:256,0x7cfa00:256,0x7cfb00:256,0x7cfe00:64},{0x7d1fc0:64,0x7d2000:256,0x7d2100:256,0x7d2200:256,0x7d2300:64,0x7d2340:64,0x7d2380:64,0x7d23c0:64,0x7d2400:1024,0x7d2800:1024,0x7d2c00:1024,0x7d3a80:64,0x7d3ac0:64,0x7d3d80:64,0x7d3dc0:64,0x7d3e00:64,0x7d4000:1024,0x7d4400:1024,0x7d4800:256,0x7d4900:256,0x7d4a00:256,0x7d4b00:256,0x7d4c00:64,0x7d4c40:64,0x7d4c80:64,0x7d4cc0:64,0x7d4d00:256,0x7d4e00:256,0x7d4f00:256,0x7d5000:1024,0x7d5400:1024,0x7d5800:1024,0x7d5c00:1024,0x7d6000:256,0x7d6100:256,0x7d6200:256,0x7d6800:1024,0x7d6c00:1024,0x7d7000:1024,0x7d7400:1024,0x7d7800:1024,0x7d7c00:1024,0x7da900:256,0x7dab00:256,0x7dd000:64,0x7dd200:256,0x7dd300:256,0x7dd500:64,0x7dd540:64,0x7dd660:16,0x7dd670:16,0x7dd700:64,0x7dd800:256,0x7dd900:256,0x7dda00:256,0x7ddb00:256,0x7ddc00:256,0x7ddd00:256,0x7dde00:256,0x7ddf00:256,0x7dfe80:64,0x7dfec0:64},{},{},{},{},{},{},{},{},{},{},{},{},{},{0x8b0900:256,0x8b8100:256,0x8b9400:256,0x8b9b00:256,0x8b9f00:256,0x8baa00:256,0x8bb000:256,0x8bb700:256,0x8bba00:256,0x8bbd00:256,0x8bc400:1024,0x8bc800:1024,0x8bcc00:1024,0x8bd000:1024,0x8bd400:1024,0x8bd900:256,0x8bdb00:256,0x8bdc00:256,0x8bdd00:256,0x8be000:256,0x8be200:256,0x8be300:256},{0x8c4b00:256,0x8c8f00:256,0x8ccd00:256,0x8cce00:256,0x8ccf00:256,0x8cd200:256,0x8ce000:256,0x8ced00:256,0x8cf000:256,0x8cf300:256,0x8cf600:256,0x8cf900:256,0x8cfa00:256,0x8cff00:256},{},{},{},{0x900000:256,0x900700:256,0x900c00:256,0x903400:256,0x907b00:256,0x90ff00:256},{},{},{},{},{},{0x960000:256,0x967300:256,0x967900:256,0x967a00:256,0x968198:4,0x9681c0:4,0x9681d8:4,0x9681fc:4,0x968a00:256,0x968b00:256,0x96df00:256,0x96f200:4,0x96f204:4,0x96f208:4,0x96f21c:4,0x96f22c:4,0x96f230:4,0x96f234:4,0x96f238:4,0x96f24c:4,0x96f250:4,0x96f25c:4,0x96f260:4,0x96f270:4,0x96f274:4,0x96f278:4,0x96f298:4,0x96f29c:4,0x96f2a0:4,0x96f2a4:4,0x96f2a8:4,0x96f2b8:4,0x96f2bc:4,0x96f2c0:4,0x96f2d4:4,0x96f2e0:4,0x96f2e8:4,0x96f2ec:4,0x96f2f0:4,0x96f2f4:4,0x96f2f8:4,0x96ff00:256},{},{0x986880:64,0x9868c0:64},{0x990000:256,0x990300:256,0x992200:256,0x992300:256,0x992400:256,0x992500:256,0x996300:256,0x996500:256,0x997600:256,0x997700:256},{},{},{},{0x9d0000:256,0x9d1200:256,0x9d3d00:256,0x9d7a00:256,0x9d9400:256,0x9d9c00:256,0x9dff00:256},{},{0x9fe200:256},{},{0xa1cf00:256},{0xa26900:256},{0xa30000:256,0xa32f04:4,0xa33500:4,0xa33504:4,0xa33508:4,0xa3350c:4,0xa33524:4,0xa33528:4,0xa3352c:4,0xa33530:4,0xa33534:4,0xa33538:4,0xa3353c:4,0xa33540:4,0xa33558:4,0xa3355c:4,0xa33560:4,0xa33564:4,0xa33568:4,0xa3356c:4,0xa33570:4,0xa33574:4,0xa33578:4,0xa3357c:4,0xa33580:4,0xa33584:4,0xa33588:4,0xa335a0:4,0xa335a4:4,0xa335a8:4,0xa335ac:4,0xa335bc:4,0xa335dc:4,0xa335f0:4,0xa37d00:256,0xa38e00:256,0xa3b100:256,0xa3b300:256,0xa3cc00:256},{},{},{0xa66f00:256},{0xa78b00:256,0xa7bd00:256},{0xa8a000:256},{},{},{0xab0800:1024,0xab0c00:1024,0xab2200:256,0xab2300:256,0xab2400:1024,0xab2800:1024,0xab2c00:1024,0xab5000:1024,0xab5400:1024,0xab5800:1024,0xab5c00:1024,0xab6800:1024,0xab6c00:1024,0xab7000:1024,0xab7400:1024,0xab7800:1024,0xab7c00:1024,0xabd000:1024,0xabd400:1024,0xabd800:1024,0xabdc00:1024},{},{},{},{0xaf0000:1024,0xaf0400:1024,0xaf0800:1024,0xaf0c00:1024,0xaf1000:1024,0xaf1400:1024,0xaf1800:1024,0xaf1e00:256,0xaf1f00:256,0xaf2a00:256,0xaf2b00:256,0xaf2c00:256,0xaf2e00:256,0xaf2f00:256,0xaf3000:1024,0xaf3400:1024,0xaf3800:1024,0xaf3c00:1024,0xaf4000:1024,0xaf4400:1024,0xaf4800:1024,0xaf4c00:1024,0xaf5000:1024,0xaf5400:1024,0xaf5800:1024,0xaf5c00:1024,0xaf6600:256,0xaf6a80:64,0xaf6ac0:64,0xaf9200:256,0xaf9300:256,0xaf9400:1024,0xaf9800:1024,0xafa000:1024,0xafa400:1024,0xafa800:1024,0xafac00:1024,0xafb200:256,0xafb880:64,0xafb900:256,0xafba00:256,0xafbb00:256,0xafbc00:1024},{},{},{},{},{0xb44c00:256,0xb44d00:256,0xb44e00:256,0xb44f00:256,0xb45400:256,0xb45500:256,0xb45600:256,0xb45800:1024,0xb45e38:4,0xb45e3c:4,0xb45e60:16,0xb45f80:64,0xb45fc0:64,0xb46000:1024,0xb46400:1024,0xb46800:1024,0xb46c00:1024,0xb47000:1024,0xb47400:1024,0xb47800:1024,0xb47c00:1024,0xb48180:64,0xb481c0:64,0xb48200:256,0xb48800:1024,0xb48c00:1024,0xb49410:4,0xb49414:4,0xb49498:4,0xb4949c:4,0xb494d8:4,0xb494dc:4,0xb494e0:16,0xb494f0:16,0xb49580:16,0xb49590:16,0xb496a0:16,0xb496b0:16,0xb49800:1024,0xb49c00:1024,0xb4a000:1024,0xb4a400:1024,0xb4a800:1024,0xb4ac00:1024,0xb4b2c0:64,0xb4b800:1024,0xb4bc00:64,0xb4bc40:64,0xb4bd94:4,0xb4c8fc:4,0xb4c900:256,0xb4ca00:256,0xb4cb00:256,0xb4d000:256,0xb4d100:256,0xb4d2e0:16,0xb4d2f0:16,0xb4d400:256,0xb4d500:256,0xb4dee0:16,0xb4def0:16,0xb4df00:256,0xb4e900:64,0xb4e940:16,0xb4e950:16,0xb4eb40:16,0xb4eb50:16},{},{0xb610c0:16,0xb610d0:16,0xb61200:64,0xb61240:64,0xb617b8:4,0xb617bc:4,0xb617c8:4,0xb617cc:4,0xb62000:1024,0xb62400:1024,0xb62800:1024,0xb62c00:1024,0xb63060:16,0xb63070:16,0xb63100:256,0xb63200:16,0xb63270:16,0xb63300:256,0xb63600:64,0xb63640:64,0xb63d00:256,0xb65000:1024,0xb65400:1024,0xb65800:1024,0xb65c00:256,0xb66000:1024,0xb66400:1024,0xb66800:1024,0xb66c00:1024,0xb67000:1024,0xb67400:1024,0xb67800:1024,0xb67c00:1024,0xb68000:1024,0xb68400:1024,0xb68800:1024,0xb68c00:1024,0xb69000:1024,0xb69400:1024,0xb69d00:256,0xb6a040:16,0xb6a050:16,0xb6ae00:256,0xb6af00:256,0xb6c800:1024,0xb6cc00:1024,0xb6ec80:64,0xb6ecc0:64,0xb6ee00:256,0xb6ef00:16,0xb6ef10:16,0xb6f000:1024,0xb6f400:1024,0xb6fe00:256},{0xb70000:1024,0xb70400:1024,0xb70800:1024,0xb70c00:1024,0xb71000:1024,0xb71400:1024,0xb71800:1024,0xb71c00:1024,0xb72000:1024,0xb72400:1024,0xb72800:1024,0xb72c00:1024,0xb73000:1024,0xb73400:1024,0xb73800:1024,0xb73c00:1024,0xb74000:1024,0xb74400:1024,0xb74eb4:4,0xb751b4:4,0xb75400:256,0xb75500:256,0xb75b80:4,0xb75b88:4,0xb75b8c:4,0xb75b90:16,0xb75c00:1024,0xb78000:1024,0xb78400:1024,0xb78800:1024,0xb78c00:1024,0xb79000:1024,0xb79400:1024,0xb79800:1024,0xb79c00:1024,0xb7a000:1024,0xb7a400:1024,0xb7a800:256,0xb7a900:256,0xb7aa00:256,0xb7ac00:1024,0xb7b600:16,0xb7b610:16,0xb7b800:1024,0xb7bc00:1024,0xb7c000:1024,0xb7c400:1024,0xb7c800:1024,0xb7cc00:1024,0xb7d000:1024,0xb7d400:1024,0xb7d800:1024,0xb7dc00:1024,0xb7e000:1024,0xb7e400:1024,0xb7e800:1024,0xb7ec00:1024,0xb7f000:1024,0xb7f400:1024,0xb7f800:1024,0xb7fc00:1024},{},{},{},{},{},{},{},{},{0xc07c9a:1,0xc0bcaa:1},{},{},{},{},{},{},{},{},{},{0xca0064:1,0xca0065:1,0xca007a:1,0xca007b:1,0xca00b0:4,0xca0380:1,0xca0381:1,0xca0480:16,0xca0490:16,0xca04fc:4,0xca0606:1,0xca0607:1,0xca0642:1,0xca0643:1,0xca0648:1,0xca0649:1,0xca0657:1,0xca0658:1,0xca0659:1,0xca065c:1,0xca065d:1,0xca0667:1,0xca066c:1,0xca066e:1,0xca066f:1,0xca0672:1,0xca06b0:16,0xca0800:1,0xca0802:1,0xca0803:1,0xca0804:1,0xca0805:1,0xca080c:1,0xca0818:1,0xca084d:1,0xca0880:16,0xca0890:16,0xca08c0:16,0xca0920:1,0xca0922:1,0xca0923:1,0xca0930:1,0xca0931:1,0xca0933:1,0xca0934:1,0xca0935:1,0xca0936:1,0xca0939:1,0xca093a:1,0xca093b:1,0xca0a40:16,0xca0c01:1,0xca0c02:1,0xca0c11:1,0xca0c12:1,0xca0c13:1,0xca0c48:1,0xca0c54:1,0xca0c55:1,0xca0c60:1,0xca0c62:1,0xca0c63:1,0xca0c6a:1,0xca0c6f:1,0xca0c74:1,0xca0e40:1,0xca0e41:1,0xca0e45:1,0xca0e49:1,0xca0e4a:1,0xca0e4b:1,0xca0e4c:1,0xca0e4e:1,0xca0e4f:1,0xca0e58:1,0xca0e61:1,0xca0e68:1,0xca0e69:1,0xca0e6c:1,0xca0e6d:1,0xca0e6f:1,0xca0e72:1,0xca0e73:1,0xca0e76:1,0xca0e77:1,0xca0e7c:1,0xca0e7d:1,0xca0e7f:1,0xca0e81:1,0xca0e87:1,0xca0e88:1,0xca0e95:1,0xca0e97:1,0xca0e9d:1,0xca0e9e:1,0xca0e9f:1,0xca0ea9:1,0xca0eaa:1,0xca0eab:1,0xca0eb0:1,0xca0eb8:1,0xca0eb9:1,0xca0ed0:1,0xca0ed1:1,0xca0ed5:1,0xca0edb:1,0xca0edc:1,0xca0ede:1,0xca0edf:1,0xca0ee1:1,0xca0ee2:1,0xca0ee3:1,0xca0ee7:1,0xca0eeb:1,0xca0eec:1,0xca0eed:1,0xca0eee:1,0xca0eef:1,0xca0ef6:1,0xca0efb:1,0xca1442:1,0xca144f:1,0xca1457:1,0xca1458:1,0xca1459:1,0xca145a:1,0xca145e:1,0xca145f:1,0xca1472:1,0xca1475:1,0xca1478:1,0xca147d:1,0xca147f:1,0xca1583:1,0xca1584:1,0xca158d:1,0xca158e:1,0xca1593:1,0xca1594:1,0xca1596:1,0xca1597:1,0xca1598:1,0xca1599:1,0xca159a:1,0xca159c:1,0xca16f8:4,0xca16fc:4,0xca1b88:1,0xca1b89:1,0xca2600:1,0xca2601:1,0xca2602:1,0xca2603:1,0xca2608:4,0xca260c:4,0xca2630:16,0xca2640:16,0xca2650:16,0xca2660:16,0xca2670:16,0xca2680:1,0xca2681:1,0xca2682:1,0xca2683:1,0xca2684:1,0xca2685:1,0xca2686:1,0xca2687:1,0xca2688:1,0xca2689:1,0xca268a:1,0xca268c:1,0xca268d:1,0xca268e:1,0xca268f:1,0xca2692:1,0xca2693:1,0xca2695:1,0xca2696:1,0xca2697:1,0xca2698:1,0xca2699:1,0xca269a:1,0xca269b:1,0xca269c:1,0xca269e:1,0xca269f:1,0xca26a0:1,0xca26a1:1,0xca26a4:4,0xca26a8:1,0xca26a9:1,0xca26aa:1,0xca26ab:1,0xca26b0:1,0xca26b1:1,0xca26b8:4,0xca26bc:4,0xca26c0:64,0xca2804:1,0xca2805:1,0xca2807:1,0xca280f:1,0xca2887:1,0xca2888:1,0xca288c:1,0xca288f:1,0xca2890:1,0xca2891:1,0xca2896:1,0xca289b:1,0xca289c:1,0xca289e:1,0xca289f:1,0xca28a2:1,0xca2908:1,0xca2909:1,0xca290b:1,0xca290c:1,0xca290d:1,0xca2980:1,0xca2982:1,0xca2983:1,0xca2998:4,0xca299c:4,0xca29c0:1,0xca29f0:16,0xca2b4c:4,0xca2b90:16,0xca2c10:16,0xca2c43:1,0xca2c4a:1,0xca2c81:1,0xca2c84:1,0xca2c85:1,0xca2c92:1,0xca2c93:1,0xca2d00:1,0xca2d01:1,0xca2d02:1,0xca2d0f:1,0xca2d10:16,0xca2e10:1,0xca2e11:1,0xca2e12:1,0xca2e14:1,0xca2e15:1,0xca2e20:16,0xca2e30:16,0xca2e80:1,0xca2ee0:16,0xca2f52:1,0xca2f53:1,0xca2f7e:1,0xca2f80:1,0xca2f82:1,0xca2f83:1,0xca39f0:16,0xca3a00:1,0xca3b00:1,0xca3bd4:4,0xca3be8:1,0xca3be9:1,0xca3bec:1,0xca3c30:4,0xca3c34:4,0xca3c60:4,0xca3c64:4,0xca3c70:16,0xca3c84:4,0xca3c88:4,0xca3c8c:4,0xca3c90:16,0xca3e70:4,0xca3ef8:4,0xca3efc:1,0xca3eff:1,0xca3f51:1,0xca3f52:1,0xca3f53:1,0xca3f54:4,0xca3f58:4,0xca3f5c:4,0xca3fa0:16,0xca3fb0:16,0xca3ff8:4,0xca4100:4,0xca4104:4,0xca4108:1,0xca4109:1,0xca4300:4,0xca4504:4,0xca4510:16,0xca4600:16,0xca4610:16,0xca4660:16,0xca46c0:16,0xca4828:4,0xca482c:4,0xca4850:16,0xca4980:4,0xca4a08:4,0xca4a0c:4,0xca4a50:16,0xca4afe:1,0xca4aff:1,0xca4bd0:16,0xca4bfc:4,0xca4cfc:4,0xca4d50:4,0xca4d54:4,0xca4d5c:4,0xca4e08:4,0xca4e0c:4,0xca4fe0:4,0xca4fe4:4,0xca4ff8:4,0xca50c0:4,0xca50c4:4,0xca50c8:4,0xca50cc:4,0xca5100:4,0xca53fc:4,0xca5400:4,0xca5404:4,0xca5408:4,0xca540c:4,0xca5410:1,0xca5411:1,0xca5418:4,0xca541c:4,0xca55d0:16,0xca56f9:1,0xca56fc:4,0xca5750:16,0xca5908:4,0xca590c:4,0xca5a00:4,0xca5a70:16,0xca5ac4:1,0xca5ae0:16,0xca5b00:4,0xca5b60:16,0xca5b80:4,0xca5bb0:16,0xca5be0:16,0xca5bf0:16,0xca5c00:4,0xca5c08:4,0xca5c0c:4,0xca5c30:16,0xca5cfc:4,0xca5d00:4,0xca5dfc:4,0xca5e5c:4,0xca5f00:4,0xca5f04:4,0xca5f08:4,0xca5f0c:4,0xca5f10:16,0xca5ff0:4,0xca5ff4:4,0xca5ffc:4,0xca6000:64,0xca6040:4,0xca6044:4,0xca6048:4,0xca604c:4,0xca6050:16,0xca6060:4,0xca6064:4,0xca6068:4,0xca606c:4,0xca6070:16,0xca6080:4,0xca6084:4,0xca6088:4,0xca608c:4,0xca6090:16,0xca60a0:4,0xca60a4:4,0xca60a8:4,0xca60ac:4,0xca60b0:16,0xca60c0:4,0xca60c4:4,0xca60c8:4,0xca60cc:4,0xca60d0:16,0xca60e0:4,0xca60e4:4,0xca60e8:4,0xca60ec:4,0xca60f0:16,0xca6100:4,0xca6104:4,0xca6108:4,0xca610c:4,0xca6110:16,0xca6120:16,0xca6130:16,0xca6140:16,0xca6150:16,0xca6160:16,0xca6170:16,0xca6180:64,0xca61c0:16,0xca61d0:16,0xca61e0:4,0xca61e4:4,0xca61e8:4,0xca61ec:4,0xca61f0:16,0xca6200:4,0xca6204:4,0xca6208:4,0xca620c:4,0xca6210:16,0xca6220:4,0xca6224:4,0xca6228:4,0xca622c:4,0xca6230:16,0xca6240:16,0xca6250:16,0xca6260:4,0xca6264:4,0xca6268:4,0xca626c:4,0xca6270:16,0xca6280:16,0xca6290:16,0xca62a0:4,0xca62a4:4,0xca62a8:4,0xca62ac:4,0xca62b0:16,0xca62c0:4,0xca62c4:4,0xca62c8:4,0xca62cc:4,0xca62d0:16,0xca62e0:4,0xca62e4:4,0xca62e8:4,0xca62ec:4,0xca62f0:16,0xca6300:64,0xca6340:16,0xca6350:16,0xca6360:4,0xca6364:4,0xca6368:4,0xca636c:4,0xca6370:16,0xca6380:16,0xca6390:16,0xca63a0:4,0xca63a4:4,0xca63a8:4,0xca63ac:4,0xca63b0:16,0xca63c0:4,0xca63c4:4,0xca63c8:4,0xca63cc:4,0xca63d0:16,0xca63e0:4,0xca63e4:4,0xca63e8:4,0xca63ec:4,0xca63f0:16,0xca6400:4,0xca6404:4,0xca6408:4,0xca640c:4,0xca6410:16,0xca6420:16,0xca6430:16,0xca6440:4,0xca6444:4,0xca6448:4,0xca644c:4,0xca6450:16,0xca6460:4,0xca6464:4,0xca6468:4,0xca646c:4,0xca6470:16,0xca6480:4,0xca6484:4,0xca6488:4,0xca648c:4,0xca6490:16,0xca64a0:4,0xca64a4:4,0xca64a8:4,0xca64ac:4,0xca64b0:16,0xca64c0:4,0xca64c4:4,0xca64c8:4,0xca64cc:4,0xca64d0:16,0xca64e0:16,0xca64f0:16,0xca6500:64,0xca6540:16,0xca6550:16,0xca6560:16,0xca6570:16,0xca6580:64,0xca65c0:16,0xca65d0:16,0xca65e0:4,0xca65e4:4,0xca65e8:4,0xca65ec:4,0xca65f0:16,0xca6600:16,0xca6610:16,0xca6620:16,0xca6630:16,0xca6640:64,0xca6680:4,0xca6684:4,0xca6688:4,0xca668c:4,0xca6690:16,0xca66a0:16,0xca66b0:16,0xca66c0:4,0xca66c4:4,0xca66c8:4,0xca66cc:4,0xca66d0:16,0xca66e0:4,0xca66e4:4,0xca66e8:4,0xca66ec:4,0xca66f0:16,0xca6700:4,0xca6704:4,0xca6708:4,0xca670c:4,0xca6710:16,0xca6720:16,0xca6730:16,0xca6740:16,0xca6750:16,0xca6760:4,0xca6764:4,0xca6768:4,0xca676c:4,0xca6770:16,0xca6780:64,0xca67c0:16,0xca67d0:16,0xca67e0:4,0xca67e4:4,0xca67e8:4,0xca67ec:4,0xca67f0:16,0xca6800:256,0xca6900:256,0xca6a00:256,0xca6b00:64,0xca6b40:64,0xca6b80:64,0xca6bc0:64,0xca6c00:256,0xca6d00:256,0xca6e00:64,0xca6e40:64,0xca6e80:64,0xca6ec0:64,0xca6f00:64,0xca6f40:64,0xca6f80:16,0xca6f90:16,0xca6fa0:16,0xca6fb0:16,0xca6fc0:64,0xca7000:256,0xca7100:16,0xca7110:16,0xca7120:16,0xca7130:16,0xca7140:64,0xca7180:64,0xca71c0:16,0xca71d0:16,0xca71e0:16,0xca71f0:16,0xca7200:16,0xca7210:16,0xca7220:16,0xca7230:16,0xca7240:64,0xca7280:64,0xca72c0:64,0xca7300:16,0xca7310:16,0xca7320:16,0xca7330:16,0xca7340:64,0xca7380:64,0xca73c0:64,0xca7400:16,0xca7410:16,0xca7420:16,0xca7430:16,0xca7440:16,0xca7450:16,0xca7460:16,0xca7470:16,0xca7480:64,0xca74c0:64,0xca7500:64,0xca7540:64,0xca7580:64,0xca75c0:64,0xca7600:16,0xca7610:16,0xca7620:16,0xca7630:16,0xca7640:64,0xca7680:64,0xca76c0:64,0xca7700:16,0xca7710:16,0xca7720:16,0xca7730:16,0xca7740:16,0xca7750:16,0xca7760:16,0xca7770:16,0xca7780:64,0xca77c0:64,0xca7800:64,0xca7840:64,0xca7880:64,0xca78c0:64,0xca7900:256,0xca7a00:4,0xca7a04:4,0xca7a20:4,0xca7a24:4,0xca7a40:16,0xca7a50:16,0xca7a70:4,0xca7a74:4,0xca7a78:4,0xca7a7c:4,0xca7a80:1,0xca7a84:1,0xca7b60:16,0xca7c10:4,0xca7c14:4,0xca7c18:4,0xca7d70:16,0xca7db0:16,0xca7f00:1,0xca7f01:1,0xca7f02:1,0xca7f03:1,0xca7f04:1,0xca7f05:1,0xca7f06:1,0xca7f07:1,0xca7f0c:4,0xca7f10:16,0xca7f28:4,0xca7f2c:4,0xca7f30:16,0xca7f70:16,0xca7f80:16,0xca7f90:16,0xca7fa0:4,0xca7fa4:4,0xca7fc0:1,0xca7fc1:1,0xca7fc2:1,0xca7fc3:1,0xca7fc4:4,0xca7fc8:4,0xca7fcc:4,0xca7fd0:1,0xca7fd1:1,0xca7fd4:4,0xca7fd8:4,0xca7fdc:4,0xca7fe0:16,0xca7ff0:16,0xca8200:16,0xca8210:16,0xca82e0:16,0xca82f0:16,0xca8310:4,0xca8314:4,0xca8330:16,0xca83d0:16,0xca8520:16,0xca863a:1,0xca8680:16,0xca8830:16,0xca88d0:16,0xca88e0:16,0xca89e7:1,0xca8da0:16,0xca8db0:16,0xca8e10:16,0xca8f04:4,0xca8f10:16,0xca8f20:16,0xca8f38:4,0xca8f3c:4,0xca92a0:16,0xca92bc:4,0xca92c4:4,0xca92c8:4,0xca92cc:4,0xca9390:16,0xca9420:16,0xca9440:16,0xca9450:16,0xca9460:16,0xca9470:16,0xca9520:16,0xca9530:16,0xca95a0:16,0xca95b0:16,0xca95e0:16,0xca95f0:16,0xca9610:16,0xca9620:16,0xca9638:4,0xca96c0:16,0xca96e0:16,0xca96f0:16,0xca9700:4,0xca9780:16,0xca9790:16,0xca98b0:16,0xca9900:4,0xca9930:16,0xca9dc0:16,0xca9dd0:16,0xca9ea0:16,0xca9eb0:16,0xcaa0b0:16,0xcaa243:1,0xcaa24b:1,0xcaa400:16,0xcaa460:16,0xcaa470:16,0xcaa560:16,0xcaa5b0:16,0xcaa5d0:16,0xcaa5ef:1,0xcaa5f0:1,0xcaa5f1:1,0xcaa5f3:1,0xcaa5f5:1,0xcaa5fb:1,0xcaa5fc:4,0xcaa6e0:16,0xcaa6f0:16,0xcaa8a0:16,0xcaa8b0:16,0xcaaa80:16,0xcaaa90:16,0xcaaad8:4,0xcaaadc:4,0xcaaae0:16,0xcaaaf0:16,0xcaabd8:4,0xcaabdc:4,0xcaabeb:1,0xcaac00:4,0xcaad00:4,0xcaad08:4,0xcaad0c:4,0xcaade0:16,0xcaadf0:16,0xcaae40:16,0xcab0e0:16,0xcab0f0:16,0xcab3f0:16,0xcab480:16,0xcab490:16,0xcab4d0:4,0xcab4d4:4,0xcab570:16,0xcab620:16,0xcab6c0:16,0xcab6d0:16,0xcabd00:64,0xcabd50:16,0xcabdb8:4,0xcabdbc:4,0xcabf00:1,0xcabf44:4,0xcabf48:4,0xcabf4c:4,0xcabf50:16,0xcac000:1024,0xcac400:1024,0xcac800:1024,0xcacc00:1024},{0xcb0004:4,0xcb000a:1,0xcb000b:1,0xcb0012:1,0xcb0018:1,0xcb002a:1,0xcb002b:1,0xcb002d:1,0xcb002e:1,0xcb002f:1,0xcb0051:1,0xcb0052:1,0xcb0053:1,0xcb005a:1,0xcb005b:1,0xcb0060:1,0xcb0061:1,0xcb0068:4,0xcb006c:4,0xcb0072:1,0xcb0073:1,0xcb007a:1,0xcb0080:1,0xcb0082:1,0xcb0083:1,0xcb0084:4,0xcb0089:1,0xcb008e:1,0xcb0090:1,0xcb0092:1,0xcb0094:1,0xcb0096:1,0xcb0097:1,0xcb0098:1,0xcb00b1:1,0xcb00e0:1,0xcb0104:4,0xcb0112:1,0xcb011a:1,0xcb011b:1,0xcb0141:1,0xcb0142:1,0xcb0143:1,0xcb0146:1,0xcb0147:1,0xcb014c:1,0xcb014d:1,0xcb015a:1,0xcb0161:1,0xcb0162:1,0xcb0163:1,0xcb0164:4,0xcb016c:1,0xcb01fd:1,0xcb01fe:1,0xcb0240:4,0xcb0244:4,0xcb0249:1,0xcb0270:4,0xcb0274:4,0xcb027e:1,0xcb027f:1,0xcb028c:1,0xcb0296:1,0xcb0298:4,0xcb029c:1,0xcb029d:1,0xcb02a0:4,0xcb02a4:4,0xcb02b4:1,0xcb02b5:1,0xcb02c4:1,0xcb02c5:1,0xcb02d1:1,0xcb02d6:1,0xcb02d7:1,0xcb02e2:1,0xcb02e3:1,0xcb02e5:1,0xcb02ec:1,0xcb02ed:1,0xcb0344:1,0xcb0348:1,0xcb0349:1,0xcb034b:1,0xcb0350:4,0xcb0354:4,0xcb0360:4,0xcb0369:1,0xcb0370:4,0xcb0374:4,0xcb0378:1,0xcb037b:1,0xcb0387:1,0xcb038b:1,0xcb038f:1,0xcb0484:1,0xcb0485:1,0xcb0486:1,0xcb0497:1,0xcb0498:4,0xcb04ae:1,0xcb04af:1,0xcb04b4:1,0xcb04ba:1,0xcb04cd:1,0xcb04d0:4,0xcb04e3:1,0xcb04e6:1,0xcb04e7:1,0xcb0504:1,0xcb0505:1,0xcb0507:1,0xcb0508:1,0xcb0509:1,0xcb050b:1,0xcb0515:1,0xcb0516:1,0xcb052c:1,0xcb052e:1,0xcb052f:1,0xcb0534:4,0xcb0538:1,0xcb0539:1,0xcb053c:1,0xcb053d:1,0xcb0572:1,0xcb0573:1,0xcb0576:1,0xcb0578:1,0xcb05ac:1,0xcb05b4:1,0xcb05b5:1,0xcb05b6:1,0xcb05b9:1,0xcb05ba:1,0xcb05bc:1,0xcb05bd:1,0xcb05be:1,0xcb05c3:1,0xcb05d6:1,0xcb05d7:1,0xcb05da:1,0xcb05db:1,0xcb0683:1,0xcb0688:1,0xcb068a:1,0xcb068b:1,0xcb068e:1,0xcb0696:1,0xcb0697:1,0xcb069d:1,0xcb069f:1,0xcb06e0:16,0xcb06f8:1,0xcb06f9:1,0xcb0781:1,0xcb078a:1,0xcb078b:1,0xcb0793:1,0xcb0796:1,0xcb0797:1,0xcb079e:1,0xcb07c0:1,0xcb07c1:1,0xcb07c8:1,0xcb0800:1,0xcb0808:1,0xcb0817:1,0xcb0818:4,0xcb081c:4,0xcb0846:1,0xcb0852:1,0xcb0856:1,0xcb0857:1,0xcb085b:1,0xcb086e:1,0xcb086f:1,0xcb0873:1,0xcb08a6:1,0xcb08a7:1,0xcb08a9:1,0xcb08ad:1,0xcb08b8:1,0xcb08ba:1,0xcb08bb:1,0xcb08be:1,0xcb08bf:1,0xcb08c0:1,0xcb08c5:1,0xcb08c6:1,0xcb08c7:1,0xcb08cb:1,0xcb08d1:1,0xcb08d2:1,0xcb08d3:1,0xcb08d4:4,0xcb08d9:1,0xcb08dc:1,0xcb0920:1,0xcb0924:1,0xcb0925:1,0xcb0939:1,0xcb093f:1,0xcb0941:1,0xcb0946:1,0xcb0947:1,0xcb0948:1,0xcb094b:1,0xcb094c:1,0xcb094d:1,0xcb0960:4,0xcb0964:1,0xcb0965:1,0xcb096c:1,0xcb099e:1,0xcb0a22:1,0xcb0a38:1,0xcb0a4a:1,0xcb0a4b:1,0xcb0a54:4,0xcb0a58:1,0xcb0a5f:1,0xcb0a7d:1,0xcb0b46:1,0xcb0b4c:4,0xcb0b52:1,0xcb0b54:4,0xcb0b64:4,0xcb0b6d:1,0xcb0b75:1,0xcb0b7a:1,0xcb0b7e:1,0xcb0b88:4,0xcb0b8d:1,0xcb0b8e:1,0xcb0b8f:1,0xcb0bb4:4,0xcb0bd0:4,0xcb0c10:1,0xcb0c13:1,0xcb0c18:1,0xcb0c39:1,0xcb0c41:1,0xcb0c42:1,0xcb0c46:1,0xcb0c47:1,0xcb0c57:1,0xcb0c58:4,0xcb0c5c:4,0xcb0c64:1,0xcb0c65:1,0xcb0c67:1,0xcb0c72:1,0xcb0c76:1,0xcb0c82:1,0xcb0c89:1,0xcb0cc4:4,0xcb0cc8:4,0xcb0ccc:4,0xcb0cd3:1,0xcb0cdb:1,0xcb0ce2:1,0xcb0cf0:4,0xcb0d12:1,0xcb0d18:1,0xcb0d2c:1,0xcb0d2d:1,0xcb0d50:4,0xcb0d54:4,0xcb0d58:1,0xcb0d59:1,0xcb0d5c:4,0xcb0dad:1,0xcb0de0:1,0xcb0de1:1,0xcb0de3:1,0xcb0de9:1,0xcb0e18:4,0xcb0e21:1,0xcb0e38:1,0xcb0e3d:1,0xcb0e3e:1,0xcb0e68:1,0xcb0e72:1,0xcb0e73:1,0xcb0e76:1,0xcb0ea2:1,0xcb0eb8:4,0xcb0ebc:4,0xcb0ec0:1,0xcb0ec2:1,0xcb0ec3:1,0xcb0ed6:1,0xcb0ee7:1,0xcb0ef6:1,0xcb0f00:16,0xcb0f14:1,0xcb0f15:1,0xcb0f16:1,0xcb0f57:1,0xcb0f58:1,0xcb0f59:1,0xcb0f69:1,0xcb0f70:4,0xcb0f74:4,0xcb0f82:1,0xcb0f83:1,0xcb0f95:1,0xcb0f97:1,0xcb0f9c:4,0xcb0fae:1,0xcb0fe3:1,0xcb0fe8:4,0xcb0fec:4,0xcb0ff0:1,0xcb0ff1:1,0xcb0ff6:1,0xcb100a:1,0xcb100c:1,0xcb100d:1,0xcb1010:4,0xcb1014:4,0xcb101b:1,0xcb1026:1,0xcb1031:1,0xcb1032:1,0xcb1033:1,0xcb103a:1,0xcb1085:1,0xcb10a1:1,0xcb10a2:1,0xcb10ba:1,0xcb10bb:1,0xcb10e4:1,0xcb10ee:1,0xcb10f0:1,0xcb10f5:1,0xcb1102:1,0xcb1112:1,0xcb111c:1,0xcb1127:1,0xcb1138:1,0xcb114a:1,0xcb114b:1,0xcb1158:1,0xcb1159:1,0xcb1188:1,0xcb11a4:1,0xcb11bb:1,0xcb11be:1,0xcb11bf:1,0xcb11e7:1,0xcb11e9:1,0xcb11f8:1,0xcb11ff:1,0xcb1202:1,0xcb1203:1,0xcb1204:1,0xcb1207:1,0xcb121f:1,0xcb1225:1,0xcb1230:1,0xcb1231:1,0xcb1232:1,0xcb1234:1,0xcb1248:4,0xcb1250:1,0xcb1251:1,0xcb1257:1,0xcb1264:1,0xcb1265:1,0xcb1269:1,0xcb126b:1,0xcb126e:1,0xcb1281:1,0xcb1283:1,0xcb1284:1,0xcb1285:1,0xcb1290:1,0xcb1299:1,0xcb12c7:1,0xcb12d0:1,0xcb12d3:1,0xcb12d7:1,0xcb1312:1,0xcb1318:1,0xcb131e:1,0xcb1320:4,0xcb1324:4,0xcb1329:1,0xcb132c:1,0xcb132d:1,0xcb132e:1,0xcb133a:1,0xcb133c:1,0xcb133d:1,0xcb1340:1,0xcb1344:1,0xcb1348:1,0xcb1365:1,0xcb136f:1,0xcb1383:1,0xcb1385:1,0xcb1390:1,0xcb1395:1,0xcb139c:1,0xcb13b0:1,0xcb13b2:1,0xcb13b3:1,0xcb13d0:1,0xcb13e4:4,0xcb13e9:1,0xcb13f2:1,0xcb13f8:1,0xcb13f9:1,0xcb13ff:1,0xcb1411:1,0xcb1428:1,0xcb1429:1,0xcb1430:1,0xcb143d:1,0xcb1441:1,0xcb1454:1,0xcb1455:1,0xcb1459:1,0xcb146a:1,0xcb146b:1,0xcb1473:1,0xcb1475:1,0xcb1476:1,0xcb1477:1,0xcb147a:1,0xcb147e:1,0xcb147f:1,0xcb1487:1,0xcb1488:4,0xcb148c:4,0xcb1496:1,0xcb14e6:1,0xcb14e8:1,0xcb14ec:1,0xcb1500:1,0xcb1501:1,0xcb1502:1,0xcb1508:1,0xcb150a:1,0xcb1512:1,0xcb1521:1,0xcb1522:1,0xcb1529:1,0xcb152c:1,0xcb1544:1,0xcb1552:1,0xcb1560:4,0xcb157c:1,0xcb1588:1,0xcb1589:1,0xcb1591:1,0xcb15ce:1,0xcb1618:1,0xcb161c:1,0xcb161d:1,0xcb161f:1,0xcb1644:1,0xcb164c:1,0xcb164e:1,0xcb1654:1,0xcb1657:1,0xcb165c:4,0xcb1663:1,0xcb166a:1,0xcb167a:1,0xcb167b:1,0xcb1683:1,0xcb16a3:1,0xcb16a6:1,0xcb16aa:1,0xcb16b0:4,0xcb16b4:4,0xcb16c2:1,0xcb16f2:1,0xcb16f3:1,0xcb16f5:1,0xcb16f6:1,0xcb16fc:1,0xcb16fd:1,0xcb1700:1,0xcb172f:1,0xcb173d:1,0xcb173e:1,0xcb173f:1,0xcb1749:1,0xcb1755:1,0xcb175c:4,0xcb1762:1,0xcb176b:1,0xcb1770:1,0xcb1782:1,0xcb178c:1,0xcb178d:1,0xcb17ac:1,0xcb17b6:1,0xcb17ba:1,0xcb17bb:1,0xcb17c0:1,0xcb17c5:1,0xcb17c6:1,0xcb17cc:4,0xcb17e0:1,0xcb17e2:1,0xcb17e3:1,0xcb17e4:4,0xcb17f9:1,0xcb17fb:1,0xcb180d:1,0xcb1812:1,0xcb181b:1,0xcb182b:1,0xcb1838:1,0xcb183a:1,0xcb1843:1,0xcb184a:1,0xcb184f:1,0xcb1850:1,0xcb1851:1,0xcb1854:1,0xcb1855:1,0xcb1856:1,0xcb185a:1,0xcb186f:1,0xcb1870:1,0xcb1874:1,0xcb187a:1,0xcb187b:1,0xcb1891:1,0xcb1898:1,0xcb1899:1,0xcb189d:1,0xcb18a1:1,0xcb18a7:1,0xcb18ba:1,0xcb18bb:1,0xcb18c7:1,0xcb18ca:1,0xcb18d4:1,0xcb18d5:1,0xcb18d9:1,0xcb18db:1,0xcb18f4:1,0xcb1913:1,0xcb1914:1,0xcb1915:1,0xcb192e:1,0xcb1930:4,0xcb1934:4,0xcb1940:1,0xcb1941:1,0xcb195b:1,0xcb1963:1,0xcb1964:1,0xcb196a:1,0xcb1983:1,0xcb1987:1,0xcb198a:1,0xcb1993:1,0xcb1999:1,0xcb199a:1,0xcb199b:1,0xcb19a4:1,0xcb19a6:1,0xcb19ae:1,0xcb19af:1,0xcb19b4:1,0xcb19b6:1,0xcb19bf:1,0xcb19c7:1,0xcb19c8:1,0xcb19ca:1,0xcb19cb:1,0xcb19d0:16,0xcb19e5:1,0xcb19eb:1,0xcb19ec:1,0xcb19f2:1,0xcb1a0c:1,0xcb1a22:1,0xcb1a31:1,0xcb1a32:1,0xcb1a37:1,0xcb1a38:1,0xcb1a39:1,0xcb1a3c:1,0xcb1a41:1,0xcb1a44:1,0xcb1a4c:1,0xcb1a50:1,0xcb1a54:1,0xcb1a61:1,0xcb1a66:1,0xcb1a67:1,0xcb1a73:1,0xcb1a74:1,0xcb1a81:1,0xcb1a8f:1,0xcb1a90:1,0xcb1a94:1,0xcb1a95:1,0xcb1a9a:1,0xcb1a9e:1,0xcb1a9f:1,0xcb1aaa:1,0xcb1aad:1,0xcb1ab0:1,0xcb1ab9:1,0xcb1aca:1,0xcb1acb:1,0xcb1ad2:1,0xcb1ad6:1,0xcb1ade:1,0xcb1ae0:1,0xcb1ae4:1,0xcb1ae8:1,0xcb1b00:1,0xcb1b0a:1,0xcb1b0f:1,0xcb1b10:1,0xcb1b14:1,0xcb1b16:1,0xcb1b17:1,0xcb1b28:1,0xcb1b2d:1,0xcb1b35:1,0xcb1b41:1,0xcb1b42:1,0xcb1b51:1,0xcb1b58:1,0xcb1b66:1,0xcb1b6d:1,0xcb1b75:1,0xcb1b79:1,0xcb1b7a:1,0xcb1b7b:1,0xcb1b7d:1,0xcb1bc8:1,0xcb1bca:1,0xcb1be9:1,0xcb1bf1:1,0xcb1bfa:1,0xcb1c0a:1,0xcb1c0c:1,0xcb1c21:1,0xcb1c22:1,0xcb1c23:1,0xcb1c2b:1,0xcb1c2c:1,0xcb1c36:1,0xcb1c38:1,0xcb1c49:1,0xcb1c4a:1,0xcb1c4c:1,0xcb1c56:1,0xcb1c58:1,0xcb1c70:1,0xcb1c83:1,0xcb1c88:1,0xcb1c8c:1,0xcb1c91:1,0xcb1ca5:1,0xcb1ca9:1,0xcb1caa:1,0xcb1cb2:1,0xcb1cb3:1,0xcb1cb9:1,0xcb1cbb:1,0xcb1cc4:1,0xcb1ce2:1,0xcb1ce3:1,0xcb1cef:1,0xcb1d02:1,0xcb1d08:1,0xcb1d09:1,0xcb1d0d:1,0xcb1d0e:1,0xcb1d1c:1,0xcb1d2e:1,0xcb1d39:1,0xcb1d3d:1,0xcb1d3f:1,0xcb1d45:1,0xcb1d49:1,0xcb1d51:1,0xcb1d5a:1,0xcb1d5f:1,0xcb1d64:1,0xcb1d67:1,0xcb1d70:1,0xcb1d78:4,0xcb1db6:1,0xcb1db7:1,0xcb1dbb:1,0xcb1dbd:1,0xcb1dbe:1,0xcb1dcd:1,0xcb1dd2:1,0xcb1dd9:1,0xcb1de3:1,0xcb1de7:1,0xcb1de9:1,0xcb1dea:1,0xcb1df8:1,0xcb1dfe:1,0xcb1dff:1,0xcb1e10:1,0xcb1e11:1,0xcb1e19:1,0xcb1e1b:1,0xcb1e1d:1,0xcb1e42:1,0xcb1e51:1,0xcb1e57:1,0xcb1e6f:1,0xcb1e79:1,0xcb1e7b:1,0xcb1e98:1,0xcb1e9c:1,0xcb1ea2:1,0xcb1ead:1,0xcb1eaf:1,0xcb1ebb:1,0xcb1ec2:1,0xcb1ed9:1,0xcb1edc:1,0xcb1ede:1,0xcb1ee8:1,0xcb1ee9:1,0xcb1eeb:1,0xcb1ef0:1,0xcb1ef1:1,0xcb1ef6:1,0xcb1efa:1,0xcb1efb:1,0xcb1f2d:1,0xcb1f2e:1,0xcb1f31:1,0xcb1f33:1,0xcb1f36:1,0xcb1f37:1,0xcb1f45:1,0xcb1f48:1,0xcb1f50:1,0xcb1f55:1,0xcb1f61:1,0xcb1f69:1,0xcb1f6a:1,0xcb1f6c:1,0xcb1f6d:1,0xcb1f7c:1,0xcb1fa2:1,0xcb1fae:1,0xcb1fb1:1,0xcb1fb5:1,0xcb1fbb:1,0xcb1fbd:1,0xcb1fcc:1,0xcb1fdc:1,0xcb1fde:1,0xcb1fdf:1,0xcb1fe1:1,0xcb1fe5:1,0xcb1ff8:1,0xcb1ff9:1,0xcb1ffd:1,0xcb2014:1,0xcb2030:1,0xcb2031:1,0xcb2038:1,0xcb203c:1,0xcb203e:1,0xcb2044:1,0xcb2045:1,0xcb204c:1,0xcb2051:1,0xcb2054:1,0xcb2055:1,0xcb205f:1,0xcb2066:1,0xcb2069:1,0xcb2082:1,0xcb2085:1,0xcb208c:1,0xcb2098:1,0xcb20ba:1,0xcb20bb:1,0xcb20c0:1,0xcb20c4:1,0xcb20cb:1,0xcb20cc:1,0xcb20cd:1,0xcb20d4:1,0xcb2104:1,0xcb2107:1,0xcb2108:4,0xcb210c:4,0xcb2115:1,0xcb211a:1,0xcb2120:1,0xcb213f:1,0xcb2140:1,0xcb2143:1,0xcb2144:1,0xcb2149:1,0xcb214f:1,0xcb2164:1,0xcb217a:1,0xcb2181:1,0xcb2183:1,0xcb2191:1,0xcb219c:1,0xcb219e:1,0xcb219f:1,0xcb21ae:1,0xcb21b9:1,0xcb21c8:1,0xcb21ca:1,0xcb21cb:1,0xcb21cc:1,0xcb21ce:1,0xcb21cf:1,0xcb21d6:1,0xcb21d7:1,0xcb21e0:1,0xcb21e1:1,0xcb21e2:1,0xcb21e9:1,0xcb21f3:1,0xcb21fa:1,0xcb2204:1,0xcb2215:1,0xcb221b:1,0xcb2227:1,0xcb2230:1,0xcb2231:1,0xcb2236:1,0xcb2238:1,0xcb2239:1,0xcb2243:1,0xcb2245:1,0xcb224c:1,0xcb225c:1,0xcb226a:1,0xcb2271:1,0xcb2293:1,0xcb2296:1,0xcb2298:1,0xcb2299:1,0xcb22a1:1,0xcb22a2:1,0xcb22bb:1,0xcb22c0:4,0xcb22c4:4,0xcb22cc:4,0xcb22e8:1,0xcb22f0:1,0xcb22f2:1,0xcb22f5:1,0xcb22fb:1,0xcb3702:1,0xcb3703:1,0xcb3704:1,0xcb370a:1,0xcb370d:1,0xcb3716:1,0xcb371e:1,0xcb375d:1,0xcb3765:1,0xcb376d:1,0xcb376e:1,0xcb3774:1,0xcb3775:1,0xcb3777:1,0xcb3780:1,0xcb3781:1,0xcb3792:1,0xcb3793:1,0xcb37c0:1,0xcb37c4:1,0xcb37da:1,0xcb37db:1,0xcb37dd:1,0xcb37e0:1,0xcb3801:1,0xcb3804:1,0xcb380c:1,0xcb3818:1,0xcb3826:1,0xcb3828:1,0xcb382e:1,0xcb3830:4,0xcb3834:4,0xcb3844:1,0xcb3845:1,0xcb3852:1,0xcb3853:1,0xcb3854:1,0xcb3855:1,0xcb385f:1,0xcb386e:1,0xcb3879:1,0xcb38a1:1,0xcb38a9:1,0xcb38ac:1,0xcb38ad:1,0xcb38af:1,0xcb38b7:1,0xcb38b9:1,0xcb38bb:1,0xcb38c0:1,0xcb38c6:1,0xcb38c9:1,0xcb38d0:1,0xcb38d1:1,0xcb38d2:1,0xcb38d6:1,0xcb38d8:1,0xcb38e3:1,0xcb38e4:1,0xcb38e8:1,0xcb38f0:1,0xcb38fc:1,0xcb38fe:1,0xcb3905:1,0xcb3906:1,0xcb390c:1,0xcb390d:1,0xcb391c:1,0xcb3927:1,0xcb392e:1,0xcb393a:1,0xcb393d:1,0xcb3942:1,0xcb3945:1,0xcb3946:1,0xcb3947:1,0xcb3949:1,0xcb395a:1,0xcb3965:1,0xcb396d:1,0xcb397b:1,0xcb399d:1,0xcb39c8:1,0xcb39ca:1,0xcb39ce:1,0xcb39de:1,0xcb39e0:16,0xcb39f6:1,0xcb39f7:1,0xcb39f9:1,0xcb39fd:1,0xcb39fe:1,0xcb39ff:1,0xcb3e02:1,0xcb3e83:1,0xcb3e8b:1,0xcb3ea1:1,0xcb3ec5:1,0xcb3ee4:4,0xcb3eea:1,0xcb3ef6:1,0xcb4ca0:4,0xcb4ca8:4,0xcb4db4:4,0xcb4e30:16,0xcb4f00:16,0xcb4f20:16,0xcb5004:1,0xcb5005:1,0xcb5020:16,0xcb5039:1,0xcb5084:4,0xcb5088:4,0xcb508c:4,0xcb5090:16,0xcb5100:4,0xcb5104:4,0xcb5110:16,0xcb5200:1,0xcb5201:1,0xcb5210:4,0xcb5214:4,0xcb5300:4,0xcb5338:4,0xcb533c:4,0xcb53e0:16,0xcb5600:16,0xcb5610:16,0xcb5620:16,0xcb5630:16,0xcb5640:16,0xcb5650:16,0xcb5660:16,0xcb5670:16,0xcb56fe:1,0xcb56ff:1,0xcb5820:16,0xcb5830:16,0xcb58c0:16,0xcb58d0:16,0xcb5900:4,0xcb5908:4,0xcb590c:4,0xcb5988:4,0xcb5a00:4,0xcb5a08:4,0xcb5a80:16,0xcb5a90:16,0xcb5aa0:16,0xcb5ab0:16,0xcb5ac0:16,0xcb5ad0:16,0xcb5b20:16,0xcb5b30:16,0xcb5b60:16,0xcb5b78:4,0xcb5b7c:4,0xcb5c00:4,0xcb5ca0:16,0xcb5cb0:16,0xcb5d00:4,0xcb5d04:4,0xcb5d08:1,0xcb5d09:1,0xcb5d0a:1,0xcb5d0b:1,0xcb5d0c:4,0xcb5d10:16,0xcb5d20:16,0xcb5d30:16,0xcb5d40:64,0xcb5d80:4,0xcb5d84:4,0xcb5d88:4,0xcb5d8c:1,0xcb5d8d:1,0xcb5d8e:1,0xcb5d8f:1,0xcb5d90:16,0xcb5da0:16,0xcb5db0:16,0xcb5dc0:64,0xcb5e00:4,0xcb5e04:4,0xcb5e08:4,0xcb5e0c:4,0xcb5e10:16,0xcb5f00:4,0xcb5f04:4,0xcb5f60:16,0xcb5f70:16,0xcb5f80:64,0xcb5fe0:16,0xcb5ff0:16,0xcb6308:4,0xcb630c:4,0xcb6310:16,0xcb6350:16,0xcb6420:16,0xcb6430:4,0xcb6434:4,0xcb643f:1,0xcb6450:16,0xcb6460:16,0xcb6470:16,0xcb64c0:16,0xcb6820:16,0xcb6960:16,0xcb6970:16,0xcb6980:16,0xcb6990:16,0xcb6b00:64,0xcb6b40:64,0xcb6ea0:16,0xcb6eb0:16,0xcb6ed0:16,0xcb6ee8:1,0xcb6ee9:1,0xcb6eea:1,0xcb72f4:4,0xcb76c0:16,0xcb76d0:16,0xcb76f1:1,0xcb76f8:4,0xcb7718:4,0xcb771c:4,0xcb7720:4,0xcb7750:4,0xcb7755:1,0xcb7771:1,0xcb7772:1,0xcb7773:1,0xcb7774:4,0xcb7778:4,0xcb777c:4,0xcb7780:64,0xcb77c0:64,0xcb8020:16,0xcb8030:16,0xcb8060:16,0xcb8070:16,0xcb80e0:4,0xcb80e4:4,0xcb8108:4,0xcb810c:4,0xcb8220:16,0xcb8230:16,0xcb8420:16,0xcb8430:16,0xcb86f0:4,0xcb86f4:4,0xcb8760:16,0xcb8770:16,0xcb87a0:16,0xcb8ee0:16,0xcb8ef0:16,0xcb9060:16,0xcb9070:16,0xcb9100:16,0xcb9110:16,0xcb9400:64,0xcb9440:16,0xcb9450:4,0xcb9456:1,0xcb9457:1,0xcb955c:4,0xcb9840:16,0xcb9850:16,0xcb9880:16,0xcb9890:16,0xcb9900:4,0xcb9cc0:64,0xcb9e10:4,0xcb9e14:4,0xcba068:4,0xcba06c:4,0xcba081:1,0xcba0c0:16,0xcba0d0:16,0xcba100:4,0xcba1b4:1,0xcba1c0:16,0xcba1d0:16,0xcba6a0:16,0xcba6b0:16,0xcba800:16,0xcba810:16,0xcbaa3a:1,0xcbaa3b:1,0xcbab00:4,0xcbabe0:16,0xcbae04:1,0xcbae07:1,0xcbae60:16,0xcbae70:16,0xcbaf80:16,0xcbaf90:16,0xcbafc0:64,0xcbb000:64,0xcbb040:16,0xcbb050:16,0xcbb0a8:4,0xcbb0ac:4,0xcbb850:16,0xcbbba0:16,0xcbbbb0:16,0xcbbd00:1,0xcbbd01:1,0xcbbd06:1,0xcbbd07:1,0xcbbd70:4,0xcbbdc0:16,0xcbbdd0:16,0xcbbe60:16,0xcbbef9:1,0xcbbf00:1,0xcbbf01:1,0xcbbf10:16,0xcbbf40:64,0xcbbf90:4,0xcbbf94:4,0xcbbf98:4,0xcbbf9c:4,0xcbc000:16,0xcbc010:16,0xcbc1e0:16,0xcbc1f0:16,0xcbc278:4,0xcbc27c:4,0xcbc340:16,0xcbc350:16,0xcbc370:4,0xcbc374:4,0xcbc380:64,0xcbc3c0:64,0xcbc400:4,0xcbc404:4,0xcbc408:4,0xcbc40c:4,0xcbcaec:4,0xcbcd40:16,0xcbcd50:16,0xcbcd80:64,0xcbcdc0:64,0xcbcf40:64,0xcbcf80:64,0xcbcfc0:64,0xcbd000:16,0xcbd010:4,0xcbd020:16,0xcbd030:16,0xcbd1e0:16,0xcbd1f0:16,0xcbd400:16,0xcbd450:16,0xcbd7e8:4,0xcbd7ec:4,0xcbdec0:16,0xcbdf00:16,0xcbdf10:4,0xcbdf14:4},{},{},{},{},{},{},{0xd20200:16,0xd20210:16,0xd20500:16,0xd20510:16,0xd20538:4,0xd2053c:4,0xd20580:16,0xd20590:16,0xd20c00:64,0xd20c40:64,0xd20c80:64,0xd20cc0:64,0xd20d00:64,0xd20d40:64,0xd20d80:64,0xd20dc0:64,0xd20e40:16,0xd20e50:16,0xd20e70:16,0xd20e80:16,0xd20e90:16,0xd20ea0:16,0xd20eb0:16,0xd20ec0:16,0xd20ed0:16,0xd20ee0:16,0xd20ef0:16,0xd20f00:16,0xd20f10:16,0xd20f20:16,0xd20f30:16,0xd20f40:16,0xd20f50:16,0xd20f60:16,0xd20f70:16,0xd20f80:64,0xd21080:64,0xd21500:64,0xd21540:64,0xd21580:64,0xd215c0:64,0xd21600:256,0xd21720:16,0xd21730:16,0xd21900:256,0xd21a00:256,0xd21b00:256,0xd21c00:1024,0xd22000:1024,0xd22400:1024,0xd22800:1024,0xd22c00:1024,0xd23088:4,0xd2308c:4,0xd23300:256,0xd23400:64,0xd23440:64,0xd23480:64,0xd234c0:64,0xd23500:64,0xd23540:64,0xd23580:64,0xd235c0:64,0xd238c0:16,0xd238d0:16,0xd24800:64,0xd24840:64,0xd24880:16,0xd24890:16,0xd248a0:16,0xd248b0:16,0xd248c0:64,0xd24900:16,0xd24910:16,0xd24920:16,0xd24930:16,0xd24940:64,0xd24980:64,0xd249c0:64,0xd24a00:16,0xd24a10:16,0xd24a20:16,0xd24a30:16,0xd24a40:16,0xd24a50:16,0xd24a60:16,0xd24a70:16,0xd24a80:16,0xd24a90:16,0xd24aa0:16,0xd24ab0:16,0xd24ac0:64,0xd24b00:256,0xd24c00:16,0xd24c10:16,0xd24c20:16,0xd24c30:16,0xd24c40:64,0xd24c80:64,0xd24cc0:64,0xd24d00:256,0xd24e00:16,0xd24e10:16,0xd24e20:16,0xd24e30:16,0xd24e40:64,0xd24e80:16,0xd24e90:16,0xd24ea0:16,0xd24eb0:16,0xd24ec0:64,0xd24f40:64,0xd24fe0:16,0xd24ff0:16,0xd25200:256,0xd25300:256,0xd25780:16,0xd25790:16,0xd257a0:16,0xd257b0:16,0xd2b9c0:64,0xd2c060:16,0xd2c070:16},{0xd34000:1024,0xd34400:256,0xd34500:256,0xd34600:256,0xd34700:256,0xd35000:256,0xd35100:256,0xd35200:256,0xd35300:256,0xd35400:256,0xd35500:256,0xd35600:256,0xd35700:256,0xd35800:256,0xd35900:256,0xd35a00:256,0xd35b00:256,0xd35c00:256,0xd35d00:256,0xd35e00:256,0xd35f00:256,0xd36000:256,0xd36100:256,0xd36200:256,0xd36300:64,0xd36340:16,0xd36350:16,0xd36360:16,0xd36370:16,0xd36380:64,0xd363c0:64,0xd36400:256,0xd36500:64,0xd36540:64,0xd36580:64,0xd365c0:64,0xd36600:256,0xd36700:64,0xd36740:64,0xd36780:64,0xd367c0:64,0xd38800:1024,0xd38c00:256,0xd38d00:256,0xd38e00:64,0xd38e40:64,0xd38e80:64,0xd38ec0:64,0xd38f00:256,0xd39000:256,0xd39100:256,0xd39200:256,0xd39300:256,0xd39400:1024,0xd39800:256,0xd39900:256,0xd39a00:256,0xd39b00:64,0xd39b40:16,0xd39b50:16,0xd39b60:16,0xd39b70:16,0xd39b80:64,0xd39bc0:64,0xd39c00:1024,0xd3a000:1024,0xd3a400:1024},{},{},{},{},{},{},{0xda0000:256,0xda0100:256,0xda0200:256,0xda0300:256,0xda0400:256,0xda0500:256,0xda0600:256,0xda0700:256,0xda0800:256,0xda0900:256,0xda0a00:256,0xda0b00:256,0xda0c00:256,0xda0d00:256,0xda0e00:256,0xda0f00:256,0xda1000:1024,0xda1400:256,0xda1500:64,0xda1540:64,0xda1580:64,0xda15c0:64,0xda1600:256,0xda1700:256,0xda1800:256,0xda1900:256,0xda1a00:256,0xda1b00:256,0xda1c00:256,0xda1d00:256,0xda1e00:256,0xda1f00:256,0xda3800:1024,0xda3c00:256,0xda3d00:256,0xda3e00:64,0xda3e40:64,0xda3e80:64,0xda3ec0:64,0xda3f00:256,0xda4000:256,0xda4100:256,0xda4200:256,0xda4300:64,0xda4340:64,0xda4380:64,0xda43c0:64,0xda4400:256,0xda4500:256,0xda4600:256,0xda4700:256,0xda4800:1024,0xda4c00:256,0xda4d00:256,0xda4e00:256,0xda4f00:256,0xda5000:1024,0xda5400:1024,0xda5800:1024,0xda5c00:1024,0xda6000:256,0xda6100:256,0xda6200:64,0xda6240:64,0xda6280:64,0xda62c0:16,0xda62d0:16,0xda62e0:16,0xda62f0:16,0xda6300:256,0xda6458:4,0xda645c:4,0xda6460:16,0xda6470:16,0xda6480:64,0xda64c0:64,0xda6800:64,0xda6840:64,0xda6880:16,0xda6890:16,0xda68a0:16,0xda68b0:16,0xda68c0:4,0xda68c4:4,0xda68c8:4,0xda68cc:4,0xda68d0:16,0xda68e0:16,0xda68f0:16,0xda6900:256,0xda6a00:256,0xda6b00:256,0xda6c00:256,0xda6d00:256,0xdab9c0:16,0xdab9d0:16,0xdab9f0:4,0xdab9f4:4,0xdac000:256,0xdac100:256,0xdac200:256,0xdac300:256,0xdac400:1024,0xdac800:1024,0xdacc00:256,0xdacd00:256,0xdace00:256,0xdacf00:256,0xdaf000:1024,0xdaf400:256,0xdaf500:256,0xdaf600:256,0xdaf700:256,0xdaf900:256},{0xdb4800:256,0xdb5200:256,0xdb5380:64,0xdb53c0:64,0xdb8000:1024,0xdb8400:1024,0xdb8800:1024,0xdb8c00:1024,0xdb9000:1024,0xdb9400:256,0xdb9500:64,0xdb9540:64,0xdb9580:64,0xdb95c0:64,0xdb9600:16,0xdb9610:16,0xdb9620:16,0xdb9630:16,0xdb9640:16,0xdb9650:16,0xdb9660:16,0xdb9670:16,0xdb9680:64,0xdb96c0:64,0xdb9700:16,0xdb9710:16,0xdb9720:16,0xdb9730:16,0xdb9740:64,0xdb9780:64,0xdb97c0:64,0xdb9800:256,0xdb9900:256,0xdb9a00:256,0xdb9b00:256,0xdb9c00:256,0xdb9d00:256,0xdb9e00:64,0xdb9e40:64,0xdb9e80:64,0xdb9ec0:64,0xdb9f00:64,0xdb9f40:64,0xdb9f80:64,0xdb9fc0:64,0xdbd800:256,0xdbd900:256,0xdbda00:256,0xdbdb00:256,0xdbdc00:256,0xdbdd00:256,0xdbde00:256,0xdbdf00:256,0xdbe000:256,0xdbe100:256,0xdbe200:256,0xdbe300:256,0xdbe400:256,0xdbe500:256,0xdbe600:256,0xdbe700:256,0xdbe800:1024,0xdbec00:256,0xdbed00:256,0xdbee00:256,0xdbef00:256,0xdbf200:256,0xdbf300:256,0xdbf400:1024},{0xdc65c0:64,0xdc7000:1024,0xdc9880:64,0xdc98c0:64,0xdc9a00:256,0xdc9b00:256,0xdca000:1024,0xdca400:1024,0xdca800:1024,0xdcac00:1024,0xdcb000:1024,0xdcb400:1024,0xdcb800:1024,0xdcbc00:1024,0xdcc000:256,0xdcc100:256,0xdcc200:256,0xdcc300:256,0xdcc400:1024,0xdcc800:1024,0xdccc00:1024,0xdce700:64,0xdce780:64,0xdce7c0:64,0xdce840:64,0xdcea00:256,0xdcf200:256,0xdcf300:256,0xdcf788:4,0xdcf78c:4,0xdcf800:1024,0xdcfc00:256},{0xdd0000:256,0xdd0100:256,0xdd0200:256,0xdd0300:64,0xdd0340:64,0xdd0380:64,0xdd03c0:64,0xdd0400:256,0xdd0500:64,0xdd0540:64,0xdd0580:64,0xdd05c0:64,0xdd0600:256,0xdd0700:16,0xdd0710:16,0xdd0720:16,0xdd0730:16,0xdd0740:16,0xdd0750:16,0xdd0760:16,0xdd0770:16,0xdd0780:64,0xdd07c0:64,0xdd0800:256,0xdd0900:256,0xdd0a00:256,0xdd0b00:64,0xdd0b40:64,0xdd0b80:64,0xdd0bc0:16,0xdd0bd0:16,0xdd0be0:16,0xdd0bf0:16,0xdd0c00:64,0xdd0c40:64,0xdd0c80:64,0xdd0d00:64,0xdd0d40:16,0xdd0d50:16,0xdd0d60:16,0xdd0d70:16,0xdd0d80:64,0xdd0dc0:64,0xdd0e00:256,0xdd0f00:256,0xdd7a00:256,0xdd7b00:256,0xdd8080:64,0xdd80c0:64,0xdd8100:256,0xdd8200:256,0xdd8300:256,0xdd85e0:16,0xdd85f0:16,0xdd8800:256,0xdd8900:256,0xddac00:1024,0xddb000:1024,0xddb400:1024,0xddc000:256,0xddc100:256,0xddc200:256,0xddc300:256,0xddc400:256,0xddc500:256,0xddc600:256,0xddc700:16,0xddc710:16,0xddc720:16,0xddc730:16,0xddc740:64,0xddc780:64,0xddc7c0:16,0xddc7e0:16,0xddc7f0:16,0xddc800:1024,0xddcc00:256,0xddcd00:256,0xddce00:256,0xddcf00:64,0xddcf40:64,0xddcf80:64,0xddcfc0:64,0xddd000:1024,0xddd400:256,0xddd500:256,0xddd600:256,0xddd700:256,0xddd800:1024,0xdddc00:1024,0xdde000:1024,0xdde400:1024,0xdde800:1024,0xddec00:256,0xdded00:256,0xddee00:256,0xddef00:64,0xddef40:64,0xddef80:64,0xddefc0:64},{0xde1000:256,0xde1100:256,0xde1200:256,0xde1300:256,0xde1400:256,0xde1500:256,0xde1600:256,0xde1700:256,0xde1800:256,0xde1900:256,0xde1a00:256,0xde1b00:256,0xde1c00:1024,0xde2000:1024,0xde2400:1024,0xde2800:1024,0xde2c00:1024,0xde3000:1024,0xde3400:1024,0xde3800:1024,0xde3c00:1024,0xde4000:1024,0xde4400:1024,0xde4800:256,0xde4900:256,0xde4a00:256,0xde4b00:256,0xde4c00:1024,0xde5000:256,0xde5100:256,0xde5200:256,0xde5300:64,0xde5340:64,0xde5380:64,0xde53c0:64,0xde5400:256,0xde5500:64,0xde5540:64,0xde5580:64,0xde55c0:64,0xde5600:256,0xde5700:256,0xde5800:256,0xde5900:256,0xde5a00:256,0xde5b00:256,0xde5c00:1024,0xde7d00:256,0xde7e80:64,0xde7ec0:64,0xde8000:1024,0xde8400:1024,0xde8800:1024,0xde8c00:1024,0xdea000:256,0xdea100:256,0xdea200:256,0xdea300:16,0xdea310:16,0xdea320:16,0xdea330:16,0xdea340:64,0xdea380:64,0xdea3c0:64,0xdea800:256,0xdea900:256,0xdeaa00:256,0xdeab00:256,0xdeac00:64,0xdeac40:64,0xdeac80:64,0xdeacc0:64,0xdead00:256,0xdeae00:256,0xdeaf00:256,0xdeb000:1024,0xdeb400:1024,0xdeb800:1024,0xdebc00:1024,0xdec000:1024,0xdec400:256,0xdec500:256,0xdec600:256,0xdec700:256,0xdec800:1024,0xdecc00:256,0xdecd00:256,0xdece00:256,0xdecf00:256,0xded000:1024,0xded400:1024,0xded800:256,0xded900:256,0xdeda00:256,0xdedb00:256,0xdedc00:256,0xdedd00:256,0xdede00:256,0xdedf00:256,0xdef000:1024,0xdef400:1024,0xdef800:256,0xdef900:64,0xdef940:64,0xdef980:16,0xdef990:16,0xdef9a0:16,0xdef9b0:16,0xdef9c0:64},{0xdf0000:256,0xdf0100:256,0xdf0200:256,0xdf0300:256,0xdf0400:1024,0xdf0800:1024,0xdf0c00:1024,0xdf1400:256,0xdf1500:256,0xdf1bb8:4,0xdf4000:1024,0xdf4400:1024,0xdf4800:1024,0xdf4c00:1024,0xdf5000:1024,0xdf5400:1024,0xdf5800:1024,0xdf5c00:1024,0xdf6000:1024,0xdf6400:1024,0xdf6800:1024,0xdf6c00:1024,0xdf7000:1024,0xdf7400:256,0xdf7500:256,0xdf7800:1024,0xdf7c00:1024,0xdf8000:256,0xdf8100:256,0xdf9000:1024,0xdf9400:1024,0xdf9800:1024,0xdf9c00:1024,0xdfa000:1024,0xdfa600:256,0xdfa700:256,0xdfc000:256,0xdfc100:256,0xdfc600:256,0xdfc700:256,0xdfc900:256,0xdfca00:256,0xdfcb00:256,0xdfd000:1024,0xdfd400:256,0xdfd500:256,0xdfd600:256,0xdfd700:256,0xdfdc00:256,0xdfdd00:256,0xdfdfb0:16,0xdfdfc0:16,0xdff000:1024,0xdff400:1024,0xdff800:1024,0xdffc80:64,0xdffcc0:64,0xdffe00:256,0xdfff00:64,0xdfff40:64,0xdfffec:4,0xdffffc:2} +]; +var cnIp16Range = { +0x400:1,0x404:1,0x408:1,0x410:1,0x428:1,0x3800:1,0x3804:1,0x399a:1,0x3b00:1,0x3b01:1,0x6c8b:1,0x6cc8:1,0x6cd9:1,0x6cda:1,0x6d8b:1,0x6dab:1,0x6db4:1,0x6dc1:1,0x6dd0:1,0x6de5:1,0x6e0f:1,0x9000:1,0x9094:1,0x9c00:1,0xa800:1,0xa804:1,0xa8fa:1,0xa94d:1,0xa981:1,0xa98d:1,0xa9ec:1,0xaa70:1,0xaaed:1,0xab08:1,0xafb0:1,0xafb1:1,0xafb2:1,0xafb3:1,0xafb4:1,0xafb5:1,0xafb6:1,0xafb7:1,0xafb8:1,0xafb9:1,0xafba:1,0xafbb:1,0xafbc:1,0xafc0:1,0xafc1:1,0xafc2:1,0xafc3:1,0xafc4:1,0xafc5:1,0xafc6:1,0xafc7:1,0xafc8:1,0xafc9:1,0xafca:1,0xafcb:1,0xafcc:1,0xafcd:1,0xafce:1,0xafcf:1,0xafd8:1,0xafd9:1,0xafdc:1,0xafdd:1,0xafde:1,0xafdf:1,0xafe0:1,0xafe1:1,0xafe2:1,0xafe3:1,0xafe4:1,0xafe5:1,0xafe6:1,0xafe7:1,0xaff0:1,0xaff3:1,0xaff8:1,0xaff9:1,0xaffa:1,0xaffb:1,0xaffc:1,0xaffd:1,0xaffe:1,0xafff:1,0xb501:1,0xc600:1,0xc7db:1,0xe907:1,0xeeff:1,0xf411:1,0xf412:1,0xf422:1,0xf470:1,0xf477:1,0xf4b7:1,0xf619:1,0x19400:1,0x19404:1,0x1940a:1,0x194c8:1,0x194d5:1,0x194df:1,0x19538:1,0x19580:1,0x1958d:1,0x19595:1,0x19599:1,0x195b9:1,0x19600:1,0x1972e:1,0x197a9:1,0x197ec:1,0x19c04:1,0x19c05:1,0x19c06:1,0x19c09:1,0x19c0a:1,0x19c0b:1,0x19c0d:1,0x19c0e:1,0x19c10:1,0x19c12:1,0x19c14:1,0x19c17:1,0x19c19:1,0x19c1b:1,0x19c1c:1,0x19c1f:1,0x19c20:1,0x19c21:1,0x19c22:1,0x19c23:1,0x19c26:1,0x19c27:1,0x19c28:1,0x19c29:1,0x19c2a:1,0x19c2e:1,0x19c30:1,0x19c31:1,0x19c32:1,0x19c33:1,0x19c35:1,0x19c36:1,0x19c37:1,0x19c39:1,0x19c3a:1,0x19c3b:1,0x19c3c:1,0x19c3d:1,0x19c3f:1,0x19c40:1,0x19c41:1,0x19c44:1,0x19c45:1,0x19c46:1,0x19c47:1,0x19c4b:1,0x19c4c:1,0x19c4d:1,0x19c4f:1,0x19c50:1,0x19c51:1,0x19c52:1,0x19c53:1,0x19c55:1,0x19c56:1,0x19c57:1,0x19c58:1,0x19c59:1,0x19c5a:1,0x19c5b:1,0x19c5c:1,0x19c5e:1,0x19c5f:1,0x19c61:1,0x19c62:1,0x19c63:1,0x19c64:1,0x19c65:1,0x19c66:1,0x19c67:1,0x19c68:1,0x19c69:1,0x19c6a:1,0x19c6b:1,0x19c6c:1,0x19c6d:1,0x19c6f:1,0x19c70:1,0x19c73:1,0x19c74:1,0x19c76:1,0x19c78:1,0x19c79:1,0x19c7a:1,0x19c7b:1,0x19c7c:1,0x19c7d:1,0x19c7e:1,0x19c7f:1,0x19c80:1,0x19c81:1,0x19c82:1,0x19c83:1,0x19c84:1,0x19c85:1,0x19c86:1,0x19c87:1,0x19c88:1,0x19c89:1,0x19c8a:1,0x19c8b:1,0x19c8c:1,0x19c90:1,0x19c91:1,0x19c92:1,0x19c93:1,0x19c94:1,0x19c95:1,0x19c96:1,0x19c97:1,0x19c98:1,0x19c99:1,0x19c9a:1,0x19c9b:1,0x19c9c:1,0x19c9d:1,0x19c9e:1,0x19c9f:1,0x19ca0:1,0x19ca1:1,0x19ca3:1,0x19ca4:1,0x19ca5:1,0x19f80:1,0x19f81:1,0x19f83:1,0x19f85:1,0x19f88:1,0x19f89:1,0x19f8a:1,0x19f8b:1,0x19f8c:1,0x19f8d:1,0x19f8e:1,0x19f8f:1,0x19f90:1,0x19f91:1,0x19f92:1,0x19f93:1,0x19f94:1,0x19f96:1,0x19f97:1,0x19f98:1,0x19f99:1,0x19f9b:1,0x19f9c:1,0x19f9d:1,0x19f9e:1,0x19f9f:1,0x19fa0:1,0x19fa2:1,0x19fa3:1,0x19fa4:1,0x19fa5:1,0x19fa6:1,0x19fa7:1,0x19fa8:1,0x19fa9:1,0x19faa:1,0x19fac:1,0x19fad:1,0x19fae:1,0x19faf:1,0x19fb0:1,0x19fb1:1,0x19fb4:1,0x19fb5:1,0x19fb6:1,0x19fb7:1,0x19fb8:1,0x19fb9:1,0x19fba:1,0x19fbb:1,0x19fbc:1,0x19fbd:1,0x19fbe:1,0x19fbf:1,0x19fc0:1,0x19fc1:1,0x19fc2:1,0x19fc3:1,0x19fc4:1,0x19fc5:1,0x19fc6:1,0x19fc7:1,0x19fc8:1,0x19fc9:1,0x19fca:1,0x19fcb:1,0x19fcc:1,0x19fce:1,0x19fcf:1,0x19fd0:1,0x19fd1:1,0x19fd2:1,0x19fd3:1,0x19fd4:1,0x19fd5:1,0x19fd6:1,0x19fd8:1,0x19fd9:1,0x19fda:1,0x19fde:1,0x19fdf:1,0x19fe0:1,0x19fe1:1,0x19fe2:1,0x19fe3:1,0x19fe4:1,0x19fe6:1,0x19fe7:1,0x19fe8:1,0x19fe9:1,0x19fea:1,0x19feb:1,0x19fec:1,0x19fed:1,0x19fee:1,0x19fef:1,0x19ff0:1,0x19ff1:1,0x19ff2:1,0x19ff3:1,0x19ff4:1,0x19ff7:1,0x19ff8:1,0x19ff9:1,0x19ffa:1,0x19ffb:1,0x19ffd:1,0x19ffe:1,0x19fff:1,0x1a800:1,0x1b8b2:1,0x1b92e:1,0x1b930:1,0x1b932:1,0x1b974:1,0x1ba94:1,0x1bab4:1,0x1bab5:1,0x1bab7:1,0x1bba0:1,0x1bd0f:1,0x1bd11:1,0x1bd6f:1,0x1bddb:1,0x1bdde:1,0x1bf7f:1,0x1bfad:1,0x1bfae:1,0x1c224:1,0x1c42f:1,0x1c4d2:1,0x1c4ef:1,0x1c609:1,0x1c715:1,0x1c741:1,0x1c751:1,0x1c752:1,0x1c9b8:1,0x1c9bc:1,0x1c9be:1,0x1c9df:1,0x1ca35:1,0x1cb1b:1,0x1cd15:1,0x1cd53:1,0x1cdf0:1,0x1ce99:1,0x1d000:1,0x1d0c8:1,0x1d0ea:1,0x1d0eb:1,0x1d166:1,0x1d169:1,0x1d16a:1,0x1d304:1,0x1d306:1,0x1d31e:1,0x1d352:1,0x1d358:1,0x1d359:1,0x1d4d4:1,0x1d4d6:1,0x1d529:1,0x1d59c:1,0x1d59d:1,0x1d59e:1,0x1d5a2:1,0x1d5e7:1,0x1d90d:1,0x1d960:1,0x1d96f:1,0x1d998:1,0x1d9fe:1,0x1dc08:1,0x1dc3e:1,0x1dc4b:1,0x1dc6e:1,0x1dc78:1,0x1dc7f:1,0x1dc9b:1,0x1dca1:1,0x1dca8:1,0x1dcaa:1,0x1dcab:1,0x1dcfc:1,0x1dd2f:1,0x1dd4b:1,0x1de52:1,0x1de83:1,0x1dff1:1,0x1dff3:1,0x1e120:1,0x1e160:1,0x1e23e:1,0x1e400:1,0x1e4c8:1,0x1e4d2:1,0x1e4d3:1,0x1e4da:1,0x1e4ea:1,0x1e597:1,0x1e723:1,0x1e998:1,0x1e999:1,0x1ea01:1,0x1eb24:1,0x1ebe0:1,0x1ebfd:1,0x1ecca:1,0x1ed90:1,0x1edb2:1,0x1edb3:1,0x1ee21:1,0x1eec0:1,0x1eec1:1,0x1f054:1,0x1f0a1:1,0x1f0a3:1,0x1f1b0:1,0x1f1b5:1,0x1f759:1,0x25a06:1,0x25a07:1,0x25bc8:1,0x25bc9:1,0x25bca:1,0x25bcb:1,0x28cbc:1,0x28cd4:1,0x28cd5:1,0x28cd6:1,0x28cd7:1,0x2d178:1,0x2d179:1,0x2d250:1,0x2d252:1,0x2d253:1,0x2d256:1,0x2d25a:1,0x2d2f6:1,0x2d323:1,0x2d34b:1,0x2d37b:1,0x2d3a5:1,0x2d3ad:1,0x2d843:1,0x2d85e:1,0x2d85f:1,0x2d8c1:1,0x2d8c8:1,0x2d8c9:1,0x2da81:1,0x2dbbc:1,0x2dd3a:1,0x2dd46:1,0x2dd6e:1,0x2ded8:1,0x301f2:1,0x302f2:1,0x32801:1,0x32802:1,0x3280e:1,0x32812:1,0x32813:1,0x32818:1,0x32819:1,0x3281a:1,0x32820:1,0x32821:1,0x32822:1,0x32823:1,0x32824:1,0x32829:1,0x32830:1,0x32831:1,0x32839:1,0x3283a:1,0x3283b:1,0x32851:1,0x32856:1,0x3285b:1,0x3286e:1,0x32898:1,0x32899:1,0x3289a:1,0x328a0:1,0x328a2:1,0x328a4:1,0x328a6:1,0x328a7:1,0x328ad:1,0x328ae:1,0x328b0:1,0x328b1:1,0x328b2:1,0x328b4:1,0x328b8:1,0x328ba:1,0x328bb:1,0x328bd:1,0x328be:1,0x328e7:1,0x328e8:1,0x328ec:1,0x328ef:1,0x328f0:1,0x328f1:1,0x328f2:1,0x328f9:1,0x328fb:1,0x328fd:1,0x328fe:1,0x328ff:1,0x32904:1,0x3290c:1,0x32914:1,0x32918:1,0x32919:1,0x3291b:1,0x32920:1,0x32921:1,0x32926:1,0x32928:1,0x32929:1,0x3292b:1,0x3292f:1,0x32933:1,0x32935:1,0x32938:1,0x3293f:1,0x32943:1,0x32944:1,0x3294f:1,0x32950:1,0x32957:1,0x3295b:1,0x3295d:1,0x32964:1,0x32968:1,0x32969:1,0x3296b:1,0x3296c:1,0x3296d:1,0x3296e:1,0x3296f:1,0x32970:1,0x32973:1,0x32974:1,0x32977:1,0x32979:1,0x3297c:1,0x3297f:1,0x32981:1,0x32982:1,0x32983:1,0x32984:1,0x32985:1,0x32987:1,0x32988:1,0x32989:1,0x3298a:1,0x3298b:1,0x3298d:1,0x3298e:1,0x3298f:1,0x32990:1,0x32991:1,0x32992:1,0x32993:1,0x32995:1,0x32997:1,0x32998:1,0x3299a:1,0x3299b:1,0x3299c:1,0x3299d:1,0x3299f:1,0x329be:1,0x329c4:1,0x329c7:1,0x329c8:1,0x329cc:1,0x329d0:1,0x329d1:1,0x329d8:1,0x329dc:1,0x329dd:1,0x329e8:1,0x329e9:1,0x329ea:1,0x329ed:1,0x329f0:1,0x329f5:1,0x329f6:1,0x329fc:1,0x329fd:1,0x329fe:1,0x329ff:1,0x32a08:1,0x32a0b:1,0x32a0c:1,0x32a0f:1,0x32a14:1,0x32a18:1,0x32a1a:1,0x32a20:1,0x32a23:1,0x32a27:1,0x32a36:1,0x32a38:1,0x32a3c:1,0x32a4a:1,0x32a4b:1,0x32a4e:1,0x32a50:1,0x32a51:1,0x32a54:1,0x32a56:1,0x32a57:1,0x32a58:1,0x32a5b:1,0x32a5c:1,0x32a5e:1,0x32a62:1,0x32a64:1,0x32a77:1,0x32a7a:1,0x32a82:1,0x32a89:1,0x32a90:1,0x32a91:1,0x32a95:1,0x32a96:1,0x32a97:1,0x32a9b:1,0x32aa2:1,0x32aaa:1,0x32aab:1,0x32aaf:1,0x32ab0:1,0x32ab4:1,0x32ab7:1,0x32ab9:1,0x32ac3:1,0x32acf:1,0x32ad2:1,0x32ad3:1,0x32ad5:1,0x32ad8:1,0x32adb:1,0x32af5:1,0x32af6:1,0x32afc:1,0x32afd:1,0x32c00:1,0x32c01:1,0x32c02:1,0x32c03:1,0x32c04:1,0x32c05:1,0x32c07:1,0x32c09:1,0x32c0a:1,0x32c0b:1,0x32c0d:1,0x32c0e:1,0x32c12:1,0x32c13:1,0x32c14:1,0x32c15:1,0x32c16:1,0x32c17:1,0x32c1a:1,0x32c1b:1,0x32c1e:1,0x32c1f:1,0x32c20:1,0x32c21:1,0x32c22:1,0x32c23:1,0x32c24:1,0x32c25:1,0x32c26:1,0x32c28:1,0x32c29:1,0x32c2d:1,0x32c2e:1,0x32c2f:1,0x32c30:1,0x32c31:1,0x32c32:1,0x32c33:1,0x32c34:1,0x32c35:1,0x32c36:1,0x32c37:1,0x32c38:1,0x32c39:1,0x32c3a:1,0x32c3b:1,0x32c3c:1,0x32c3d:1,0x32c3e:1,0x32c3f:1,0x32c40:1,0x32c42:1,0x32c43:1,0x32c44:1,0x32c45:1,0x32c46:1,0x32c47:1,0x32c48:1,0x32c49:1,0x32c4a:1,0x32c4b:1,0x32c4c:1,0x32c4d:1,0x32c4e:1,0x32c4f:1,0x32c50:1,0x32c51:1,0x32c52:1,0x32c53:1,0x32c54:1,0x32c55:1,0x32c56:1,0x32c57:1,0x32c58:1,0x32c59:1,0x32c5a:1,0x32c5b:1,0x32c5c:1,0x32c5d:1,0x32c5e:1,0x32c5f:1,0x32c60:1,0x32c61:1,0x32c62:1,0x32c63:1,0x32c64:1,0x32c65:1,0x32c66:1,0x32c67:1,0x32c68:1,0x32c69:1,0x32c6a:1,0x32c6b:1,0x32c6c:1,0x32c6d:1,0x32c6f:1,0x32c70:1,0x32c71:1,0x32c72:1,0x32c73:1,0x32c74:1,0x32c75:1,0x32c76:1,0x32c77:1,0x32c78:1,0x32c79:1,0x32c7a:1,0x32c7b:1,0x32c7c:1,0x32c7d:1,0x32c7e:1,0x32c7f:1,0x32c80:1,0x32c81:1,0x32c82:1,0x32c83:1,0x32c84:1,0x32c85:1,0x32c86:1,0x32c87:1,0x32c88:1,0x32c89:1,0x32c8a:1,0x32c8b:1,0x32cdc:1,0x32cdd:1,0x32cde:1,0x32cdf:1,0x32ce0:1,0x32ce1:1,0x32ce2:1,0x32ce3:1,0x32ce4:1,0x32ce5:1,0x32ce6:1,0x32ce7:1,0x32cf8:1,0x32cfa:1,0x32cfb:1,0x32d32:1,0x32d36:1,0x32d38:1,0x32d3c:1,0x32d40:1,0x32d42:1,0x32d44:1,0x32d48:1,0x32d4c:1,0x32d4f:1,0x32d58:1,0x32d59:1,0x32d5b:1,0x32d60:1,0x32d63:1,0x32d64:1,0x32d66:1,0x32d68:1,0x32d6a:1,0x32d6b:1,0x32d6c:1,0x32d6d:1,0x32d70:1,0x32d72:1,0x32d74:1,0x32d76:1,0x32d78:1,0x32d7c:1,0x32d7d:1,0x32d7f:1,0x32d8c:1,0x32d8d:1,0x32d90:1,0x32d91:1,0x32d93:1,0x32da0:1,0x32da5:1,0x32da6:1,0x32dba:1,0x32dbb:1,0x32dcb:1,0x32ddb:1,0x32ddc:1,0x32ddd:1,0x32e00:1,0x32e01:1,0x32e03:1,0x32e04:1,0x32e08:1,0x32e10:1,0x32e1b:1,0x32e1d:1,0x32e1e:1,0x32e3b:1,0x32e41:1,0x32e44:1,0x32e51:1,0x32e55:1,0x32e61:1,0x32e62:1,0x32e64:1,0x32e78:1,0x32e81:1,0x32e82:1,0x32e83:1,0x32e84:1,0x32e86:1,0x32e87:1,0x32e9a:1,0x32ea0:1,0x32ea8:1,0x32eac:1,0x32eaf:1,0x32eb8:1,0x32eb9:1,0x32ebe:1,0x32ec1:1,0x32ec2:1,0x32ee1:1,0x32eee:1,0x32ef4:1,0x32ef5:1,0x32ef7:1,0x32ef9:1,0x32efb:1,0x32efc:1,0x32efe:1,0x32f00:1,0x32f07:1,0x32f09:1,0x32f0d:1,0x32f10:1,0x32f2b:1,0x32f35:1,0x32f40:1,0x32f47:1,0x32f50:1,0x32f51:1,0x32f5f:1,0x32f7b:1,0x32f7c:1,0x34808:1,0x34814:1,0x34816:1,0x34839:1,0x3483a:1,0x3483b:1,0x3483c:1,0x3483d:1,0x3485c:1,0x348c2:1,0x348e3:1,0x34922:1,0x34924:1,0x34928:1,0x34929:1,0x3492a:1,0x34930:1,0x34938:1,0x3493a:1,0x3493f:1,0x3495e:1,0x34b01:1,0x34d8d:1,0x34e6d:1,0x3698b:1,0x36991:1,0x369a2:1,0x369a3:1,0x36ae7:1,0x36e58:1,0x36e59:1,0x36e5c:1,0x373de:1,0x3741c:1,0x3741d:1,0x3742f:1,0x37435:1,0x37617:1,0x3771c:1,0x3771f:1,0x37a8c:1,0x37be6:1,0x37c6e:1,0x37f7e:1,0x37f7f:1,0x37fff:1 +}; + +var fakeIpRange = [ +0x4a7d7f66,0x4a7d9b66,0x4a7d2766,0x4a7d2771,0xd155e58a,0x80797e8b,0x9f6a794b,0xa9840d67,0xc043c606,0xca6a0102,0xcab50755,0xcba1e6ab,0xcb620741,0xcf0c5862,0xd0381f2b,0xd1913632,0xd1dc1eae,0xd1244921,0xd35e4293,0xd5a9fb23,0xd8ddbcb6,0xd8eab30d,0xf3b9bb27,0x253d369e,0x42442b2,0x2e52ae44,0x3b1803ad,0x402158a1,0x4021632f,0x4042a3fb,0x4168cafc,0x41a0db71,0x422dfced,0x480ecd68,0x480ecd63,0x4e10310f,0x807c62d,0x5d2e0859 +]; + +var subnetIpRangeList = [ +0,1, +167772160,184549376, //10.0.0.0/8 +2886729728,2887778304, //172.16.0.0/12 +3232235520,3232301056, //192.168.0.0/16 +2130706432,2130706688 //127.0.0.0/24 +]; + +var hasOwnProperty = Object.hasOwnProperty; + +function check_ipv4(host) { + var re_ipv4 = /^\d+\.\d+\.\d+\.\d+$/g; + if (re_ipv4.test(host)) { + return true; + } +} +function convertAddress(ipchars) { + var bytes = ipchars.split('.'); + var result = (bytes[0] << 24) | + (bytes[1] << 16) | + (bytes[2] << 8) | + (bytes[3]); + return result >>> 0; +} +function isInSingleRange(ipRange, intIp) { + if ( hasOwnProperty.call(cnIp16Range, intIp >>> 6) ) { + for ( var range = 1; range < 64; range*=4 ) { + var master = intIp & ~(range-1); + if ( hasOwnProperty.call(ipRange, master) ) + return intIp - master < ipRange[master]; + } + } else { + for ( var range = 64; range <= 1024; range*=4 ) { + var master = intIp & ~(range-1); + if ( hasOwnProperty.call(ipRange, master) ) + return intIp - master < ipRange[master]; + } + } +} +function isInSubnetRange(ipRange, intIp) { + for ( var i = 0; i < 10; i += 2 ) { + if ( ipRange[i] <= intIp && intIp < ipRange[i+1] ) + return true; + } +} +function getProxyFromDirectIP(strIp) { + var intIp = convertAddress(strIp); + if ( isInSubnetRange(subnetIpRangeList, intIp) ) { + return direct; + } + return ip_proxy; +} +function getProxyFromIP(strIp) { + var intIp = convertAddress(strIp); + { + var len = fakeIpRange.length; + for ( var i = 0; i < len; ++i ) { + if ( intIp == fakeIpRange[i] ) + return wall_proxy; + } + } + if ( isInSubnetRange(subnetIpRangeList, intIp) ) { + return direct; + } + var index = (intIp >>> 24) & 0xff; + if ( isInSingleRange(cnIpRange[index], intIp) ) { + return nowall_proxy; + } + return auto_proxy; +} +function isInDomains(domain_dict, host) { + var suffix; + var pos1 = host.lastIndexOf('.'); + + suffix = host.substring(pos1 + 1); + if (suffix == "cn") { + return true; + } + + var domains = domain_dict[suffix]; + if ( domains === undefined ) { + return false; + } + host = host.substring(0, pos1); + var pos = host.lastIndexOf('.'); + + while(1) { + if (pos <= 0) { + if (hasOwnProperty.call(domains, host)) { + return true; + } else { + return false; + } + } + suffix = host.substring(pos + 1); + if (hasOwnProperty.call(domains, suffix)) { + return true; + } + pos = host.lastIndexOf('.', pos - 1); + } +} + +/* +* This file is part of Adblock Plus , +* Copyright (C) 2006-2014 Eyeo GmbH +* +* Adblock Plus is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 3 as +* published by the Free Software Foundation. +* +* Adblock Plus is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Adblock Plus. If not, see . +*/ + +function createDict() +{ + var result = {}; + result.__proto__ = null; + return result; +} + +function getOwnPropertyDescriptor(obj, key) +{ + if (obj.hasOwnProperty(key)) + { + return obj[key]; + } + return null; +} + +function extend(subclass, superclass, definition) +{ + if (Object.__proto__) + { + definition.__proto__ = superclass.prototype; + subclass.prototype = definition; + } + else + { + var tmpclass = function(){}, ret; + tmpclass.prototype = superclass.prototype; + subclass.prototype = new tmpclass(); + subclass.prototype.constructor = superclass; + for (var i in definition) + { + if (definition.hasOwnProperty(i)) + { + subclass.prototype[i] = definition[i]; + } + } + } +} + +function Filter(text) +{ + this.text = text; + this.subscriptions = []; +} +Filter.prototype = { + text: null, + subscriptions: null, + toString: function() + { + return this.text; + } +}; +Filter.knownFilters = createDict(); +Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/; +Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/; +Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/; +Filter.fromText = function(text) +{ + if (text in Filter.knownFilters) + { + return Filter.knownFilters[text]; + } + var ret; + if (text[0] == "!") + { + ret = new CommentFilter(text); + } + else + { + ret = RegExpFilter.fromText(text); + } + Filter.knownFilters[ret.text] = ret; + return ret; +}; + +function InvalidFilter(text, reason) +{ + Filter.call(this, text); + this.reason = reason; +} +extend(InvalidFilter, Filter, { + reason: null +}); + +function CommentFilter(text) +{ + Filter.call(this, text); +} +extend(CommentFilter, Filter, { +}); + +function ActiveFilter(text, domains) +{ + Filter.call(this, text); + this.domainSource = domains; +} +extend(ActiveFilter, Filter, { + domainSource: null, + domainSeparator: null, + ignoreTrailingDot: true, + domainSourceIsUpperCase: false, + getDomains: function() + { + var prop = getOwnPropertyDescriptor(this, "domains"); + if (prop) + { + return prop; + } + var domains = null; + if (this.domainSource) + { + var source = this.domainSource; + if (!this.domainSourceIsUpperCase) + { + source = source.toUpperCase(); + } + var list = source.split(this.domainSeparator); + if (list.length == 1 && list[0][0] != "~") + { + domains = createDict(); + domains[""] = false; + if (this.ignoreTrailingDot) + { + list[0] = list[0].replace(/\.+$/, ""); + } + domains[list[0]] = true; + } + else + { + var hasIncludes = false; + for (var i = 0; i < list.length; i++) + { + var domain = list[i]; + if (this.ignoreTrailingDot) + { + domain = domain.replace(/\.+$/, ""); + } + if (domain == "") + { + continue; + } + var include; + if (domain[0] == "~") + { + include = false; + domain = domain.substr(1); + } + else + { + include = true; + hasIncludes = true; + } + if (!domains) + { + domains = createDict(); + } + domains[domain] = include; + } + domains[""] = !hasIncludes; + } + this.domainSource = null; + } + return this.domains; + }, + sitekeys: null, + isActiveOnDomain: function(docDomain, sitekey) + { + if (this.getSitekeys() && (!sitekey || this.getSitekeys().indexOf(sitekey.toUpperCase()) < 0)) + { + return false; + } + if (!this.getDomains()) + { + return true; + } + if (!docDomain) + { + return this.getDomains()[""]; + } + if (this.ignoreTrailingDot) + { + docDomain = docDomain.replace(/\.+$/, ""); + } + docDomain = docDomain.toUpperCase(); + while (true) + { + if (docDomain in this.getDomains()) + { + return this.domains[docDomain]; + } + var nextDot = docDomain.indexOf("."); + if (nextDot < 0) + { + break; + } + docDomain = docDomain.substr(nextDot + 1); + } + return this.domains[""]; + }, + isActiveOnlyOnDomain: function(docDomain) + { + if (!docDomain || !this.getDomains() || this.getDomains()[""]) + { + return false; + } + if (this.ignoreTrailingDot) + { + docDomain = docDomain.replace(/\.+$/, ""); + } + docDomain = docDomain.toUpperCase(); + for (var domain in this.getDomains()) + { + if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) + { + return false; + } + } + return true; + } +}); + +function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) +{ + ActiveFilter.call(this, text, domains, sitekeys); + if (contentType != null) + { + this.contentType = contentType; + } + if (matchCase) + { + this.matchCase = matchCase; + } + if (thirdParty != null) + { + this.thirdParty = thirdParty; + } + if (sitekeys != null) + { + this.sitekeySource = sitekeys; + } + if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpSource.length - 1] == "/") + { + var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i"); + this.regexp = regexp; + } + else + { + this.regexpSource = regexpSource; + } +} +extend(RegExpFilter, ActiveFilter, { + domainSourceIsUpperCase: true, + length: 1, + domainSeparator: "|", + regexpSource: null, + getRegexp: function() + { + var prop = getOwnPropertyDescriptor(this, "regexp"); + if (prop) + { + return prop; + } + var source = this.regexpSource.replace(/\*+/g, "*").replace(/\^\|$/, "^").replace(/\W/g, "\\$&").replace(/\\\*/g, ".*").replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)").replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?").replace(/^\\\|/, "^").replace(/\\\|$/, "$").replace(/^(\.\*)/, "").replace(/(\.\*)$/, ""); + var regexp = new RegExp(source, this.matchCase ? "" : "i"); + this.regexp = regexp; + return regexp; + }, + contentType: 2147483647, + matchCase: false, + thirdParty: null, + sitekeySource: null, + getSitekeys: function() + { + var prop = getOwnPropertyDescriptor(this, "sitekeys"); + if (prop) + { + return prop; + } + var sitekeys = null; + if (this.sitekeySource) + { + sitekeys = this.sitekeySource.split("|"); + this.sitekeySource = null; + } + this.sitekeys = sitekeys; + return this.sitekeys; + }, + matches: function(location, contentType, docDomain, thirdParty, sitekey) + { + if (this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, sitekey)) + { + return true; + } + return false; + } +}); +RegExpFilter.prototype["0"] = "#this"; +RegExpFilter.fromText = function(text) +{ + var blocking = true; + var origText = text; + if (text.indexOf("@@") == 0) + { + blocking = false; + text = text.substr(2); + } + var contentType = null; + var matchCase = null; + var domains = null; + var sitekeys = null; + var thirdParty = null; + var collapse = null; + var options; + var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null; + if (match) + { + options = match[1].toUpperCase().split(","); + text = match.input.substr(0, match.index); + for (var _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6) + { + var option = options[_loopIndex6]; + var value = null; + var separatorIndex = option.indexOf("="); + if (separatorIndex >= 0) + { + value = option.substr(separatorIndex + 1); + option = option.substr(0, separatorIndex); + } + option = option.replace(/-/, "_"); + if (option in RegExpFilter.typeMap) + { + if (contentType == null) + { + contentType = 0; + } + contentType |= RegExpFilter.typeMap[option]; + } + else if (option[0] == "~" && option.substr(1) in RegExpFilter.typeMap) + { + if (contentType == null) + { + contentType = RegExpFilter.prototype.contentType; + } + contentType &= ~RegExpFilter.typeMap[option.substr(1)]; + } + else if (option == "MATCH_CASE") + { + matchCase = true; + } + else if (option == "~MATCH_CASE") + { + matchCase = false; + } + else if (option == "DOMAIN" && typeof value != "undefined") + { + domains = value; + } + else if (option == "THIRD_PARTY") + { + thirdParty = true; + } + else if (option == "~THIRD_PARTY") + { + thirdParty = false; + } + else if (option == "COLLAPSE") + { + collapse = true; + } + else if (option == "~COLLAPSE") + { + collapse = false; + } + else if (option == "SITEKEY" && typeof value != "undefined") + { + sitekeys = value; + } + else + { + return new InvalidFilter(origText, "Unknown option " + option.toLowerCase()); + } + } + } + if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) + { + if (contentType == null) + { + contentType = RegExpFilter.prototype.contentType; + } + contentType &= ~RegExpFilter.typeMap.DOCUMENT; + } + try + { + if (blocking) + { + return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse); + } + else + { + return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys); + } + } + catch (e) + { + return new InvalidFilter(origText, e); + } +}; +RegExpFilter.typeMap = { + OTHER: 1, + SCRIPT: 2, + IMAGE: 4, + STYLESHEET: 8, + OBJECT: 16, + SUBDOCUMENT: 32, + DOCUMENT: 64, + XBL: 1, + PING: 1, + XMLHTTPREQUEST: 2048, + OBJECT_SUBREQUEST: 4096, + DTD: 1, + MEDIA: 16384, + FONT: 32768, + BACKGROUND: 4, + POPUP: 268435456, + ELEMHIDE: 1073741824 +}; +RegExpFilter.prototype.contentType &= ~ (RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP); + +function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse) +{ + RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); + this.collapse = collapse; +} +extend(BlockingFilter, RegExpFilter, { + collapse: null +}); + +function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) +{ + RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys); +} +extend(WhitelistFilter, RegExpFilter, { +}); + +function Matcher() +{ + this.clear(); +} +Matcher.prototype = { + filterByKeyword: null, + keywordByFilter: null, + clear: function() + { + this.filterByKeyword = createDict(); + this.keywordByFilter = createDict(); + }, + add: function(filter) + { + if (filter.text in this.keywordByFilter) + { + return; + } + var keyword = this.findKeyword(filter); + var oldEntry = this.filterByKeyword[keyword]; + if (typeof oldEntry == "undefined") + { + this.filterByKeyword[keyword] = filter; + } + else if (oldEntry.length == 1) + { + this.filterByKeyword[keyword] = [oldEntry, filter]; + } + else + { + oldEntry.push(filter); + } + this.keywordByFilter[filter.text] = keyword; + }, + remove: function(filter) + { + if (!(filter.text in this.keywordByFilter)) + { + return; + } + var keyword = this.keywordByFilter[filter.text]; + var list = this.filterByKeyword[keyword]; + if (list.length <= 1) + { + delete this.filterByKeyword[keyword]; + } + else + { + var index = list.indexOf(filter); + if (index >= 0) + { + list.splice(index, 1); + if (list.length == 1) + { + this.filterByKeyword[keyword] = list[0]; + } + } + } + delete this.keywordByFilter[filter.text]; + }, + findKeyword: function(filter) + { + var result = ""; + var text = filter.text; + if (Filter.regexpRegExp.test(text)) + { + return result; + } + var match = Filter.optionsRegExp.exec(text); + if (match) + { + text = match.input.substr(0, match.index); + } + if (text.substr(0, 2) == "@@") + { + text = text.substr(2); + } + var candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g); + if (!candidates) + { + return result; + } + var hash = this.filterByKeyword; + var resultCount = 16777215; + var resultLength = 0; + for (var i = 0, l = candidates.length; i < l; i++) + { + var candidate = candidates[i].substr(1); + var count = candidate in hash ? hash[candidate].length : 0; + if (count < resultCount || count == resultCount && candidate.length > resultLength) + { + result = candidate; + resultCount = count; + resultLength = candidate.length; + } + } + return result; + }, + hasFilter: function(filter) + { + return filter.text in this.keywordByFilter; + }, + getKeywordForFilter: function(filter) + { + if (filter.text in this.keywordByFilter) + { + return this.keywordByFilter[filter.text]; + } + else + { + return null; + } + }, + _checkEntryMatch: function(keyword, location, contentType, docDomain, thirdParty, sitekey) + { + var list = this.filterByKeyword[keyword]; + for (var i = 0; i < list.length; i++) + { + var filter = list[i]; + if (filter == "#this") + { + filter = list; + } + if (filter.matches(location, contentType, docDomain, thirdParty, sitekey)) + { + return filter; + } + } + return null; + }, + matchesAny: function(location, contentType, docDomain, thirdParty, sitekey) + { + var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); + if (candidates === null) + { + candidates = []; + } + candidates.push(""); + for (var i = 0, l = candidates.length; i < l; i++) + { + var substr = candidates[i]; + if (substr in this.filterByKeyword) + { + var result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); + if (result) + { + return result; + } + } + } + return null; + } +}; + +function CombinedMatcher() +{ + this.blacklist = new Matcher(); + this.whitelist = new Matcher(); + this.resultCache = createDict(); +} +CombinedMatcher.maxCacheEntries = 1000; +CombinedMatcher.prototype = { + blacklist: null, + whitelist: null, + resultCache: null, + cacheEntries: 0, + clear: function() + { + this.blacklist.clear(); + this.whitelist.clear(); + this.resultCache = createDict(); + this.cacheEntries = 0; + }, + add: function(filter) + { + if (filter instanceof WhitelistFilter) + { + this.whitelist.add(filter); + } + else + { + this.blacklist.add(filter); + } + if (this.cacheEntries > 0) + { + this.resultCache = createDict(); + this.cacheEntries = 0; + } + }, + remove: function(filter) + { + if (filter instanceof WhitelistFilter) + { + this.whitelist.remove(filter); + } + else + { + this.blacklist.remove(filter); + } + if (this.cacheEntries > 0) + { + this.resultCache = createDict(); + this.cacheEntries = 0; + } + }, + findKeyword: function(filter) + { + if (filter instanceof WhitelistFilter) + { + return this.whitelist.findKeyword(filter); + } + else + { + return this.blacklist.findKeyword(filter); + } + }, + hasFilter: function(filter) + { + if (filter instanceof WhitelistFilter) + { + return this.whitelist.hasFilter(filter); + } + else + { + return this.blacklist.hasFilter(filter); + } + }, + getKeywordForFilter: function(filter) + { + if (filter instanceof WhitelistFilter) + { + return this.whitelist.getKeywordForFilter(filter); + } + else + { + return this.blacklist.getKeywordForFilter(filter); + } + }, + isSlowFilter: function(filter) + { + var matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist; + if (matcher.hasFilter(filter)) + { + return !matcher.getKeywordForFilter(filter); + } + else + { + return !matcher.findKeyword(filter); + } + }, + matchesAnyInternal: function(location, contentType, docDomain, thirdParty, sitekey) + { + var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); + if (candidates === null) + { + candidates = []; + } + candidates.push(""); + var blacklistHit = null; + for (var i = 0, l = candidates.length; i < l; i++) + { + var substr = candidates[i]; + if (substr in this.whitelist.filterByKeyword) + { + var result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); + if (result) + { + return result; + } + } + if (substr in this.blacklist.filterByKeyword && blacklistHit === null) + { + blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey); + } + } + return blacklistHit; + }, + matchesAny: function(location, docDomain) + { + var key = location + " " + docDomain + " "; + if (key in this.resultCache) + { + return this.resultCache[key]; + } + var result = this.matchesAnyInternal(location, 0, docDomain, null, null); + if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) + { + this.resultCache = createDict(); + this.cacheEntries = 0; + } + this.resultCache[key] = result; + this.cacheEntries++; + return result; + } +}; +var defaultMatcher = new CombinedMatcher(); + +function FindProxyForURL(url, host) { + if ( isPlainHostName(host) === true ) { + return direct; + } + if ( check_ipv4(host) === true ) { + return getProxyFromDirectIP(host); + } + if ( isInDomains(white_domains, host) === true ) { + return nowall_proxy; + } + var filter = defaultMatcher.matchesAny(url, host); + if ( filter instanceof WhitelistFilter ) { + return nowall_proxy; + } + if ( filter instanceof BlockingFilter ) { + return wall_proxy; + } + + var strIp = dnsResolve(host); + if (!strIp) { + return wall_proxy; + } + return getProxyFromIP(strIp); +} + diff --git a/packages/pac-resolver/test/isInNet.js b/packages/pac-resolver/test/isInNet.js new file mode 100644 index 00000000..63929972 --- /dev/null +++ b/packages/pac-resolver/test/isInNet.js @@ -0,0 +1,29 @@ +/** + * Module dependencies. + */ + +var assert = require('assert'); +var { isInNet } = require('../').sandbox; + +describe('isInNet(host, pattern, mask)', function () { + var tests = [ + ['198.95.249.79', '198.95.249.79', '255.255.255.255', true], + ['198.95.249.78', '198.95.249.79', '255.255.255.255', false], + ['198.95.1.1', '198.95.0.0', '255.255.0.0', true], + ['198.94.1.1', '198.95.0.0', '255.255.0.0', false], + [null, '198.95.0.0', '255.255.0.0', false], + ]; + + tests.forEach(function (test) { + var expected = test.pop(); + it( + 'should return `' + expected + '` for "' + test.join('", "') + '"', + function (done) { + isInNet(test[0], test[1], test[2]).then((res) => { + assert.equal(expected, res); + done(); + }, done); + } + ); + }); +}); diff --git a/packages/pac-resolver/test/isPlainHostName.js b/packages/pac-resolver/test/isPlainHostName.js new file mode 100644 index 00000000..a6f44673 --- /dev/null +++ b/packages/pac-resolver/test/isPlainHostName.js @@ -0,0 +1,23 @@ +/** + * Module dependencies. + */ + +var assert = require('assert'); +var { isPlainHostName } = require('../').sandbox; + +describe('isPlainHostName(host)', function () { + var tests = [ + ['www', true], + ['www.netscape.com', false], + ]; + + tests.forEach(function (test) { + var expected = test.pop(); + it( + 'should return `' + expected + '` for "' + test.join('", "') + '"', + function () { + assert.equal(expected, isPlainHostName(test[0])); + } + ); + }); +}); diff --git a/packages/pac-resolver/test/isResolvable.js b/packages/pac-resolver/test/isResolvable.js new file mode 100644 index 00000000..ac76fefb --- /dev/null +++ b/packages/pac-resolver/test/isResolvable.js @@ -0,0 +1,26 @@ +/** + * Module dependencies. + */ + +var assert = require('assert'); +const { isResolvable } = require('../').sandbox; + +describe('isResolvable(host)', function () { + var tests = [ + ['www.netscape.com', true], + ['bogus.domain.foobar', false], + ]; + + tests.forEach(function (test) { + var expected = test.pop(); + it( + 'should return `' + expected + '` for "' + test.join('", "') + '"', + function (done) { + isResolvable(test[0]).then((res) => { + assert.equal(expected, res); + done(); + }, done); + } + ); + }); +}); diff --git a/packages/pac-resolver/test/localHostOrDomainIs.js b/packages/pac-resolver/test/localHostOrDomainIs.js new file mode 100644 index 00000000..c99d4d5e --- /dev/null +++ b/packages/pac-resolver/test/localHostOrDomainIs.js @@ -0,0 +1,25 @@ +/** + * Module dependencies. + */ + +var assert = require('assert'); +const { localHostOrDomainIs } = require('../').sandbox; + +describe('localHostOrDomainIs(host, hostdom)', function () { + var tests = [ + ['www.netscape.com', 'www.netscape.com', true], + ['www', 'www.netscape.com', true], + ['www.mcom.com', 'www.netscape.com', false], + ['home.netscape.com', 'www.netscape.com', false], + ]; + + tests.forEach(function (test) { + var expected = test.pop(); + it( + 'should return `' + expected + '` for "' + test.join('", "') + '"', + function () { + assert.equal(expected, localHostOrDomainIs(test[0], test[1])); + } + ); + }); +}); diff --git a/packages/pac-resolver/test/myIpAddress.js b/packages/pac-resolver/test/myIpAddress.js new file mode 100644 index 00000000..ea8bc9a3 --- /dev/null +++ b/packages/pac-resolver/test/myIpAddress.js @@ -0,0 +1,16 @@ +/** + * Module dependencies. + */ + +var isIP = require('net').isIP; +var assert = require('assert'); +var { myIpAddress } = require('../').sandbox; + +describe('myIpAddress()', function () { + it('should return an IPv4 address', function (done) { + myIpAddress().then((ip) => { + assert.equal(4, isIP(ip)); + done(); + }, done); + }); +}); diff --git a/packages/pac-resolver/test/shExpMatch.js b/packages/pac-resolver/test/shExpMatch.js new file mode 100644 index 00000000..9c8318aa --- /dev/null +++ b/packages/pac-resolver/test/shExpMatch.js @@ -0,0 +1,41 @@ +/** + * Module dependencies. + */ + +var assert = require('assert'); +var { shExpMatch } = require('../').sandbox; + +describe('shExpMatch(str, shexp)', function () { + var tests = [ + ['http://home.netscape.com/people/ari/index.html', '*/ari/*', true], + [ + 'http://home.netscape.com/people/montulli/index.html', + '*/ari/*', + false, + ], + [ + 'http://home.example.com/people/yourpage/index.html', + '.*/mypage/.*', + false, + ], + ['www.hotmail.com', '*hotmail.com*', true], + ['phishing-scam.com?email=someone@hotmail.com', '*hotmail.com*', true], + ['abcdomain.com', '(*.abcdomain.com|abcdomain.com)', true], + ['foo.abcdomain.com', '(*.abcdomain.com|abcdomain.com)', true], + ['abddomain.com', '(*.abcdomain.com|abcdomain.com)', false], + ['abcdomain.com', '*.n.com', false], + ['a.com', '?.com', true], + ['b.com', '?.com', true], + ['ab.com', '?.com', false], + ]; + + tests.forEach(function (test) { + var expected = test.pop(); + it( + 'should return `' + expected + '` for "' + test.join('", "') + '"', + function () { + assert.equal(expected, shExpMatch(test[0], test[1])); + } + ); + }); +}); diff --git a/packages/pac-resolver/test/test.js b/packages/pac-resolver/test/test.js new file mode 100644 index 00000000..6d88b8d3 --- /dev/null +++ b/packages/pac-resolver/test/test.js @@ -0,0 +1,389 @@ +const assert = require('assert'); +const { resolve } = require('path'); +const { readFileSync } = require('fs'); +const { createPacResolver } = require('../'); + +describe('FindProxyForURL', function () { + it('should return `undefined` by default', function (done) { + var FindProxyForURL = createPacResolver( + 'function FindProxyForURL (url, host) {' + ' /* noop */' + '}' + ); + FindProxyForURL('http://foo.com/', 'foo.com', function (err, res) { + if (err) return done(err); + assert.strictEqual(undefined, res); + done(); + }); + }); + + it('should return the value that gets returned', function (done) { + var FindProxyForURL = createPacResolver( + 'function FindProxyForURL (url, host) {' + + ' return { foo: "bar" };' + + '}' + ); + FindProxyForURL('http://foo.com/', 'foo.com', function (err, res) { + if (err) return done(err); + assert.deepEqual({ foo: 'bar' }, res); + done(); + }); + }); + + it('should not modify the passed-in options object', function (done) { + function foo() {} + const opts = { sandbox: { foo } }; + const FindProxyForURL = createPacResolver( + 'function FindProxyForURL (url, host) { return typeof foo; }', + opts + ); + assert.deepEqual(opts, { sandbox: { foo } }); + FindProxyForURL('http://foo.com/', function (err, res) { + if (err) return done(err); + assert.deepEqual('function', res); + done(); + }); + }); + + it('should prevent untrusted code from escaping the sandbox', function () { + let err; + try { + createPacResolver( + `// Real PAC config: + function FindProxyForURL(url, host) { + return "DIRECT"; + } + + // But also run arbitrary code: + var f = this.constructor.constructor(\` + process.exit(1); + \`); + + f(); + ` + ); + } catch (_err) { + err = _err; + } + assert.strictEqual(err.message, 'process is not defined'); + }); + + describe('official docs Example #1', function () { + var FindProxyForURL = createPacResolver( + 'function FindProxyForURL(url, host) {' + + ' if (isPlainHostName(host) ||' + + ' dnsDomainIs(host, ".netscape.com"))' + + ' return "DIRECT";' + + ' else' + + ' return "PROXY w3proxy.netscape.com:8080; DIRECT";' + + '}' + ); + + it('should return "DIRECT" for "localhost"', function (done) { + FindProxyForURL( + 'http://localhost/hello', + 'localhost', + function (err, res) { + if (err) return done(err); + assert.equal('DIRECT', res); + done(); + } + ); + }); + + it('should return "DIRECT" for "foo.netscape.com"', function (done) { + FindProxyForURL( + 'http://foo.netscape.com/', + 'foo.netscape.com', + function (err, res) { + if (err) return done(err); + assert.equal('DIRECT', res); + done(); + } + ); + }); + + it('should return "PROXY …" for "google.com"', function (done) { + FindProxyForURL( + 'http://google.com/t', + 'google.com', + function (err, res) { + if (err) return done(err); + assert.equal( + 'PROXY w3proxy.netscape.com:8080; DIRECT', + res + ); + done(); + } + ); + }); + }); + + describe('official docs Example #1b', function () { + var FindProxyForURL = createPacResolver( + 'function FindProxyForURL(url, host)' + + '{' + + ' if ((isPlainHostName(host) ||' + + ' dnsDomainIs(host, ".netscape.com")) &&' + + ' !localHostOrDomainIs(host, "www.netscape.com") &&' + + ' !localHostOrDomainIs(host, "merchant.netscape.com"))' + + ' return "DIRECT";' + + ' else' + + ' return "PROXY w3proxy.netscape.com:8080; DIRECT";' + + '}' + ); + + it('should return "DIRECT" for "localhost"', function (done) { + FindProxyForURL( + 'http://localhost/hello', + 'localhost', + function (err, res) { + if (err) return done(err); + assert.equal('DIRECT', res); + done(); + } + ); + }); + + it('should return "DIRECT" for "foo.netscape.com"', function (done) { + FindProxyForURL( + 'http://foo.netscape.com/', + 'foo.netscape.com', + function (err, res) { + if (err) return done(err); + assert.equal('DIRECT', res); + done(); + } + ); + }); + + it('should return "PROXY …" for "www.netscape.com"', function (done) { + FindProxyForURL( + 'http://www.netscape.com/', + 'www.netscape.com', + function (err, res) { + if (err) return done(err); + assert.equal( + 'PROXY w3proxy.netscape.com:8080; DIRECT', + res + ); + done(); + } + ); + }); + + it('should return "PROXY …" for "merchant.netscape.com"', function (done) { + FindProxyForURL( + 'http://merchant.netscape.com/', + 'merchant.netscape.com', + function (err, res) { + if (err) return done(err); + assert.equal( + 'PROXY w3proxy.netscape.com:8080; DIRECT', + res + ); + done(); + } + ); + }); + }); + + describe('official docs Example #5', function () { + var FindProxyForURL = createPacResolver( + 'function FindProxyForURL(url, host)' + + '{' + + ' if (url.substring(0, 5) == "http:") {' + + ' return "PROXY http-proxy.mydomain.com:8080";' + + ' }' + + ' else if (url.substring(0, 4) == "ftp:") {' + + ' return "PROXY ftp-proxy.mydomain.com:8080";' + + ' }' + + ' else if (url.substring(0, 7) == "gopher:") {' + + ' return "PROXY gopher-proxy.mydomain.com:8080";' + + ' }' + + ' else if (url.substring(0, 6) == "https:" ||' + + ' url.substring(0, 6) == "snews:") {' + + ' return "PROXY security-proxy.mydomain.com:8080";' + + ' }' + + ' else {' + + ' return "DIRECT";' + + ' }' + + '}' + ); + + it('should return "DIRECT" for "foo://netscape.com"', function (done) { + FindProxyForURL( + 'foo://netscape.com/hello', + 'netscape.com', + function (err, res) { + if (err) return done(err); + assert.equal('DIRECT', res); + done(); + } + ); + }); + + it('should return "PROXY http…" for "http://netscape.com"', function (done) { + FindProxyForURL( + 'http://netscape.com/hello', + 'netscape.com', + function (err, res) { + if (err) return done(err); + assert.equal('PROXY http-proxy.mydomain.com:8080', res); + done(); + } + ); + }); + + it('should return "PROXY ftp…" for "ftp://netscape.com"', function (done) { + FindProxyForURL( + 'ftp://netscape.com/hello', + 'netscape.com', + function (err, res) { + if (err) return done(err); + assert.equal('PROXY ftp-proxy.mydomain.com:8080', res); + done(); + } + ); + }); + + it('should return "PROXY gopher…" for "gopher://netscape.com"', function (done) { + FindProxyForURL( + 'gopher://netscape.com/hello', + 'netscape.com', + function (err, res) { + if (err) return done(err); + assert.equal('PROXY gopher-proxy.mydomain.com:8080', res); + done(); + } + ); + }); + + it('should return "PROXY security…" for "https://netscape.com"', function (done) { + FindProxyForURL( + 'https://netscape.com/hello', + 'netscape.com', + function (err, res) { + if (err) return done(err); + assert.equal('PROXY security-proxy.mydomain.com:8080', res); + done(); + } + ); + }); + + it('should return "PROXY security…" for "snews://netscape.com"', function (done) { + FindProxyForURL( + 'snews://netscape.com/hello', + 'netscape.com', + function (err, res) { + if (err) return done(err); + assert.equal('PROXY security-proxy.mydomain.com:8080', res); + done(); + } + ); + }); + }); + + describe('GitHub issue #3', function () { + var FindProxyForURL = createPacResolver( + 'function FindProxyForURL(url, host) {\n' + + ' if (isHostInAnySubnet(host, ["10.1.2.0", "10.1.3.0"], "255.255.255.0")) {\n' + + ' return "HTTPS proxy.example.com";\n' + + ' }\n' + + '\n' + + ' if (isHostInAnySubnet(host, ["10.2.2.0", "10.2.3.0"], "255.255.255.0")) {\n' + + ' return "HTTPS proxy.example.com";\n' + + ' }\n' + + '\n' + + ' // Everything else, go direct:\n' + + ' return "DIRECT";\n' + + '}\n' + + '\n' + + '// Checks if the single host is within a list of subnets using the single mask.\n' + + 'function isHostInAnySubnet(host, subnets, mask) {\n' + + ' var subnets_length = subnets.length;\n' + + ' for (i = 0; i < subnets_length; i++) {\n' + + ' if (isInNet(host, subnets[i], mask)) {\n' + + ' return true;\n' + + ' }\n' + + ' }\n' + + '}\n' + ); + + it('should return "HTTPS proxy.example.com" for "http://10.1.2.3/bar.html"', function (done) { + FindProxyForURL( + 'http://10.1.2.3/bar.html', + '10.1.2.3', + function (err, res) { + if (err) return done(err); + assert.equal('HTTPS proxy.example.com', res); + done(); + } + ); + }); + + it('should return "DIRECT" for "http://foo.com/bar.html"', function (done) { + FindProxyForURL( + 'http://foo.com/bar.html', + 'foo.com', + function (err, res) { + if (err) return done(err); + assert.equal('DIRECT', res); + done(); + } + ); + }); + }); + + // https://github.com/breakwa11/gfw_whitelist + // https://github.com/TooTallNate/node-pac-resolver/issues/20 + describe('GitHub issue #20', function () { + const FindProxyForURL = createPacResolver( + readFileSync(resolve(__dirname, 'fixtures/gfw_whitelist.pac')) + ); + + it('should return "DIRECT" for "https://example.cn"', function (done) { + FindProxyForURL('https://example.cn/').then((res) => { + assert.equal('DIRECT;', res); + done(); + }, done); + }); + + it('should return "SOCKS5 127.0.0.1:1080;" for "https://example.com"', function (done) { + FindProxyForURL('https://example.com/').then((res) => { + assert.equal('SOCKS5 127.0.0.1:1080;', res); + done(); + }, done); + }); + }); + + describe('`filename` option', function () { + const code = String(function FindProxyForURL() { + throw new Error('fail'); + }); + + it('should include `proxy.pac` in stack traces by default', function (done) { + const FindProxyForURL = createPacResolver(code); + FindProxyForURL('https://example.com/').catch((err) => { + assert(err); + assert.equal(err.message, 'fail'); + assert( + err.stack.indexOf('at FindProxyForURL (proxy.pac:') !== -1 + ); + done(); + }); + }); + + it('should include `fail.pac` in stack traces by option', function (done) { + const FindProxyForURL = createPacResolver(code, { + filename: 'fail.pac', + }); + FindProxyForURL('https://example.com/').catch((err) => { + assert(err); + assert.equal(err.message, 'fail'); + assert( + err.stack.indexOf('at FindProxyForURL (fail.pac:') !== -1 + ); + done(); + }); + }); + }); +}); diff --git a/packages/pac-resolver/test/timeRange.js b/packages/pac-resolver/test/timeRange.js new file mode 100644 index 00000000..711b4837 --- /dev/null +++ b/packages/pac-resolver/test/timeRange.js @@ -0,0 +1,70 @@ +/** + * Module dependencies. + */ + +var assert = require('assert'); +const { timeRange } = require('../').sandbox; +var vanillaGetHours = Date.prototype.getHours; +var vanillaGetMinutes = Date.prototype.getMinutes; +var vanillaGetSeconds = Date.prototype.getSeconds; +var vanillaGetUTCHours = Date.prototype.getUTCHours; +var vanillaGetUTCMinutes = Date.prototype.getUTCMinutes; +var vanillaGetUTCSeconds = Date.prototype.getUTCSeconds; + +describe('hooks', function () { + before(function () { + // Setting local time as 01:24:30 + Date.prototype.getHours = function () { + return 1; + }; + Date.prototype.getMinutes = function () { + return 24; + }; + Date.prototype.getSeconds = function () { + return 30; + }; + + // Setting UTC time as 19:54:30 + Date.prototype.getUTCHours = function () { + return 19; + }; + Date.prototype.getUTCMinutes = function () { + return 54; + }; + Date.prototype.getUTCSeconds = function () { + return 30; + }; + }); + + after(function () { + Date.prototype.getHours = vanillaGetHours; + Date.prototype.getUTCHours = vanillaGetUTCHours; + Date.prototype.getUTCMinutes = vanillaGetUTCMinutes; + Date.prototype.getUTCSeconds = vanillaGetUTCSeconds; + }); + + describe('timeRange()', function () { + var tests = [ + [1, true], + [1, 2, true], + [0, 0, 0, 30, false], + [0, 0, 0, 0, 30, 0, false], + [0, 0, 0, 0, 30, 0, 'GMT', false], + [0, 0, 0, 20, 0, 0, 'GMT', true], + ]; + + tests.forEach(function (test) { + var expected = test.pop(); + it( + 'should return `' + + expected + + '` for "' + + test.join('", "') + + '"', + function () { + assert.equal(expected, timeRange.apply(this, test)); + } + ); + }); + }); +}); diff --git a/packages/pac-resolver/test/weekdayRange.js b/packages/pac-resolver/test/weekdayRange.js new file mode 100644 index 00000000..e4a092eb --- /dev/null +++ b/packages/pac-resolver/test/weekdayRange.js @@ -0,0 +1,55 @@ +/** + * Module dependencies. + */ + +var assert = require('assert'); +const { weekdayRange } = require('../').sandbox; +var vanillaGetUTCDay = Date.prototype.getUTCDay; +var vanillaGetDay = Date.prototype.getDay; + +describe('hooks', function () { + before(function () { + Date.prototype.getDay = function () { + return 6; + }; // Setting local weekday as SAT(6) + Date.prototype.getUTCDay = function () { + return 5; + }; // Setting UTC weekday as FRI(5) + }); + + after(function () { + Date.prototype.getUTCDay = vanillaGetUTCDay; + Date.prototype.getDay = vanillaGetDay; + }); + + describe('weekdayRange(wd1, wd2, gmt)', function () { + var tests = [ + ['MON', 'FRI', false], + ['MON', 'FRI', 'GMT', true], + ['SAT', true], + ['SAT', 'GMT', false], + ['FRI', 'MON', true], + ['SAT', 'MON', 'GMT', false], + ['SOME', 'RANDOM', false], + ['RANDOM', false], + ['RANDOM', 'VALUE', 'IST', false], + ]; + + tests.forEach(function (test) { + var expected = test.pop(); + it( + 'should return `' + + expected + + '` for "' + + test.join('", "') + + '"', + function () { + assert.equal( + expected, + weekdayRange(test[0], test[1], test[2]) + ); + } + ); + }); + }); +}); diff --git a/packages/pac-resolver/tsconfig.json b/packages/pac-resolver/tsconfig.json new file mode 100644 index 00000000..24b6cf7e --- /dev/null +++ b/packages/pac-resolver/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/proxy-agent/README.md b/packages/proxy-agent/README.md new file mode 100644 index 00000000..48417577 --- /dev/null +++ b/packages/proxy-agent/README.md @@ -0,0 +1,99 @@ +proxy-agent +=========== +### Maps proxy protocols to `http.Agent` implementations +[![Build Status](https://travis-ci.org/TooTallNate/node-proxy-agent.svg?branch=master)](https://travis-ci.org/TooTallNate/node-proxy-agent) + +This module provides an `http.Agent` implementation which automatically uses +proxy servers based off of the various proxy-related environment variables +(`HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY` among others). + +Which proxy is used for each HTTP request is determined by the +[`proxy-from-env`](https://www.npmjs.com/package/proxy-from-env) module, so +check its documentation for instructions on configuring your environment variables. + +An LRU cache is used so that `http.Agent` instances are transparently re-used for +subsequent HTTP requests to the same proxy server. + +The currently implemented protocol mappings are listed in the table below: + + +| Protocol | Proxy Agent for `http` requests | Proxy Agent for `https` requests | Example +|:----------:|:-------------------------------:|:--------------------------------:|:--------: +| `http` | [http-proxy-agent][] | [https-proxy-agent][] | `http://proxy-server-over-tcp.com:3128` +| `https` | [http-proxy-agent][] | [https-proxy-agent][] | `https://proxy-server-over-tls.com:3129` +| `socks(v5)`| [socks-proxy-agent][] | [socks-proxy-agent][] | `socks://username:password@some-socks-proxy.com:9050` (username & password are optional) +| `socks5` | [socks-proxy-agent][] | [socks-proxy-agent][] | `socks5://username:password@some-socks-proxy.com:9050` (username & password are optional) +| `socks4` | [socks-proxy-agent][] | [socks-proxy-agent][] | `socks4://some-socks-proxy.com:9050` +| `pac-*` | [pac-proxy-agent][] | [pac-proxy-agent][] | `pac+http://www.example.com/proxy.pac` + + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install proxy-agent +``` + + +Example +------- + +```ts +import * as https from 'https'; +import { ProxyAgent } from 'proxy-agent'; + +// The correct proxy `Agent` implementation to use will be determined +// via the `http_proxy` / `https_proxy` / `no_proxy` / etc. env vars +const agent = new ProxyAgent(); + +// The rest works just like any other normal HTTP request +https.get('https://jsonip.com', { agent }, (res) => { + console.log(res.statusCode, res.headers); + res.pipe(process.stdout); +}); +``` + + +API +--- + +### new ProxyAgent() + +Creates an `http.Agent` instance which relies on the various proxy-related +environment variables. An LRU cache is used, so the same `http.Agent` instance +will be returned if identical args are passed in. + + +License +------- + +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +[http-proxy-agent]: https://github.com/TooTallNate/node-http-proxy-agent +[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent +[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent +[pac-proxy-agent]: https://github.com/TooTallNate/node-pac-proxy-agent diff --git a/packages/proxy-agent/jest.config.js b/packages/proxy-agent/jest.config.js new file mode 100644 index 00000000..ee66e76e --- /dev/null +++ b/packages/proxy-agent/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/packages/proxy-agent/package.json b/packages/proxy-agent/package.json new file mode 100644 index 00000000..196498df --- /dev/null +++ b/packages/proxy-agent/package.json @@ -0,0 +1,61 @@ +{ + "name": "proxy-agent", + "version": "5.0.0", + "description": "Maps proxy protocols to `http.Agent` implementations", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "test": "jest --env node --verbose --bail", + "lint": "eslint . --ext .ts", + "prepublishOnly": "npm run build" + }, + "engines": { + "node": ">= 14" + }, + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/node-proxy-agent.git" + }, + "keywords": [ + "http", + "https", + "socks", + "agent", + "mapping", + "proxy" + ], + "author": "Nathan Rajlich (http://n8.io/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/TooTallNate/node-proxy-agent/issues" + }, + "homepage": "https://github.com/TooTallNate/node-proxy-agent", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "lru-cache": "^9.1.1", + "pac-proxy-agent": "^5.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^7.0.0" + }, + "devDependencies": { + "@types/agent-base": "^4.2.0", + "@types/debug": "^4.1.7", + "@types/jest": "^29.5.1", + "@types/node": "^14.18.43", + "@types/proxy-from-env": "^1.0.1", + "async-listen": "^2.1.0", + "jest": "^29.5.0", + "proxy": "workspace:*", + "socksv5": "github:TooTallNate/socksv5#fix/dstSock-close-event", + "ts-jest": "^29.1.0", + "tsconfig": "workspace:*", + "typescript": "^5.0.4" + } +} diff --git a/packages/proxy-agent/src/index.ts b/packages/proxy-agent/src/index.ts new file mode 100644 index 00000000..41bb2576 --- /dev/null +++ b/packages/proxy-agent/src/index.ts @@ -0,0 +1,108 @@ +import * as http from 'http'; +import * as https from 'https'; +import { LRUCache } from 'lru-cache'; +import { Agent, AgentConnectOpts } from 'agent-base'; +import createDebug from 'debug'; +import { getProxyForUrl } from 'proxy-from-env'; +import { PacProxyAgent, PacProxyAgentOptions } from 'pac-proxy-agent'; +import { HttpProxyAgent, HttpProxyAgentOptions } from 'http-proxy-agent'; +import { HttpsProxyAgent, HttpsProxyAgentOptions } from 'https-proxy-agent'; +import { SocksProxyAgent, SocksProxyAgentOptions } from 'socks-proxy-agent'; + +const debug = createDebug('proxy-agent'); + +const PROTOCOLS = [ + ...HttpProxyAgent.protocols, + ...SocksProxyAgent.protocols, + ...PacProxyAgent.protocols, +] as const; + +type ValidProtocol = (typeof PROTOCOLS)[number]; + +/** + * Supported proxy types. + */ +export const proxies: { + [P in ValidProtocol]: new (...args: never[]) => Agent; +} = { + http: HttpProxyAgent, + https: HttpsProxyAgent, + socks: SocksProxyAgent, + socks4: SocksProxyAgent, + socks4a: SocksProxyAgent, + socks5: SocksProxyAgent, + socks5h: SocksProxyAgent, + 'pac-data': PacProxyAgent, + 'pac-file': PacProxyAgent, + 'pac-ftp': PacProxyAgent, + 'pac-http': PacProxyAgent, + 'pac-https': PacProxyAgent, +}; + +function isValidProtocol(v: string): v is ValidProtocol { + return (PROTOCOLS as readonly string[]).includes(v); +} + +export type ProxyAgentOptions = HttpProxyAgentOptions<""> & + HttpsProxyAgentOptions<""> & + SocksProxyAgentOptions & + PacProxyAgentOptions<"">; + +/** + * Uses the appropriate `Agent` subclass based off of the "proxy" + * environment variables that are currently set. + * + * An LRU cache is used, to prevent unnecessary creation of proxy + * `http.Agent` instances. + */ +export class ProxyAgent extends Agent { + /** + * Cache for `Agent` instances. + */ + cache = new LRUCache({ max: 20 }); + + connectOpts?: ProxyAgentOptions; + + constructor(opts?: ProxyAgentOptions) { + super(); + debug('Creating new ProxyAgent instance'); + this.connectOpts = opts; + } + + async connect( + req: http.ClientRequest, + opts: AgentConnectOpts + ): Promise { + const protocol = opts.secureEndpoint ? 'https:' : 'http:'; + const host = req.getHeader('host'); + const url = new URL(req.path, `${protocol}//${host}`).href; + debug('Request URL: %o', url); + + const proxy = getProxyForUrl(url); + + if (!proxy) { + debug('Proxy not enabled for URL: %o', url); + return opts.secureEndpoint ? https.globalAgent : http.globalAgent; + } + + debug('Proxy URL: %o', proxy); + + // attempt to get a cached `http.Agent` instance first + let agent = this.cache.get(proxy); + if (!agent) { + const proxyUrl = new URL(proxy); + const proxyProto = proxyUrl.protocol.replace(':', ''); + if (!isValidProtocol(proxyProto)) { + throw new Error(`Unsupported protocol for proxy URL: ${proxy}`); + } + const ctor = proxies[proxyProto]; + // @ts-expect-error meh… + agent = new ctor(proxy, this.connectOpts); + this.cache.set(proxy, agent); + } else { + debug('Cache hit for proxy URL: %o', proxy); + } + + return agent; + } +} diff --git a/packages/proxy-agent/test/ssl-cert-snakeoil.key b/packages/proxy-agent/test/ssl-cert-snakeoil.key new file mode 100644 index 00000000..fd125012 --- /dev/null +++ b/packages/proxy-agent/test/ssl-cert-snakeoil.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCzURxIqzer0ACAbX/lHdsn4Gd9PLKrf7EeDYfIdV0HZKPD8WDr +bBx2/fBu0OW2sjnzv/SVZbJ0DAuPE/p0+eT0qb2qC10iz9iTD7ribd7gxhirVb8y +b3fBjXsxc8V8p4Ny1LcvNSqCjwUbJqdRogfoJeTiqPM58z5sNzuv5iq7iwIDAQAB +AoGAPMQy4olrP0UotlzlJ36bowLP70ffgHCwU+/f4NWs5fF78c3du0oSx1w820Dd +Z7E0JF8bgnlJJTxjumPZz0RUCugrEHBKJmzEz3cxF5E3+7NvteZcjKn9D67RrM5x +1/uSZ9cqKE9cYvY4fSuHx18diyZ4axR/wB1Pea2utjjDM+ECQQDb9ZbmmaWMiRpQ +5Up+loxP7BZNPsEVsm+DVJmEFbaFgGfncWBqSIqnPNjMwTwj0OigTwCAEGPkfRVW +T0pbYWCxAkEA0LK7SCTwzyDmhASUalk0x+3uCAA6ryFdwJf/wd8TRAvVOmkTEldX +uJ7ldLvfrONYO3v56uKTU/SoNdZYzKtO+wJAX2KM4ctXYy5BXztPpr2acz4qHa1N +Bh+vBAC34fOYhyQ76r3b1btHhWZ5jbFuZwm9F2erC94Ps5IaoqcX07DSwQJAPKGw +h2U0EPkd/3zVIZCJJQya+vgWFIs9EZcXVtvYXQyTBkVApTN66MhBIYjzkub5205J +bVQmOV37AKklY1DhwQJAA1wos0cYxro02edzatxd0DIR2r4qqOqLkw6BhYHhq6HJ +ZvIcQkHqdSXzdETFc01I1znDGGIrJHcnvKWgBPoEUg== +-----END RSA PRIVATE KEY----- diff --git a/packages/proxy-agent/test/ssl-cert-snakeoil.pem b/packages/proxy-agent/test/ssl-cert-snakeoil.pem new file mode 100644 index 00000000..b115a5e9 --- /dev/null +++ b/packages/proxy-agent/test/ssl-cert-snakeoil.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB1TCCAT4CCQDV5mPlzm9+izANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDEyQ3 +NTI3YmQ3Ny1hYjNlLTQ3NGItYWNlNy1lZWQ2MDUzOTMxZTcwHhcNMTUwNzA2MjI0 +NTA3WhcNMjUwNzAzMjI0NTA3WjAvMS0wKwYDVQQDEyQ3NTI3YmQ3Ny1hYjNlLTQ3 +NGItYWNlNy1lZWQ2MDUzOTMxZTcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +ALNRHEirN6vQAIBtf+Ud2yfgZ308sqt/sR4Nh8h1XQdko8PxYOtsHHb98G7Q5bay +OfO/9JVlsnQMC48T+nT55PSpvaoLXSLP2JMPuuJt3uDGGKtVvzJvd8GNezFzxXyn +g3LUty81KoKPBRsmp1GiB+gl5OKo8znzPmw3O6/mKruLAgMBAAEwDQYJKoZIhvcN +AQEFBQADgYEACzoHUF8UV2Z6541Q2wKEA0UFUzmUjf/E1XwBO+1P15ZZ64uw34B4 +1RwMPtAo9RY/PmICTWtNxWGxkzwb2JtDWtnxVER/lF8k2XcXPE76fxTHJF/BKk9J +QU8OTD1dd9gHCBviQB9TqntRZ5X7axjtuWjb2umY+owBYzAHZkp1HKI= +-----END CERTIFICATE----- diff --git a/packages/proxy-agent/test/test.ts b/packages/proxy-agent/test/test.ts new file mode 100644 index 00000000..f477025b --- /dev/null +++ b/packages/proxy-agent/test/test.ts @@ -0,0 +1,181 @@ +import * as fs from 'fs'; +import * as http from 'http'; +import * as https from 'https'; +import assert from 'assert'; +import { json, req } from 'agent-base'; +import { ProxyServer, createProxy } from 'proxy'; +// @ts-expect-error no types +import socks from 'socksv5'; +import { listen } from 'async-listen'; +import { ProxyAgent } from '../src'; + +const sslOptions = { + key: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.key'), + cert: fs.readFileSync(__dirname + '/ssl-cert-snakeoil.pem'), +}; + +describe('ProxyAgent', () => { + // target servers + let httpServer: http.Server; + let httpServerUrl: URL; + let httpsServer: https.Server; + let httpsServerUrl: URL; + + // proxy servers + let httpProxyServer: ProxyServer; + let httpProxyServerUrl: URL; + let httpsProxyServer: ProxyServer; + let httpsProxyServerUrl: URL; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let socksServer: any; + let socksPort: number; + + beforeAll(async () => { + // setup target HTTP server + httpServer = http.createServer(); + httpServerUrl = (await listen(httpServer)) as URL; + }); + + beforeAll(async () => { + // setup target SSL HTTPS server + httpsServer = https.createServer(sslOptions); + httpsServerUrl = (await listen(httpsServer)) as URL; + }); + + beforeAll(async () => { + // setup SOCKS proxy server + // @ts-expect-error no types + socksServer = socks.createServer((_info, accept) => { + accept(); + }); + socksServer.useAuth(socks.auth.None()); + await listen(socksServer); + socksPort = socksServer.address().port; + }); + + beforeAll(async () => { + // setup HTTP proxy server + httpProxyServer = createProxy(); + httpProxyServerUrl = (await listen(httpProxyServer)) as URL; + }); + + beforeAll(async () => { + // setup SSL HTTPS proxy server + httpsProxyServer = createProxy(https.createServer(sslOptions)); + httpsProxyServerUrl = (await listen(httpsProxyServer)) as URL; + }); + + afterAll(() => { + socksServer.close(); + httpServer.close(); + httpsServer.close(); + httpProxyServer.close(); + httpsProxyServer.close(); + }); + + beforeEach(() => { + delete process.env.HTTP_PROXY; + delete process.env.HTTPS_PROXY; + delete process.env.NO_PROXY; + }); + + describe('"http" module', () => { + it('should work with no proxy from env', async () => { + httpServer.once('request', function (req, res) { + res.end(JSON.stringify(req.headers)); + }); + + // `NO_PROXY` should take precedence + process.env.NO_PROXY = '*'; + process.env.HTTP_PROXY = httpProxyServerUrl.href; + const agent = new ProxyAgent(); + + const res = await req(new URL('/test', httpServerUrl), { agent }); + const body = await json(res); + assert.equal(httpServerUrl.host, body.host); + assert(!('via' in body)); + }); + + it('should work over "http" proxy', async () => { + httpServer.once('request', function (req, res) { + res.end(JSON.stringify(req.headers)); + }); + + process.env.HTTP_PROXY = httpProxyServerUrl.href; + const agent = new ProxyAgent(); + + const res = await req(new URL('/test', httpServerUrl), { agent }); + const body = await json(res); + assert.equal(httpServerUrl.host, body.host); + assert('via' in body); + }); + + it('should work over "https" proxy', async () => { + httpServer.once('request', function (req, res) { + res.end(JSON.stringify(req.headers)); + }); + + process.env.HTTP_PROXY = httpsProxyServerUrl.href; + const agent = new ProxyAgent({ rejectUnauthorized: false }); + + const res = await req(new URL('/test', httpServerUrl), { agent }); + const body = await json(res); + assert.equal(httpServerUrl.host, body.host); + }); + + it('should work over "socks" proxy', async () => { + httpServer.once('request', function (req, res) { + res.end(JSON.stringify(req.headers)); + }); + + process.env.HTTP_PROXY = `socks://localhost:${socksPort}`; + const agent = new ProxyAgent(); + + const res = await req(new URL('/test', httpServerUrl), { agent }); + const body = await json(res); + assert.equal(httpServerUrl.host, body.host); + }); + }); + + describe('"https" module', () => { + it('should work over "https" proxy', async () => { + let gotReq = false; + httpsServer.once('request', function (req, res) { + res.end(JSON.stringify(req.headers)); + gotReq = true; + }); + + process.env.HTTPS_PROXY = httpsProxyServerUrl.href; + const agent = new ProxyAgent({ rejectUnauthorized: false }); + + const res = await req(new URL('/test', httpsServerUrl), { + agent, + rejectUnauthorized: false, + }); + const body = await json(res); + assert(gotReq); + assert.equal(httpsServerUrl.host, body.host); + }); + + describe('over "socks" proxy', () => { + it('should work', async () => { + let gotReq = false; + httpsServer.once('request', function (req, res) { + gotReq = true; + res.end(JSON.stringify(req.headers)); + }); + + process.env.HTTP_PROXY = `socks://localhost:${socksPort}`; + const agent = new ProxyAgent(); + + const res = await req(new URL('/test', httpsServerUrl), { + agent, + rejectUnauthorized: false, + }); + const body = await json(res); + assert(gotReq); + assert.equal(httpsServerUrl.host, body.host); + }); + }); + }); +}); diff --git a/packages/proxy-agent/test/tsconfig.json b/packages/proxy-agent/test/tsconfig.json new file mode 100644 index 00000000..a79e2e63 --- /dev/null +++ b/packages/proxy-agent/test/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../tsconfig.json", + "include": ["test.ts"] +} diff --git a/packages/proxy-agent/tsconfig.json b/packages/proxy-agent/tsconfig.json new file mode 100644 index 00000000..24b6cf7e --- /dev/null +++ b/packages/proxy-agent/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/proxy/.eslintignore b/packages/proxy/.eslintignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/packages/proxy/.eslintignore @@ -0,0 +1 @@ +dist diff --git a/packages/proxy/README.md b/packages/proxy/README.md new file mode 100644 index 00000000..2cedbebc --- /dev/null +++ b/packages/proxy/README.md @@ -0,0 +1,136 @@ +proxy +===== +### An HTTP proxy written with Node.js (think Squid) +[![Build Status](https://github.com/TooTallNate/proxy/workflows/Node%20CI/badge.svg)](https://github.com/TooTallNate/proxy/actions?workflow=Node+CI) + +This module provides standard "HTTP proxy" logic. You can script your own server +using the `proxy` server API. Be sure to take a look at the "Examples" section +below. + +There is also a companion `proxy(1)` CLI tool, which spawns an HTTP(s) proxy +server with the specified options. + +You could think of `proxy(1)` as similar to some of the other popular open +source HTTP proxy software: + + * [Squid][] + * [Privoxy][] + * [Apache][] with [`mod_proxy`][mod_proxy] + * [More…](http://wikipedia.org/wiki/Proxy_server#Web_proxy_servers) + + +Installation +------------ + +Install with `npm`: + +``` bash +$ npm install proxy +``` + +If you would like to have the `proxy(1)` CLI program in your `$PATH`, then +install "globally": + +``` bash +$ npm install -g proxy +``` + + +Examples +-------- + +#### Basic HTTP(s) proxy server + +A basic HTTP(s) server with all the default options. All requests are allowed. +CONNECT HTTP method works as well. + +``` js +var http = require('http'); +var setup = require('proxy'); + +var server = setup(http.createServer()); +server.listen(3128, function () { + var port = server.address().port; + console.log('HTTP(s) proxy server listening on port %d', port); +}); +``` + + +CLI Tool Examples +----------------- + +The `proxy(1)` CLI tool can be used to spawn HTTP(s) proxy server instances with +various options. + +#### Port to bind to + +Pass the `-p`/`--port` option to with a port number to specify a TCP port to +bind to. Defaults to __3128__ if none is specified. + +``` bash +$ proxy --port 8080 +``` + +#### Custom `Proxy-Authenticate` command + +Pass the `-a`/`--authenticate` switch with a command to execute when the client +`Proxy-Authorization` header is given. This command determines whether or not the +request is authorized based on the "exit code" of the command. + +The relevant request authentication information is passed in as +`PROXY_AUTH_USERNAME`, `PROXY_AUTH_PASSWORD` and `PROXY_AUTH_SCHEME` environment +variables. + +For example, to authorize "Basic" authentication with username "foo" and +password "bar": + +``` bash +$ proxy --authenticate 'if \ + [ "$PROXY_AUTH_USERNAME" = "foo" ] && \ + [ "$PROXY_AUTH_PASSWORD" = "bar" ]; \ + then exit 0; \ + fi; \ + exit 1;' +``` + +#### Custom outgoing interface + +Pass the `-l`/`--local-address` argument with an IP address of the network +interface to send the outgoing requests through. It is the equivalent of setting +a `localAddress` field in the options when calling `http.request()`. + +``` bash +$ proxy --local-address 192.168.0.10 +``` + +License +------- + +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +[Squid]: http://www.squid-cache.org/ +[Privoxy]: http://www.privoxy.org/ +[Apache]: http://www.apache.org/ +[mod_proxy]: http://httpd.apache.org/docs/current/mod/mod_proxy.html diff --git a/packages/proxy/jest.config.js b/packages/proxy/jest.config.js new file mode 100644 index 00000000..ee66e76e --- /dev/null +++ b/packages/proxy/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/packages/proxy/package.json b/packages/proxy/package.json new file mode 100644 index 00000000..610b8875 --- /dev/null +++ b/packages/proxy/package.json @@ -0,0 +1,53 @@ +{ + "name": "proxy", + "version": "1.0.2", + "description": "An HTTP proxy written with Node.js (think Squid)", + "main": "./dist/proxy.js", + "types": "./dist/proxy.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "test": "mocha --reporter spec", + "lint": "eslint . --ext .ts", + "prepublishOnly": "npm run build" + }, + "keywords": [ + "http", + "https", + "proxy", + "connect", + "tunnel", + "squid", + "privoxy", + "apache", + "mod_proxy", + "via", + "x-forwarded-for" + ], + "author": "Nathan Rajlich (http://n8.io/)", + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/proxy.git" + }, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "^2.0.0", + "args": "^5.0.3", + "basic-auth-parser": "0.0.2-1", + "debug": "^4.3.4" + }, + "devDependencies": { + "@types/args": "^5.0.0", + "@types/debug": "^4.1.7", + "@types/mocha": "^5.2.7", + "@types/node": "^14.18.43", + "mocha": "6", + "tsconfig": "workspace:*", + "typescript": "^5.0.4" + }, + "engines": { + "node": ">= 14" + } +} diff --git a/packages/proxy/src/bin/proxy.ts b/packages/proxy/src/bin/proxy.ts new file mode 100755 index 00000000..19975f29 --- /dev/null +++ b/packages/proxy/src/bin/proxy.ts @@ -0,0 +1,103 @@ +#!/usr/bin/env node +import args from 'args'; +import createDebug from 'debug' +import { spawn } from 'child_process'; +import once from '@tootallnate/once'; +// @ts-expect-error no types for "basic-auth-parser" +import basicAuthParser = require('basic-auth-parser'); +import { createProxy } from '../proxy'; +//import pkg from '../pkg'; + +const debug = createDebug('proxy'); + +process.title = 'proxy'; + +args.option( + 'port', + 'Port number to the proxy server should bind to', + 3128, + parseInt +) + .option( + 'authenticate', + '"authenticate" command to run when the "Proxy-Authorization" header is sent', + '', + String + ) + //.option( + // 'local-address', + // 'IP address of the network interface to send the outgoing requests through', + // '', + // String + //); + +//const flags = args.parse(process.argv, { name: pkg.name }); +const flags = args.parse(process.argv); +const { port, authenticate } = flags; + +const proxy = createProxy(); + +/** + * Outbound proxy requests will use `agent: false`. + */ + +//debug("setting outbound proxy request's `agent` to `false`"); +//proxy.agent = false; + +/** + * Proxy outgoing request localAddress parameter + */ + +//if (flags.localAddress) { +// proxy.localAddress = flags.localAddress; +//} + +/** + * Proxy authenticate function. + */ + +if (authenticate) { + debug('setting `authenticate()` function for: "%s"', authenticate); + proxy.authenticate = async (req) => { + debug('authenticate(): "%s"', authenticate); + + // parse the "Proxy-Authorization" header + const auth = req.headers['proxy-authorization']; + if (!auth) { + // optimization: don't invoke the child process if no + // "Proxy-Authorization" header was given + return false; + } + const parsed = basicAuthParser(auth); + debug('parsed "Proxy-Authorization": %j', parsed); + + // spawn a child process with the user-specified "authenticate" command + const env = { ...process.env }; + // add "auth" related ENV variables + for (const [ key, value ] of Object.entries(parsed)) { + env['PROXY_AUTH_' + key.toUpperCase()] = value as string; + } + + // TODO: add Windows support (use `cross-spawn`?) + const child = spawn('/bin/sh', ['-c', authenticate], { + env, + stdio: ['ignore', 'inherit', 'inherit'] + }); + + const [code, signal] = await once(child, 'exit'); + debug( + 'authentication child process "exit" event: %s %s', + code, + signal + ); + return code === 0; + }; +} + +proxy.listen(port, function() { + console.log( + 'HTTP(s) proxy server listening on port %d', + // @ts-expect-error "port" is a number + proxy.address().port + ); +}); diff --git a/packages/proxy/src/pkg.ts b/packages/proxy/src/pkg.ts new file mode 100644 index 00000000..3c359ec4 --- /dev/null +++ b/packages/proxy/src/pkg.ts @@ -0,0 +1,6 @@ +import { join } from 'path'; +import { readFileSync } from 'fs'; + +export default JSON.parse( + readFileSync(join(__dirname, '../package.json'), 'utf8') +); \ No newline at end of file diff --git a/packages/proxy/src/proxy.ts b/packages/proxy/src/proxy.ts new file mode 100644 index 00000000..c58c616f --- /dev/null +++ b/packages/proxy/src/proxy.ts @@ -0,0 +1,463 @@ +import assert from 'assert'; +import * as net from 'net'; +import * as url from 'url'; +import * as http from 'http'; +import * as os from 'os'; +import pkg from './pkg'; + +import createDebug from 'debug'; + +// log levels +const debug = { + request: createDebug('proxy ← ← ←'), + response: createDebug('proxy → → →'), + proxyRequest: createDebug('proxy ↑ ↑ ↑'), + proxyResponse: createDebug('proxy ↓ ↓ ↓'), +}; + +// hostname +const hostname = os.hostname(); + +export interface ProxyServer extends http.Server { + authenticate?: (req: http.IncomingMessage) => boolean | Promise; +} + +/** + * Sets up an `http.Server` or `https.Server` instance with the necessary + * "request" and "connect" event listeners in order to make the server act + * as an HTTP proxy. + */ +export function createProxy(server?: http.Server): ProxyServer { + if (!server) server = http.createServer(); + server.on('request', onrequest); + server.on('connect', onconnect); + return server; +} + +/** + * 13.5.1 End-to-end and Hop-by-hop Headers + * + * Hop-by-hop headers must be removed by the proxy before passing it on to the + * next endpoint. Per-request basis hop-by-hop headers MUST be listed in a + * Connection header, (section 14.10) to be introduced into HTTP/1.1 (or later). + */ +const hopByHopHeaders = [ + 'Connection', + 'Keep-Alive', + 'Proxy-Authenticate', + 'Proxy-Authorization', + 'TE', + 'Trailers', + 'Transfer-Encoding', + 'Upgrade', +]; + +// create a case-insensitive RegExp to match "hop by hop" headers +const isHopByHop = new RegExp('^(' + hopByHopHeaders.join('|') + ')$', 'i'); + +/** + * Iterator function for the request/response's "headers". + */ +function* eachHeader(obj: http.IncomingMessage) { + // every even entry is a "key", every odd entry is a "value" + let key: string | null = null; + for (const v of obj.rawHeaders) { + if (key === null) { + key = v; + } else { + yield [key, v]; + key = null; + } + } +} + +/** + * HTTP GET/POST/DELETE/PUT, etc. proxy requests. + */ +async function onrequest( + this: ProxyServer, + req: http.IncomingMessage, + res: http.ServerResponse +) { + debug.request('%s %s HTTP/%s ', req.method, req.url, req.httpVersion); + const socket = req.socket; + + // pause the socket during authentication so no data is lost + socket.pause(); + + try { + const success = await authenticate(this, req); + if (!success) return requestAuthorization(req, res); + } catch (_err: unknown) { + const err = _err as Error; + // an error occured during login! + res.writeHead(500); + res.end((err.stack || err.message || err) + '\n'); + return; + } + + socket.resume(); + const parsed = url.parse(req.url || '/'); + + // setup outbound proxy request HTTP headers + const headers: http.OutgoingHttpHeaders = {}; + let hasXForwardedFor = false; + let hasVia = false; + const via = '1.1 ' + hostname + ' (proxy/' + pkg.version + ')'; + + for (const header of eachHeader(req)) { + debug.request('Request Header: %o', header); + const key = header[0]; + let value = header[1]; + const keyLower = key.toLowerCase(); + + if (!hasXForwardedFor && 'x-forwarded-for' === keyLower) { + // append to existing "X-Forwarded-For" header + // http://en.wikipedia.org/wiki/X-Forwarded-For + hasXForwardedFor = true; + value += ', ' + socket.remoteAddress; + debug.proxyRequest( + 'appending to existing "%s" header: "%s"', + key, + value + ); + } + + if (!hasVia && 'via' === keyLower) { + // append to existing "Via" header + hasVia = true; + value += ', ' + via; + debug.proxyRequest( + 'appending to existing "%s" header: "%s"', + key, + value + ); + } + + if (isHopByHop.test(key)) { + debug.proxyRequest('ignoring hop-by-hop header "%s"', key); + } else { + const v = headers[key] as string; + if (Array.isArray(v)) { + v.push(value); + } else if (null != v) { + headers[key] = [v, value]; + } else { + headers[key] = value; + } + } + } + + // add "X-Forwarded-For" header if it's still not here by now + // http://en.wikipedia.org/wiki/X-Forwarded-For + if (!hasXForwardedFor) { + headers['X-Forwarded-For'] = socket.remoteAddress; + debug.proxyRequest( + 'adding new "X-Forwarded-For" header: "%s"', + headers['X-Forwarded-For'] + ); + } + + // add "Via" header if still not set by now + if (!hasVia) { + headers.Via = via; + debug.proxyRequest('adding new "Via" header: "%s"', headers.Via); + } + + // custom `http.Agent` support, set `server.agent` + //let agent = server.agent; + //if (null != agent) { + // debug.proxyRequest( + // 'setting custom `http.Agent` option for proxy request: %s', + // agent + // ); + // parsed.agent = agent; + // agent = null; + //} + + //if (!parsed.port) { + // // default the port number if not specified, for >= node v0.11.6... + // // https://github.com/joyent/node/issues/6199 + // parsed.port = 80; + //} + + if (parsed.protocol !== 'http:') { + // only "http://" is supported, "https://" should use CONNECT method + res.writeHead(400); + res.end('Only "http:" protocol prefix is supported\n'); + return; + } + + //if (server.localAddress) { + // parsed.localAddress = server.localAddress; + //} + + let gotResponse = false; + const proxyReq = http.request({ + ...parsed, + method: req.method, + headers, + }); + debug.proxyRequest('%s %s HTTP/1.1 ', proxyReq.method, proxyReq.path); + + proxyReq.on('response', function (proxyRes) { + debug.proxyResponse('HTTP/1.1 %s', proxyRes.statusCode); + gotResponse = true; + + const headers: http.OutgoingHttpHeaders = {}; + for (const [key, value] of eachHeader(proxyRes)) { + debug.proxyResponse('Proxy Response Header: "%s: %s"', key, value); + if (isHopByHop.test(key)) { + debug.response('ignoring hop-by-hop header "%s"', key); + } else { + const v = headers[key] as string; + if (Array.isArray(v)) { + v.push(value); + } else if (null != v) { + headers[key] = [v, value]; + } else { + headers[key] = value; + } + } + } + + debug.response('HTTP/1.1 %s', proxyRes.statusCode); + res.writeHead(proxyRes.statusCode || 200, headers); + proxyRes.pipe(res); + res.on('finish', onfinish); + }); + + proxyReq.on('error', function (err: NodeJS.ErrnoException) { + debug.proxyResponse( + 'proxy HTTP request "error" event\n%s', + err.stack || err + ); + cleanup(); + if (gotResponse) { + debug.response( + 'already sent a response, just destroying the socket...' + ); + socket.destroy(); + } else if ('ENOTFOUND' == err.code) { + debug.response('HTTP/1.1 404 Not Found'); + res.writeHead(404); + res.end(); + } else { + debug.response('HTTP/1.1 500 Internal Server Error'); + res.writeHead(500); + res.end(); + } + }); + + // if the client closes the connection prematurely, + // then close the upstream socket + function onclose() { + debug.request( + 'client socket "close" event, aborting HTTP request to "%s"', + req.url + ); + proxyReq.abort(); + cleanup(); + } + socket.on('close', onclose); + + function onfinish() { + debug.response('"finish" event'); + cleanup(); + } + + function cleanup() { + debug.response('cleanup'); + socket.removeListener('close', onclose); + res.removeListener('finish', onfinish); + } + + req.pipe(proxyReq); +} + +/** + * HTTP CONNECT proxy requests. + */ +async function onconnect( + this: ProxyServer, + req: http.IncomingMessage, + socket: net.Socket, + head: Buffer +) { + debug.request('%s %s HTTP/%s ', req.method, req.url, req.httpVersion); + assert( + !head || 0 == head.length, + '"head" should be empty for proxy requests' + ); + + let res: http.ServerResponse | null; + let gotResponse = false; + + // define request socket event listeners + socket.on('close', function onclientclose() { + debug.request('HTTP request %s socket "close" event', req.url); + }); + + socket.on('end', function onclientend() { + debug.request('HTTP request %s socket "end" event', req.url); + }); + + socket.on('error', function onclienterror(err) { + debug.request( + 'HTTP request %s socket "error" event:\n%s', + req.url, + err.stack || err + ); + }); + + // define target socket event listeners + function ontargetclose() { + debug.proxyResponse('proxy target %s "close" event', req.url); + socket.destroy(); + } + + function ontargetend() { + debug.proxyResponse('proxy target %s "end" event', req.url); + } + + function ontargeterror(err: NodeJS.ErrnoException) { + debug.proxyResponse( + 'proxy target %s "error" event:\n%s', + req.url, + err.stack || err + ); + if (gotResponse) { + debug.response( + 'already sent a response, just destroying the socket...' + ); + socket.destroy(); + } else if (err.code === 'ENOTFOUND') { + debug.response('HTTP/1.1 404 Not Found'); + if (res) { + res.writeHead(404); + res.end(); + } + } else { + debug.response('HTTP/1.1 500 Internal Server Error'); + if (res) { + res.writeHead(500); + res.end(); + } + } + } + + function ontargetconnect() { + debug.proxyResponse('proxy target %s "connect" event', req.url); + debug.response('HTTP/1.1 200 Connection established'); + gotResponse = true; + + if (res) { + res.removeListener('finish', onfinish); + + res.writeHead(200, 'Connection established'); + res.flushHeaders(); + + // relinquish control of the `socket` from the ServerResponse instance + res.detachSocket(socket); + + // nullify the ServerResponse object, so that it can be cleaned + // up before this socket proxying is completed + res = null; + } + + socket.pipe(target); + target.pipe(socket); + } + + // create the `res` instance for this request since Node.js + // doesn't provide us with one :( + res = new http.ServerResponse(req); + res.shouldKeepAlive = false; + res.chunkedEncoding = false; + res.useChunkedEncodingByDefault = false; + res.assignSocket(socket); + + // called for the ServerResponse's "finish" event + // XXX: normally, node's "http" module has a "finish" event listener that would + // take care of closing the socket once the HTTP response has completed, but + // since we're making this ServerResponse instance manually, that event handler + // never gets hooked up, so we must manually close the socket... + function onfinish() { + debug.response('response "finish" event'); + if (res) { + res.detachSocket(socket); + } + socket.end(); + } + res.once('finish', onfinish); + + // pause the socket during authentication so no data is lost + socket.pause(); + + try { + const success = await authenticate(this, req); + if (!success) return requestAuthorization(req, res); + } catch (_err) { + const err = _err as Error; + // an error occured during login! + res.writeHead(500); + res.end((err.stack || err.message || err) + '\n'); + return; + } + + socket.resume(); + + if (!req.url) { + throw new TypeError('No "url" provided'); + } + + // `req.url` should look like "example.com:443" + const lastColon = req.url.lastIndexOf(':'); + const host = req.url.substring(0, lastColon); + const port = parseInt(req.url.substring(lastColon + 1), 10); + const opts = { host: host.replace(/^\[|\]$/g, ""), port }; + + debug.proxyRequest('connecting to proxy target %o', opts); + const target = net.connect(opts); + target.on('connect', ontargetconnect); + target.on('close', ontargetclose); + target.on('error', ontargeterror); + target.on('end', ontargetend); +} + +/** + * Checks `Proxy-Authorization` request headers. Same logic applied to CONNECT + * requests as well as regular HTTP requests. + */ +async function authenticate(server: ProxyServer, req: http.IncomingMessage) { + if (typeof server.authenticate === 'function') { + debug.request('authenticating request "%s %s"', req.method, req.url); + return server.authenticate(req); + } + // no `server.authenticate()` function, so just allow the request + return true; +} + +/** + * Sends a "407 Proxy Authentication Required" HTTP response to the `socket`. + */ +function requestAuthorization( + req: http.IncomingMessage, + res: http.ServerResponse +) { + // request Basic proxy authorization + debug.response( + 'requesting proxy authorization for "%s %s"', + req.method, + req.url + ); + + // TODO: make "realm" and "type" (Basic) be configurable... + const realm = 'proxy'; + + const headers = { + 'Proxy-Authenticate': 'Basic realm="' + realm + '"', + }; + res.writeHead(407, headers); + res.end(); +} diff --git a/packages/proxy/test/test.js b/packages/proxy/test/test.js new file mode 100644 index 00000000..c2e0ae9e --- /dev/null +++ b/packages/proxy/test/test.js @@ -0,0 +1,196 @@ +/** + * Module dependencies. + */ + +const fs = require('fs'); +const net = require('net'); +const path = require('path'); +const http = require('http'); +const https = require('https'); +const assert = require('assert'); +const { createProxy } = require('../'); + +describe('proxy', () => { + var proxy; + var proxyPort; + + var server; + var serverPort; + + before(function (done) { + // setup proxy server + proxy = createProxy(http.createServer()); + proxy.listen(() => { + proxyPort = proxy.address().port; + done(); + }); + }); + + before(function (done) { + // setup target server + server = http.createServer(); + server.listen(() => { + serverPort = server.address().port; + done(); + }); + }); + + after(function (done) { + proxy.once('close', () => { + done(); + }); + proxy.close(); + }); + + after(function (done) { + server.once('close', () => { + done(); + }); + server.close(); + }); + + it('should proxy HTTP GET requests', function (done) { + var gotData = false; + var gotRequest = false; + var host = '127.0.0.1:' + serverPort; + server.once('request', function (req, res) { + gotRequest = true; + // ensure headers are being proxied + assert(req.headers['user-agent'] == 'curl/7.30.0'); + assert(req.headers.host == host); + assert(req.headers.accept == '*/*'); + res.end(); + }); + + var socket = net.connect({ port: proxyPort }); + socket.once('close', () => { + assert(gotData); + assert(gotRequest); + done(); + }); + socket.once('connect', () => { + socket.write( + 'GET http://' + + host + + '/ HTTP/1.1\r\n' + + 'User-Agent: curl/7.30.0\r\n' + + 'Host: ' + + host + + '\r\n' + + 'Accept: */*\r\n' + + 'Proxy-Connection: Keep-Alive\r\n' + + '\r\n' + ); + }); + socket.setEncoding('utf8'); + socket.once('data', function (data) { + assert(0 == data.indexOf('HTTP/1.1 200 OK\r\n')); + gotData = true; + socket.destroy(); + }); + }); + + it('should establish connection for CONNECT requests', function (done) { + var gotData = false; + var socket = net.connect({ port: proxyPort }); + socket.once('close', () => { + assert(gotData); + done(); + }); + socket.once('connect', () => { + var host = '127.0.0.1:' + serverPort; + socket.write( + 'CONNECT ' + + host + + ' HTTP/1.1\r\n' + + 'Host: ' + + host + + '\r\n' + + 'User-Agent: curl/7.30.0\r\n' + + 'Proxy-Connection: Keep-Alive\r\n' + + '\r\n' + ); + }); + socket.setEncoding('utf8'); + socket.once('data', function (data) { + assert( + 0 == data.indexOf('HTTP/1.1 200 Connection established\r\n') + ); + gotData = true; + socket.destroy(); + }); + }); + + describe('authentication', () => { + function clearAuth() { + delete proxy.authenticate; + } + + before(clearAuth); + after(clearAuth); + + it('should invoke the `server.authenticate()` function when set', function (done) { + var auth = 'Basic Zm9vOmJhcg=='; + var called = false; + proxy.authenticate = (req) => { + assert(auth === req.headers['proxy-authorization']); + socket.destroy(); + called = true; + }; + var socket = net.connect({ port: proxyPort }); + socket.once('close', () => { + assert(called); + done(); + }); + socket.once('connect', () => { + socket.write( + 'GET / HTTP/1.1\r\n' + + 'Host: foo.com\r\n' + + 'Proxy-Authorization: ' + + auth + + '\r\n' + + '\r\n' + ); + }); + }); + + it('should provide the HTTP client with a 407 response status code', function (done) { + // reject everything + proxy.authenticate = () => false; + var gotData = false; + var socket = net.connect({ port: proxyPort }); + socket.once('close', () => { + assert(gotData); + done(); + }); + socket.once('connect', () => { + socket.write('GET / HTTP/1.1\r\nHost: foo.com\r\n\r\n'); + }); + socket.setEncoding('utf8'); + socket.once('data', function (data) { + assert(0 == data.indexOf('HTTP/1.1 407')); + gotData = true; + socket.destroy(); + }); + }); + + it("should close the socket after a CONNECT request's 407 response status code", function (done) { + // reject everything + proxy.authenticate = () => false; + var gotData = false; + var socket = net.connect({ port: proxyPort }); + socket.once('close', () => { + assert(gotData); + done(); + }); + socket.once('connect', () => { + socket.write('CONNECT 127.0.0.1:80 HTTP/1.1\r\n\r\n'); + }); + socket.setEncoding('utf8'); + socket.once('data', function (data) { + assert(0 == data.indexOf('HTTP/1.1 407')); + gotData = true; + }); + }); + }); +}); diff --git a/packages/proxy/tsconfig.json b/packages/proxy/tsconfig.json new file mode 100644 index 00000000..24b6cf7e --- /dev/null +++ b/packages/proxy/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/socks-proxy-agent/README.md b/packages/socks-proxy-agent/README.md new file mode 100644 index 00000000..3d4375c4 --- /dev/null +++ b/packages/socks-proxy-agent/README.md @@ -0,0 +1,152 @@ +socks-proxy-agent +================ +### A SOCKS proxy `http.Agent` implementation for HTTP and HTTPS +[![Build Status](https://github.com/TooTallNate/node-socks-proxy-agent/workflows/Node%20CI/badge.svg)](https://github.com/TooTallNate/node-socks-proxy-agent/actions?workflow=Node+CI) + +This module provides an `http.Agent` implementation that connects to a +specified SOCKS proxy server, and can be used with the built-in `http` +and `https` modules. + +It can also be used in conjunction with the `ws` module to establish a WebSocket +connection over a SOCKS proxy. See the "Examples" section below. + +Installation +------------ + +Install with `npm`: + +``` bash +npm install socks-proxy-agent +``` + + +Examples +-------- + +#### TypeScript example + +```ts +import https from 'https'; +import { SocksProxyAgent } from 'socks-proxy-agent'; + +const info = { + hostname: 'br41.nordvpn.com', + userId: 'your-name@gmail.com', + password: 'abcdef12345124' +}; +const agent = new SocksProxyAgent(info); + +https.get('https://ipinfo.io', { agent }, (res) => { + console.log(res.headers); + res.pipe(process.stdout); +}); +``` + +#### `http` module example + +```js +var url = require('url'); +var http = require('http'); +var { SocksProxyAgent } = require('socks-proxy-agent'); + +// SOCKS proxy to connect to +var proxy = process.env.socks_proxy || 'socks://127.0.0.1:1080'; +console.log('using proxy server %j', proxy); + +// HTTP endpoint for the proxy to connect to +var endpoint = process.argv[2] || 'http://nodejs.org/api/'; +console.log('attempting to GET %j', endpoint); +var opts = url.parse(endpoint); + +// create an instance of the `SocksProxyAgent` class with the proxy server information +var agent = new SocksProxyAgent(proxy); +opts.agent = agent; + +http.get(opts, function (res) { + console.log('"response" event!', res.headers); + res.pipe(process.stdout); +}); +``` + +#### `https` module example + +```js +var url = require('url'); +var https = require('https'); +var { SocksProxyAgent } = require('socks-proxy-agent'); + +// SOCKS proxy to connect to +var proxy = process.env.socks_proxy || 'socks://127.0.0.1:1080'; +console.log('using proxy server %j', proxy); + +// HTTP endpoint for the proxy to connect to +var endpoint = process.argv[2] || 'https://encrypted.google.com/'; +console.log('attempting to GET %j', endpoint); +var opts = url.parse(endpoint); + +// create an instance of the `SocksProxyAgent` class with the proxy server information +var agent = new SocksProxyAgent(proxy); +opts.agent = agent; + +https.get(opts, function (res) { + console.log('"response" event!', res.headers); + res.pipe(process.stdout); +}); +``` + +#### `ws` WebSocket connection example + +``` js +var WebSocket = require('ws'); +var { SocksProxyAgent } = require('socks-proxy-agent'); + +// SOCKS proxy to connect to +var proxy = process.env.socks_proxy || 'socks://127.0.0.1:1080'; +console.log('using proxy server %j', proxy); + +// WebSocket endpoint for the proxy to connect to +var endpoint = process.argv[2] || 'ws://echo.websocket.events'; +console.log('attempting to connect to WebSocket %j', endpoint); + +// create an instance of the `SocksProxyAgent` class with the proxy server information +var agent = new SocksProxyAgent(proxy); + +// initiate the WebSocket connection +var socket = new WebSocket(endpoint, { agent: agent }); + +socket.on('open', function () { + console.log('"open" event!'); + socket.send('hello world'); +}); + +socket.on('message', function (data, flags) { + console.log('"message" event! %j %j', data, flags); + socket.close(); +}); +``` + +License +------- + +(The MIT License) + +Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/socks-proxy-agent/package.json b/packages/socks-proxy-agent/package.json new file mode 100644 index 00000000..098b1207 --- /dev/null +++ b/packages/socks-proxy-agent/package.json @@ -0,0 +1,136 @@ +{ + "name": "socks-proxy-agent", + "version": "7.0.0", + "description": "A SOCKS proxy `http.Agent` implementation for HTTP and HTTPS", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "author": { + "email": "nathan@tootallnate.net", + "name": "Nathan Rajlich", + "url": "http://n8.io/" + }, + "contributors": [ + { + "name": "Kiko Beats", + "email": "josefrancisco.verdu@gmail.com" + }, + { + "name": "Josh Glazebrook", + "email": "josh@joshglazebrook.com" + }, + { + "name": "talmobi", + "email": "talmobi@users.noreply.github.com" + }, + { + "name": "Indospace.io", + "email": "justin@indospace.io" + }, + { + "name": "Kilian von Pflugk", + "email": "github@jumoog.io" + }, + { + "name": "Kyle", + "email": "admin@hk1229.cn" + }, + { + "name": "Matheus Fernandes", + "email": "matheus.frndes@gmail.com" + }, + { + "name": "Ricky Miller", + "email": "richardkazuomiller@gmail.com" + }, + { + "name": "Shantanu Sharma", + "email": "shantanu34@outlook.com" + }, + { + "name": "Tim Perry", + "email": "pimterry@gmail.com" + }, + { + "name": "Vadim Baryshev", + "email": "vadimbaryshev@gmail.com" + }, + { + "name": "jigu", + "email": "luo1257857309@gmail.com" + }, + { + "name": "Alba Mendez", + "email": "me@jmendeth.com" + }, + { + "name": "Дмитрий Гуденков", + "email": "Dimangud@rambler.ru" + }, + { + "name": "Andrei Bitca", + "email": "63638922+andrei-bitca-dc@users.noreply.github.com" + }, + { + "name": "Andrew Casey", + "email": "amcasey@users.noreply.github.com" + }, + { + "name": "Brandon Ros", + "email": "brandonros1@gmail.com" + }, + { + "name": "Dang Duy Thanh", + "email": "thanhdd.it@gmail.com" + }, + { + "name": "Dimitar Nestorov", + "email": "8790386+dimitarnestorov@users.noreply.github.com" + } + ], + "repository": { + "type": "git", + "url": "git://github.com/TooTallNate/node-socks-proxy-agent.git" + }, + "bugs": { + "url": "https://github.com/TooTallNate/node-socks-proxy-agent/issues" + }, + "keywords": [ + "agent", + "http", + "https", + "proxy", + "socks", + "socks4", + "socks4a", + "socks5", + "socks5h" + ], + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.4", + "socks": "^2.7.1" + }, + "devDependencies": { + "@types/debug": "^4.1.7", + "@types/node": "^14.18.43", + "cacheable-lookup": "^6.1.0", + "dns2": "^2.1.0", + "mocha": "^9.2.2", + "socksv5": "github:TooTallNate/socksv5#fix/dstSock-close-event", + "tsconfig": "workspace:*", + "typescript": "^5.0.4" + }, + "engines": { + "node": ">= 14" + }, + "scripts": { + "build": "tsc", + "test": "mocha --reporter spec", + "lint": "eslint . --ext .ts", + "prepublishOnly": "npm run build" + }, + "license": "MIT" +} diff --git a/packages/socks-proxy-agent/src/index.ts b/packages/socks-proxy-agent/src/index.ts new file mode 100644 index 00000000..5e4fddd9 --- /dev/null +++ b/packages/socks-proxy-agent/src/index.ts @@ -0,0 +1,208 @@ +import { SocksClient, SocksProxy, SocksClientOptions } from 'socks'; +import { Agent, AgentConnectOpts } from 'agent-base'; +import createDebug from 'debug'; +import * as dns from 'dns'; +import * as net from 'net'; +import * as tls from 'tls'; +import * as http from 'http'; + +interface BaseSocksProxyAgentOptions { + tls?: tls.ConnectionOptions | null; +} + +interface SocksProxyAgentOptionsExtra { + timeout?: number; +} + +const debug = createDebug('socks-proxy-agent'); + +function parseSocksURL(url: URL): { lookup: boolean; proxy: SocksProxy } { + let lookup = false; + let type: SocksProxy['type'] = 5; + const host = url.hostname; + + // From RFC 1928, Section 3: https://tools.ietf.org/html/rfc1928#section-3 + // "The SOCKS service is conventionally located on TCP port 1080" + const port = parseInt(url.port, 10) || 1080; + + // figure out if we want socks v4 or v5, based on the "protocol" used. + // Defaults to 5. + switch (url.protocol.replace(':', '')) { + case 'socks4': + lookup = true; + type = 4; + break; + // pass through + case 'socks4a': + type = 4; + break; + case 'socks5': + lookup = true; + type = 5; + break; + // pass through + case 'socks': // no version specified, default to 5h + type = 5; + break; + case 'socks5h': + type = 5; + break; + default: + throw new TypeError( + `A "socks" protocol must be specified! Got: ${String( + url.protocol + )}` + ); + } + + const proxy: SocksProxy = { + host, + port, + type, + }; + + if (url.username) { + Object.defineProperty(proxy, 'userId', { + value: decodeURIComponent(url.username), + enumerable: false, + }); + } + + if (url.password != null) { + Object.defineProperty(proxy, 'password', { + value: decodeURIComponent(url.password), + enumerable: false, + }); + } + + return { lookup, proxy }; +} + +export interface SocksProxyAgentOptions + extends BaseSocksProxyAgentOptions, + Partial> {} + +export class SocksProxyAgent extends Agent { + static protocols = [ + "socks", + "socks4", + "socks4a", + "socks5", + "socks5h", + ] as const; + + private readonly shouldLookup: boolean; + private readonly proxy: SocksProxy; + private readonly tlsConnectionOptions: tls.ConnectionOptions; + public timeout: number | null; + + constructor(uri: string | URL, opts?: SocksProxyAgentOptionsExtra) { + super(); + + const url = typeof uri === "string" ? new URL(uri) : uri; + const { proxy, lookup } = parseSocksURL(url); + + this.shouldLookup = lookup; + this.proxy = proxy; + //this.tlsConnectionOptions = proxyOptions.tls != null ? proxyOptions.tls : {} + this.tlsConnectionOptions = {}; + this.timeout = opts?.timeout ?? null; + } + + /** + * Initiates a SOCKS connection to the specified SOCKS proxy server, + * which in turn connects to the specified remote host and port. + */ + async connect( + req: http.ClientRequest, + opts: AgentConnectOpts + ): Promise { + const { shouldLookup, proxy, timeout } = this; + + let { host } = opts; + const { port, lookup: lookupFn = dns.lookup } = opts; + + if (!host) { + throw new Error("No `host` defined!"); + } + + if (shouldLookup) { + // Client-side DNS resolution for "4" and "5" socks proxy versions. + host = await new Promise((resolve, reject) => { + // Use the request's custom lookup, if one was configured: + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + lookupFn(host!, {}, (err, res) => { + if (err) { + reject(err); + } else { + resolve(res); + } + }); + }); + } + + const socksOpts: SocksClientOptions = { + proxy, + destination: { + host, + port: typeof port === "number" ? port : parseInt(port, 10), + }, + command: "connect", + timeout: timeout ?? undefined, + }; + + const cleanup = (tlsSocket?: tls.TLSSocket) => { + req.destroy(); + socket.destroy(); + if (tlsSocket) tlsSocket.destroy(); + }; + + debug("Creating socks proxy connection: %o", socksOpts); + const { socket } = await SocksClient.createConnection(socksOpts); + debug("Successfully created socks proxy connection"); + + if (timeout !== null) { + socket.setTimeout(timeout); + socket.on("timeout", () => cleanup()); + } + + if (opts.secureEndpoint) { + // The proxy is connecting to a TLS server, so upgrade + // this socket connection to a TLS connection. + debug("Upgrading socket connection to TLS"); + const servername = opts.servername ?? opts.host; + + const tlsSocket = tls.connect({ + ...omit(opts, "host", "path", "port"), + socket, + servername, + ...this.tlsConnectionOptions, + }); + + tlsSocket.once("error", (error) => { + debug("Socket TLS error", error.message); + cleanup(tlsSocket); + }); + + return tlsSocket; + } + + return socket; + } +} + +function omit]>( + obj: T, + ...keys: K +): { + [K2 in Exclude]: T[K2]; +} { + const ret = {} as { [K in keyof typeof obj]: (typeof obj)[K] }; + let key: keyof typeof obj; + for (key in obj) { + if (!keys.includes(key)) { + ret[key] = obj[key]; + } + } + return ret; +} diff --git a/packages/socks-proxy-agent/test/ssl-cert-snakeoil.key b/packages/socks-proxy-agent/test/ssl-cert-snakeoil.key new file mode 100644 index 00000000..fd125012 --- /dev/null +++ b/packages/socks-proxy-agent/test/ssl-cert-snakeoil.key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCzURxIqzer0ACAbX/lHdsn4Gd9PLKrf7EeDYfIdV0HZKPD8WDr +bBx2/fBu0OW2sjnzv/SVZbJ0DAuPE/p0+eT0qb2qC10iz9iTD7ribd7gxhirVb8y +b3fBjXsxc8V8p4Ny1LcvNSqCjwUbJqdRogfoJeTiqPM58z5sNzuv5iq7iwIDAQAB +AoGAPMQy4olrP0UotlzlJ36bowLP70ffgHCwU+/f4NWs5fF78c3du0oSx1w820Dd +Z7E0JF8bgnlJJTxjumPZz0RUCugrEHBKJmzEz3cxF5E3+7NvteZcjKn9D67RrM5x +1/uSZ9cqKE9cYvY4fSuHx18diyZ4axR/wB1Pea2utjjDM+ECQQDb9ZbmmaWMiRpQ +5Up+loxP7BZNPsEVsm+DVJmEFbaFgGfncWBqSIqnPNjMwTwj0OigTwCAEGPkfRVW +T0pbYWCxAkEA0LK7SCTwzyDmhASUalk0x+3uCAA6ryFdwJf/wd8TRAvVOmkTEldX +uJ7ldLvfrONYO3v56uKTU/SoNdZYzKtO+wJAX2KM4ctXYy5BXztPpr2acz4qHa1N +Bh+vBAC34fOYhyQ76r3b1btHhWZ5jbFuZwm9F2erC94Ps5IaoqcX07DSwQJAPKGw +h2U0EPkd/3zVIZCJJQya+vgWFIs9EZcXVtvYXQyTBkVApTN66MhBIYjzkub5205J +bVQmOV37AKklY1DhwQJAA1wos0cYxro02edzatxd0DIR2r4qqOqLkw6BhYHhq6HJ +ZvIcQkHqdSXzdETFc01I1znDGGIrJHcnvKWgBPoEUg== +-----END RSA PRIVATE KEY----- diff --git a/packages/socks-proxy-agent/test/ssl-cert-snakeoil.pem b/packages/socks-proxy-agent/test/ssl-cert-snakeoil.pem new file mode 100644 index 00000000..b115a5e9 --- /dev/null +++ b/packages/socks-proxy-agent/test/ssl-cert-snakeoil.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIB1TCCAT4CCQDV5mPlzm9+izANBgkqhkiG9w0BAQUFADAvMS0wKwYDVQQDEyQ3 +NTI3YmQ3Ny1hYjNlLTQ3NGItYWNlNy1lZWQ2MDUzOTMxZTcwHhcNMTUwNzA2MjI0 +NTA3WhcNMjUwNzAzMjI0NTA3WjAvMS0wKwYDVQQDEyQ3NTI3YmQ3Ny1hYjNlLTQ3 +NGItYWNlNy1lZWQ2MDUzOTMxZTcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +ALNRHEirN6vQAIBtf+Ud2yfgZ308sqt/sR4Nh8h1XQdko8PxYOtsHHb98G7Q5bay +OfO/9JVlsnQMC48T+nT55PSpvaoLXSLP2JMPuuJt3uDGGKtVvzJvd8GNezFzxXyn +g3LUty81KoKPBRsmp1GiB+gl5OKo8znzPmw3O6/mKruLAgMBAAEwDQYJKoZIhvcN +AQEFBQADgYEACzoHUF8UV2Z6541Q2wKEA0UFUzmUjf/E1XwBO+1P15ZZ64uw34B4 +1RwMPtAo9RY/PmICTWtNxWGxkzwb2JtDWtnxVER/lF8k2XcXPE76fxTHJF/BKk9J +QU8OTD1dd9gHCBviQB9TqntRZ5X7axjtuWjb2umY+owBYzAHZkp1HKI= +-----END CERTIFICATE----- diff --git a/packages/socks-proxy-agent/test/test.js b/packages/socks-proxy-agent/test/test.js new file mode 100644 index 00000000..c0144404 --- /dev/null +++ b/packages/socks-proxy-agent/test/test.js @@ -0,0 +1,278 @@ +/* global describe, before, after, it */ + +const socks = require('socksv5'); +const assert = require('assert'); +const https = require('https'); +const http = require('http'); +const url = require('url'); +const path = require('path'); +const fs = require('fs'); + +const dns2 = require('dns2'); +const CacheableLookup = require('cacheable-lookup'); + +const { req, json } = require('agent-base'); +const { SocksProxyAgent } = require('..'); + +describe('SocksProxyAgent', () => { + let httpServer; + let httpPort; + let httpsServer; + let httpsPort; + let socksServer; + let socksPort; + + before(function (done) { + // setup SOCKS proxy server + socksServer = socks.createServer(function (_info, accept) { + accept(); + }); + socksServer.listen(0, '127.0.0.1', () => { + socksPort = socksServer.address().port; + done(); + }); + socksServer.useAuth(socks.auth.None()); + }); + + before(function (done) { + // setup target HTTP server + httpServer = http.createServer(); + httpServer.listen(() => { + httpPort = httpServer.address().port; + done(); + }); + }); + + before(function (done) { + // setup target SSL HTTPS server + const options = { + key: fs.readFileSync( + path.resolve(__dirname, 'ssl-cert-snakeoil.key') + ), + cert: fs.readFileSync( + path.resolve(__dirname, 'ssl-cert-snakeoil.pem') + ), + }; + httpsServer = https.createServer(options); + httpsServer.listen(() => { + httpsPort = httpsServer.address().port; + done(); + }); + }); + + after(function (done) { + socksServer.once('close', () => { + done(); + }); + socksServer.close(); + }); + + after(function (done) { + httpServer.once('close', () => { + done(); + }); + httpServer.close(); + }); + + after(function (done) { + httpsServer.once('close', () => { + done(); + }); + httpsServer.close(); + }); + + describe('constructor', () => { + it('should throw an Error if no "proxy" argument is given', () => { + assert.throws(() => new SocksProxyAgent()); + }); + it('should accept a "string" proxy argument', () => { + const agent = new SocksProxyAgent(`socks://127.0.0.1:${socksPort}`); + assert.equal('127.0.0.1', agent.proxy.host); + assert.equal(socksPort, agent.proxy.port); + }); + it('should accept a `new URL()` result object argument', () => { + const opts = new URL(`socks://127.0.0.1:${socksPort}`); + const agent = new SocksProxyAgent(opts); + assert.equal('127.0.0.1', agent.proxy.host); + assert.equal(socksPort, agent.proxy.port); + }); + it('setup timeout', function (done) { + httpServer.once('request', function (req, res) { + assert.equal('/timeout', req.url); + res.statusCode = 200; + setTimeout(() => res.end('Written after 1000'), 500); + }); + + const agent = new SocksProxyAgent( + `socks://127.0.0.1:${socksPort}`, + { timeout: 50 } + ); + + const opts = { + protocol: 'http:', + host: `127.0.0.1:${httpPort}`, + port: httpPort, + hostname: '127.0.0.1', + path: '/timeout', + agent, + headers: { foo: 'bar' }, + }; + + const req = http.get(opts); + + req.once('error', (err) => { + assert.equal(err.message, 'socket hang up'); + done(); + }); + }); + }); + + describe('"http" module', () => { + it('should work against an HTTP endpoint', async () => { + httpServer.once('request', function (req, res) { + assert.equal('/foo', req.url); + res.statusCode = 404; + res.end(JSON.stringify(req.headers)); + }); + + const res = await req(`http://127.0.0.1:${httpPort}/foo`, { + agent: new SocksProxyAgent(`socks://127.0.0.1:${socksPort}`), + headers: { foo: 'bar' }, + }); + assert.equal(404, res.statusCode); + + const body = await json(res); + assert.equal('bar', body.foo); + }); + }); + + describe('"https" module', () => { + it('should work against an HTTPS endpoint', async () => { + httpsServer.once('request', function (req, res) { + assert.equal('/foo', req.url); + res.statusCode = 404; + res.end(JSON.stringify(req.headers)); + }); + + const agent = new SocksProxyAgent(`socks://127.0.0.1:${socksPort}`); + + const res = await req(`https://127.0.0.1:${httpsPort}/foo`, { + agent, + rejectUnauthorized: false, + headers: { foo: 'bar' }, + }); + assert.equal(404, res.statusCode); + + const body = await json(res); + assert.equal('bar', body.foo); + }); + }); + + describe('Custom lookup option', () => { + let dnsServer; + let dnsQueries; + + before((done) => { + dnsQueries = []; + + // A custom DNS server that always replies with 127.0.0.1: + dnsServer = dns2.createServer({ + udp: true, + handle: (request, send) => { + const response = + dns2.Packet.createResponseFromRequest(request); + const [question] = request.questions; + const { name } = question; + + dnsQueries.push({ + type: question.type, + name: question.name, + }); + + response.answers.push({ + name, + type: dns2.Packet.TYPE.A, + class: dns2.Packet.CLASS.IN, + ttl: 300, + address: '127.0.0.1', + }); + send(response); + }, + }); + dnsServer.listen({ udp: 5333 }); + dnsServer.on('listening', () => done()); + }); + + after(() => { + dnsServer.close(); + }); + + it("should use a requests's custom lookup function with socks5", function (done) { + httpServer.once('request', function (req, res) { + assert.equal('/foo', req.url); + res.statusCode = 404; + res.end(); + }); + + let agent = new SocksProxyAgent(`socks5://127.0.0.1:${socksPort}`); + let opts = url.parse( + `http://non-existent-domain.test:${httpPort}/foo` + ); + opts.agent = agent; + + opts.lookup = (hostname, _opts, callback) => { + if (hostname === 'non-existent-domain.test') + callback(null, '127.0.0.1'); + else callback(new Error('Bad domain')); + }; + + let req = http.get(opts, function (res) { + assert.equal(404, res.statusCode); + done(); + }); + req.once('error', done); + }); + + it('should support caching DNS requests', function (done) { + httpServer.on('request', function (req, res) { + res.statusCode = 200; + res.end(); + }); + + let agent = new SocksProxyAgent(`socks5://127.0.0.1:${socksPort}`); + let opts = url.parse(`http://test-domain.test:${httpPort}/foo`); + opts.agent = agent; + + const cacheableLookup = new CacheableLookup(); + cacheableLookup.servers = ['127.0.0.1:5333']; + opts.lookup = cacheableLookup.lookup; + + // No DNS queries made initially + assert.deepEqual(dnsQueries, []); + + http.get(opts, function (res) { + assert.equal(200, res.statusCode); + + // Initial DNS query for first request + assert.deepEqual(dnsQueries, [ + { name: 'test-domain.test', type: dns2.Packet.TYPE.A }, + { name: 'test-domain.test', type: dns2.Packet.TYPE.AAAA }, + ]); + + http.get(opts, function (res) { + assert.equal(200, res.statusCode); + + // Still the same. No new DNS queries, so the response was cached + assert.deepEqual(dnsQueries, [ + { name: 'test-domain.test', type: dns2.Packet.TYPE.A }, + { + name: 'test-domain.test', + type: dns2.Packet.TYPE.AAAA, + }, + ]); + done(); + }).once('error', done); + }).once('error', done); + }); + }); +}); diff --git a/packages/socks-proxy-agent/tsconfig.json b/packages/socks-proxy-agent/tsconfig.json new file mode 100644 index 00000000..24b6cf7e --- /dev/null +++ b/packages/socks-proxy-agent/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/packages/tsconfig/base.json b/packages/tsconfig/base.json new file mode 100644 index 00000000..b28bb607 --- /dev/null +++ b/packages/tsconfig/base.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "composite": false, + "declaration": true, + "declarationMap": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "sourceMap": true, + "inlineSources": false, + "isolatedModules": true, + "moduleResolution": "node", + "noUnusedLocals": false, + "noUnusedParameters": false, + "preserveWatchOutput": true, + "skipLibCheck": true, + "module": "CommonJS", + "target": "ES2020", + "lib": ["ESNext"], + "strict": true + } +} \ No newline at end of file diff --git a/packages/tsconfig/package.json b/packages/tsconfig/package.json new file mode 100644 index 00000000..70219b6f --- /dev/null +++ b/packages/tsconfig/package.json @@ -0,0 +1,6 @@ +{ + "name": "tsconfig", + "version": "0.0.0", + "private": true, + "license": "MIT" +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..27120aba --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,5152 @@ +lockfileVersion: '6.0' + +importers: + + .: + devDependencies: + '@typescript-eslint/eslint-plugin': + specifier: ^5.59.1 + version: 5.59.1(@typescript-eslint/parser@5.59.1)(eslint@7.32.0)(typescript@5.0.4) + '@typescript-eslint/parser': + specifier: ^5.59.1 + version: 5.59.1(eslint@7.32.0)(typescript@5.0.4) + eslint: + specifier: ^7.32.0 + version: 7.32.0 + eslint-config-prettier: + specifier: ^8.8.0 + version: 8.8.0(eslint@7.32.0) + eslint-config-turbo: + specifier: ^1.9.3 + version: 1.9.3(eslint@7.32.0) + prettier: + specifier: ^2.5.1 + version: 2.8.0 + turbo: + specifier: latest + version: 1.9.3 + + packages/agent-base: + dependencies: + debug: + specifier: ^4.3.4 + version: 4.3.4 + devDependencies: + '@types/debug': + specifier: ^4.1.7 + version: 4.1.7 + '@types/jest': + specifier: ^29.5.1 + version: 29.5.1 + '@types/node': + specifier: ^14.18.43 + version: 14.18.43 + '@types/semver': + specifier: ^7.3.13 + version: 7.3.13 + '@types/ws': + specifier: ^6.0.4 + version: 6.0.4 + async-listen: + specifier: ^2.1.0 + version: 2.1.0 + jest: + specifier: ^29.5.0 + version: 29.5.0(@types/node@14.18.43) + ts-jest: + specifier: ^29.1.0 + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: ^5.0.4 + version: 5.0.4 + ws: + specifier: ^3.3.3 + version: 3.3.3 + + packages/data-uri-to-buffer: + devDependencies: + '@types/jest': + specifier: ^27.0.2 + version: 27.0.2 + '@types/node': + specifier: ^14.18.43 + version: 14.18.43 + jest: + specifier: ^29.5.0 + version: 29.5.0(@types/node@14.18.43) + ts-jest: + specifier: ^29.1.0 + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: ^5.0.4 + version: 5.0.4 + + packages/degenerator: + dependencies: + ast-types: + specifier: ^0.13.2 + version: 0.13.4 + escodegen: + specifier: ^1.8.1 + version: 1.14.3 + esprima: + specifier: ^4.0.0 + version: 4.0.1 + vm2: + specifier: ^3.9.17 + version: 3.9.17 + devDependencies: + '@types/escodegen': + specifier: ^0.0.6 + version: 0.0.6 + '@types/esprima': + specifier: ^4.0.2 + version: 4.0.2 + '@types/jest': + specifier: ^29.5.1 + version: 29.5.1 + '@types/node': + specifier: ^14.18.43 + version: 14.18.43 + jest: + specifier: ^29.5.0 + version: 29.5.0(@types/node@14.18.43) + ts-jest: + specifier: ^29.1.0 + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: ^5.0.4 + version: 5.0.4 + + packages/get-uri: + dependencies: + '@tootallnate/once': + specifier: ^2.0.0 + version: 2.0.0 + basic-ftp: + specifier: ^5.0.2 + version: 5.0.2 + data-uri-to-buffer: + specifier: ^4.0.1 + version: link:../data-uri-to-buffer + debug: + specifier: ^4.3.4 + version: 4.3.4 + fs-extra: + specifier: ^8.1.0 + version: 8.1.0 + devDependencies: + '@types/debug': + specifier: ^4.1.7 + version: 4.1.7 + '@types/fs-extra': + specifier: ^8.1.2 + version: 8.1.2 + '@types/ftpd': + specifier: ^0.2.35 + version: 0.2.35 + '@types/jest': + specifier: ^29.5.1 + version: 29.5.1 + '@types/node': + specifier: ^14.18.43 + version: 14.18.43 + async-listen: + specifier: ^2.1.0 + version: 2.1.0 + ftpd: + specifier: https://files-jg1s1zt9l.n8.io/ftpd-v0.2.14.tgz + version: '@files-jg1s1zt9l.n8.io/ftpd-v0.2.14.tgz' + jest: + specifier: ^29.5.0 + version: 29.5.0(@types/node@14.18.43) + st: + specifier: ^1.2.2 + version: 1.2.2 + ts-jest: + specifier: ^29.1.0 + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: ^5.0.4 + version: 5.0.4 + + packages/http-proxy-agent: + dependencies: + '@tootallnate/once': + specifier: ^2.0.0 + version: 2.0.0 + agent-base: + specifier: ^6.0.2 + version: link:../agent-base + debug: + specifier: ^4.3.4 + version: 4.3.4 + devDependencies: + '@types/debug': + specifier: ^4.1.7 + version: 4.1.7 + '@types/node': + specifier: ^14.18.43 + version: 14.18.43 + mocha: + specifier: ^6.2.3 + version: 6.2.3 + proxy: + specifier: workspace:* + version: link:../proxy + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: ^5.0.4 + version: 5.0.4 + + packages/https-proxy-agent: + dependencies: + agent-base: + specifier: ^6.0.2 + version: link:../agent-base + debug: + specifier: '4' + version: 4.3.4 + devDependencies: + '@types/debug': + specifier: '4' + version: 4.1.0 + '@types/jest': + specifier: ^29.5.1 + version: 29.5.1 + '@types/node': + specifier: ^14.18.43 + version: 14.18.43 + async-listen: + specifier: ^2.1.0 + version: 2.1.0 + jest: + specifier: ^29.5.0 + version: 29.5.0(@types/node@14.18.43) + proxy: + specifier: workspace:* + version: link:../proxy + ts-jest: + specifier: ^29.1.0 + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: ^5.0.4 + version: 5.0.4 + + packages/pac-proxy-agent: + dependencies: + '@tootallnate/once': + specifier: ^2.0.0 + version: 2.0.0 + agent-base: + specifier: ^6.0.2 + version: link:../agent-base + debug: + specifier: ^4.3.4 + version: 4.3.4 + get-uri: + specifier: ^5.0.0 + version: link:../get-uri + http-proxy-agent: + specifier: ^5.0.0 + version: link:../http-proxy-agent + https-proxy-agent: + specifier: ^5.0.1 + version: link:../https-proxy-agent + pac-resolver: + specifier: ^5.0.1 + version: link:../pac-resolver + socks-proxy-agent: + specifier: ^7.0.0 + version: link:../socks-proxy-agent + devDependencies: + '@types/debug': + specifier: ^4.1.7 + version: 4.1.7 + '@types/node': + specifier: ^14.18.43 + version: 14.18.43 + mocha: + specifier: ^6.2.3 + version: 6.2.3 + proxy: + specifier: workspace:* + version: link:../proxy + socksv5: + specifier: 0.0.6 + version: 0.0.6 + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: ^5.0.4 + version: 5.0.4 + + packages/pac-resolver: + dependencies: + degenerator: + specifier: ^3.0.2 + version: link:../degenerator + ip: + specifier: ^1.1.5 + version: 1.1.8 + netmask: + specifier: ^2.0.2 + version: 2.0.2 + devDependencies: + '@types/ip': + specifier: ^1.1.0 + version: 1.1.0 + '@types/netmask': + specifier: ^1.0.30 + version: 1.0.30 + '@types/node': + specifier: ^14.18.43 + version: 14.18.43 + mocha: + specifier: ^9.2.1 + version: 9.2.1 + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: ^5.0.4 + version: 5.0.4 + + packages/proxy: + dependencies: + '@tootallnate/once': + specifier: ^2.0.0 + version: 2.0.0 + args: + specifier: ^5.0.3 + version: 5.0.3 + basic-auth-parser: + specifier: 0.0.2-1 + version: 0.0.2-1 + debug: + specifier: ^4.3.4 + version: 4.3.4 + devDependencies: + '@types/args': + specifier: ^5.0.0 + version: 5.0.0 + '@types/debug': + specifier: ^4.1.7 + version: 4.1.7 + '@types/mocha': + specifier: ^5.2.7 + version: 5.2.7 + '@types/node': + specifier: ^14.18.43 + version: 14.18.43 + mocha: + specifier: '6' + version: 6.2.3 + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: ^5.0.4 + version: 5.0.4 + + packages/proxy-agent: + dependencies: + agent-base: + specifier: ^6.0.2 + version: link:../agent-base + debug: + specifier: ^4.3.4 + version: 4.3.4 + http-proxy-agent: + specifier: ^5.0.0 + version: link:../http-proxy-agent + https-proxy-agent: + specifier: ^5.0.1 + version: link:../https-proxy-agent + lru-cache: + specifier: ^9.1.1 + version: 9.1.1 + pac-proxy-agent: + specifier: ^5.0.0 + version: link:../pac-proxy-agent + proxy-from-env: + specifier: ^1.1.0 + version: 1.1.0 + socks-proxy-agent: + specifier: ^7.0.0 + version: link:../socks-proxy-agent + devDependencies: + '@types/agent-base': + specifier: ^4.2.0 + version: 4.2.0 + '@types/debug': + specifier: ^4.1.7 + version: 4.1.7 + '@types/jest': + specifier: ^29.5.1 + version: 29.5.1 + '@types/node': + specifier: ^14.18.43 + version: 14.18.43 + '@types/proxy-from-env': + specifier: ^1.0.1 + version: 1.0.1 + async-listen: + specifier: ^2.1.0 + version: 2.1.0 + jest: + specifier: ^29.5.0 + version: 29.5.0(@types/node@14.18.43) + proxy: + specifier: workspace:* + version: link:../proxy + socksv5: + specifier: github:TooTallNate/socksv5#fix/dstSock-close-event + version: github.com/TooTallNate/socksv5/d937368b28e929396166d77a06d387a4a902bd51 + ts-jest: + specifier: ^29.1.0 + version: 29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4) + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: ^5.0.4 + version: 5.0.4 + + packages/socks-proxy-agent: + dependencies: + agent-base: + specifier: ^6.0.2 + version: link:../agent-base + debug: + specifier: ^4.3.4 + version: 4.3.4 + socks: + specifier: ^2.7.1 + version: 2.7.1 + devDependencies: + '@types/debug': + specifier: ^4.1.7 + version: 4.1.7 + '@types/node': + specifier: ^14.18.43 + version: 14.18.43 + cacheable-lookup: + specifier: ^6.1.0 + version: 6.1.0 + dns2: + specifier: ^2.1.0 + version: 2.1.0 + mocha: + specifier: ^9.2.2 + version: 9.2.2 + socksv5: + specifier: github:TooTallNate/socksv5#fix/dstSock-close-event + version: github.com/TooTallNate/socksv5/d937368b28e929396166d77a06d387a4a902bd51 + tsconfig: + specifier: workspace:* + version: link:../tsconfig + typescript: + specifier: ^5.0.4 + version: 5.0.4 + + packages/tsconfig: {} + +packages: + + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 + dev: true + + /@babel/code-frame@7.12.11: + resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} + dependencies: + '@babel/highlight': 7.18.6 + dev: true + + /@babel/code-frame@7.21.4: + resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.18.6 + dev: true + + /@babel/compat-data@7.21.4: + resolution: {integrity: sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/core@7.21.4: + resolution: {integrity: sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.21.4 + '@babel/generator': 7.21.4 + '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) + '@babel/helper-module-transforms': 7.21.2 + '@babel/helpers': 7.21.0 + '@babel/parser': 7.21.4 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.4 + '@babel/types': 7.21.4 + convert-source-map: 1.9.0 + debug: 4.3.4 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/generator@7.21.4: + resolution: {integrity: sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.4 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 + jsesc: 2.5.2 + dev: true + + /@babel/helper-compilation-targets@7.21.4(@babel/core@7.21.4): + resolution: {integrity: sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/compat-data': 7.21.4 + '@babel/core': 7.21.4 + '@babel/helper-validator-option': 7.21.0 + browserslist: 4.21.5 + lru-cache: 5.1.1 + semver: 6.3.0 + dev: true + + /@babel/helper-environment-visitor@7.18.9: + resolution: {integrity: sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-function-name@7.21.0: + resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/types': 7.21.4 + dev: true + + /@babel/helper-hoist-variables@7.18.6: + resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.4 + dev: true + + /@babel/helper-module-imports@7.21.4: + resolution: {integrity: sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.4 + dev: true + + /@babel/helper-module-transforms@7.21.2: + resolution: {integrity: sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-module-imports': 7.21.4 + '@babel/helper-simple-access': 7.20.2 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/helper-validator-identifier': 7.19.1 + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.4 + '@babel/types': 7.21.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/helper-plugin-utils@7.20.2: + resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-simple-access@7.20.2: + resolution: {integrity: sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.4 + dev: true + + /@babel/helper-split-export-declaration@7.18.6: + resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.21.4 + dev: true + + /@babel/helper-string-parser@7.19.4: + resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-identifier@7.19.1: + resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helper-validator-option@7.21.0: + resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==} + engines: {node: '>=6.9.0'} + dev: true + + /@babel/helpers@7.21.0: + resolution: {integrity: sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.20.7 + '@babel/traverse': 7.21.4 + '@babel/types': 7.21.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/highlight@7.18.6: + resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.19.1 + chalk: 2.4.2 + js-tokens: 4.0.0 + dev: true + + /@babel/parser@7.21.4: + resolution: {integrity: sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.21.4 + dev: true + + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.4): + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.21.4): + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.4): + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.21.4): + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.4): + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-jsx@7.21.4(@babel/core@7.21.4): + resolution: {integrity: sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.4): + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.4): + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.4): + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.4): + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.4): + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.4): + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.4): + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/plugin-syntax-typescript@7.21.4(@babel/core@7.21.4): + resolution: {integrity: sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/core': 7.21.4 + '@babel/helper-plugin-utils': 7.20.2 + dev: true + + /@babel/template@7.20.7: + resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.21.4 + '@babel/parser': 7.21.4 + '@babel/types': 7.21.4 + dev: true + + /@babel/traverse@7.21.4: + resolution: {integrity: sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.21.4 + '@babel/generator': 7.21.4 + '@babel/helper-environment-visitor': 7.18.9 + '@babel/helper-function-name': 7.21.0 + '@babel/helper-hoist-variables': 7.18.6 + '@babel/helper-split-export-declaration': 7.18.6 + '@babel/parser': 7.21.4 + '@babel/types': 7.21.4 + debug: 4.3.4 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@babel/types@7.21.4: + resolution: {integrity: sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.19.4 + '@babel/helper-validator-identifier': 7.19.1 + to-fast-properties: 2.0.0 + dev: true + + /@bcoe/v8-coverage@0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + + /@eslint-community/eslint-utils@4.4.0(eslint@7.32.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 7.32.0 + eslint-visitor-keys: 3.4.0 + dev: true + + /@eslint-community/regexpp@4.5.0: + resolution: {integrity: sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@0.4.3: + resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 7.3.1 + globals: 13.18.0 + ignore: 4.0.6 + import-fresh: 3.3.0 + js-yaml: 3.14.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/config-array@0.5.0: + resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/object-schema@1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@istanbuljs/load-nyc-config@1.1.0: + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + dev: true + + /@istanbuljs/schema@0.1.3: + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + dev: true + + /@jest/console@29.5.0: + resolution: {integrity: sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@types/node': 14.18.43 + chalk: 4.1.2 + jest-message-util: 29.5.0 + jest-util: 29.5.0 + slash: 3.0.0 + dev: true + + /@jest/core@29.5.0: + resolution: {integrity: sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.5.0 + '@jest/reporters': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 14.18.43 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.8.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.5.0 + jest-config: 29.5.0(@types/node@14.18.43) + jest-haste-map: 29.5.0 + jest-message-util: 29.5.0 + jest-regex-util: 29.4.3 + jest-resolve: 29.5.0 + jest-resolve-dependencies: 29.5.0 + jest-runner: 29.5.0 + jest-runtime: 29.5.0 + jest-snapshot: 29.5.0 + jest-util: 29.5.0 + jest-validate: 29.5.0 + jest-watcher: 29.5.0 + micromatch: 4.0.5 + pretty-format: 29.5.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + + /@jest/environment@29.5.0: + resolution: {integrity: sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/fake-timers': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 14.18.43 + jest-mock: 29.5.0 + dev: true + + /@jest/expect-utils@29.5.0: + resolution: {integrity: sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.4.3 + dev: true + + /@jest/expect@29.5.0: + resolution: {integrity: sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + expect: 29.5.0 + jest-snapshot: 29.5.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/fake-timers@29.5.0: + resolution: {integrity: sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@sinonjs/fake-timers': 10.0.2 + '@types/node': 14.18.43 + jest-message-util: 29.5.0 + jest-mock: 29.5.0 + jest-util: 29.5.0 + dev: true + + /@jest/globals@29.5.0: + resolution: {integrity: sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.5.0 + '@jest/expect': 29.5.0 + '@jest/types': 29.5.0 + jest-mock: 29.5.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/reporters@29.5.0: + resolution: {integrity: sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@jridgewell/trace-mapping': 0.3.18 + '@types/node': 14.18.43 + chalk: 4.1.2 + collect-v8-coverage: 1.0.1 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.0 + istanbul-lib-instrument: 5.2.1 + istanbul-lib-report: 3.0.0 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.1.5 + jest-message-util: 29.5.0 + jest-util: 29.5.0 + jest-worker: 29.5.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.1.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/schemas@29.4.3: + resolution: {integrity: sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.25.24 + dev: true + + /@jest/source-map@29.4.3: + resolution: {integrity: sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jridgewell/trace-mapping': 0.3.18 + callsites: 3.1.0 + graceful-fs: 4.2.11 + dev: true + + /@jest/test-result@29.5.0: + resolution: {integrity: sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.5.0 + '@jest/types': 29.5.0 + '@types/istanbul-lib-coverage': 2.0.4 + collect-v8-coverage: 1.0.1 + dev: true + + /@jest/test-sequencer@29.5.0: + resolution: {integrity: sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.5.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.5.0 + slash: 3.0.0 + dev: true + + /@jest/transform@29.5.0: + resolution: {integrity: sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.21.4 + '@jest/types': 29.5.0 + '@jridgewell/trace-mapping': 0.3.18 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.5.0 + jest-regex-util: 29.4.3 + jest-util: 29.5.0 + micromatch: 4.0.5 + pirates: 4.0.5 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/types@29.5.0: + resolution: {integrity: sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.4.3 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 14.18.43 + '@types/yargs': 17.0.24 + chalk: 4.1.2 + dev: true + + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.18 + dev: true + + /@jridgewell/resolve-uri@3.1.0: + resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/set-array@1.1.2: + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.14: + resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.18: + resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.13.0 + dev: true + + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: true + optional: true + + /@sinclair/typebox@0.25.24: + resolution: {integrity: sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==} + dev: true + + /@sinonjs/commons@2.0.0: + resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==} + dependencies: + type-detect: 4.0.8 + dev: true + + /@sinonjs/fake-timers@10.0.2: + resolution: {integrity: sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==} + dependencies: + '@sinonjs/commons': 2.0.0 + dev: true + + /@tootallnate/once@2.0.0: + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + dev: false + + /@types/agent-base@4.2.0: + resolution: {integrity: sha512-8mrhPstU+ZX0Ugya8tl5DsDZ1I5ZwQzbL/8PA0z8Gj0k9nql7nkaMzmPVLj+l/nixWaliXi+EBiLA8bptw3z7Q==} + dependencies: + '@types/events': 3.0.0 + '@types/node': 14.18.43 + dev: true + + /@types/args@5.0.0: + resolution: {integrity: sha512-3fNb8ja/wQWFrHf5SQC5S3n0iBXdnT3PTPEJni2tBQRuv0BnAsz5u12U5gPRBSR7xdY6fI6QjWoTK/8ysuTt0w==} + dev: true + + /@types/babel__core@7.20.0: + resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==} + dependencies: + '@babel/parser': 7.21.4 + '@babel/types': 7.21.4 + '@types/babel__generator': 7.6.4 + '@types/babel__template': 7.4.1 + '@types/babel__traverse': 7.18.5 + dev: true + + /@types/babel__generator@7.6.4: + resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} + dependencies: + '@babel/types': 7.21.4 + dev: true + + /@types/babel__template@7.4.1: + resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} + dependencies: + '@babel/parser': 7.21.4 + '@babel/types': 7.21.4 + dev: true + + /@types/babel__traverse@7.18.5: + resolution: {integrity: sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q==} + dependencies: + '@babel/types': 7.21.4 + dev: true + + /@types/debug@4.1.0: + resolution: {integrity: sha512-QEYGliFbMQmDLZAcXJS+AbMDtnH0HjvgiALFbWPHsVn9dD75No+/MGEfpLCsAOWKg8WjtQxwfaPbRiq25XLYDw==} + dev: true + + /@types/debug@4.1.7: + resolution: {integrity: sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==} + dependencies: + '@types/ms': 0.7.31 + dev: true + + /@types/escodegen@0.0.6: + resolution: {integrity: sha512-AjwI4MvWx3HAOaZqYsjKWyEObT9lcVV0Y0V8nXo6cXzN8ZiMxVhf6F3d/UNvXVGKrEzL/Dluc5p+y9GkzlTWig==} + dev: true + + /@types/esprima@4.0.2: + resolution: {integrity: sha512-DKqdyuy7Go7ir6iKhZ0jUvgt/h9Q5zb9xS+fLeeXD2QSHv8gC6TimgujBBGfw8dHrpx4+u2HlMv7pkYOOfuUqg==} + dependencies: + '@types/estree': 1.0.1 + dev: true + + /@types/estree@1.0.1: + resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} + dev: true + + /@types/events@3.0.0: + resolution: {integrity: sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==} + dev: true + + /@types/fs-extra@8.1.2: + resolution: {integrity: sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg==} + dependencies: + '@types/node': 12.20.55 + dev: true + + /@types/ftpd@0.2.35: + resolution: {integrity: sha512-MeD8k0cx6wsmN84+59ex+2NCa0x5TgspdYWSDr91TWFutR4xITXD3ElExPvWJYmvS+HEvi1qiFc60NI5QKIVFQ==} + dependencies: + '@types/node': 12.20.55 + dev: true + + /@types/graceful-fs@4.1.6: + resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==} + dependencies: + '@types/node': 14.18.43 + dev: true + + /@types/ip@1.1.0: + resolution: {integrity: sha512-dwNe8gOoF70VdL6WJBwVHtQmAX4RMd62M+mAB9HQFjG1/qiCLM/meRy95Pd14FYBbEDwCq7jgJs89cHpLBu4HQ==} + dependencies: + '@types/node': 14.18.43 + dev: true + + /@types/istanbul-lib-coverage@2.0.4: + resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==} + dev: true + + /@types/istanbul-lib-report@3.0.0: + resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==} + dependencies: + '@types/istanbul-lib-coverage': 2.0.4 + dev: true + + /@types/istanbul-reports@3.0.1: + resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==} + dependencies: + '@types/istanbul-lib-report': 3.0.0 + dev: true + + /@types/jest@27.0.2: + resolution: {integrity: sha512-4dRxkS/AFX0c5XW6IPMNOydLn2tEhNhJV7DnYK+0bjoJZ+QTmfucBlihX7aoEsh/ocYtkLC73UbnBXBXIxsULA==} + dependencies: + jest-diff: 27.5.1 + pretty-format: 27.5.1 + dev: true + + /@types/jest@29.5.1: + resolution: {integrity: sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==} + dependencies: + expect: 29.5.0 + pretty-format: 29.5.0 + dev: true + + /@types/json-schema@7.0.11: + resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} + dev: true + + /@types/mocha@5.2.7: + resolution: {integrity: sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==} + dev: true + + /@types/ms@0.7.31: + resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} + dev: true + + /@types/netmask@1.0.30: + resolution: {integrity: sha512-Kl1xAICLv1Y7/WsNXkPKldRMz3QmXUYMIzr3rMXnIBDy9c4/sYG7V6P6u7Ja3w+uNtNQrRudJduqVoYX/DxfZg==} + dev: true + + /@types/node@12.20.55: + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + dev: true + + /@types/node@14.18.43: + resolution: {integrity: sha512-n3eFEaoem0WNwLux+k272P0+aq++5o05bA9CfiwKPdYPB5ZambWKdWoeHy7/OJiizMhzg27NLaZ6uzjLTzXceQ==} + dev: true + + /@types/prettier@2.7.2: + resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==} + dev: true + + /@types/proxy-from-env@1.0.1: + resolution: {integrity: sha512-luG++TFHyS61eKcfkR1CVV6a1GMNXDjtqEQIIfaSHax75xp0HU3SlezjOi1yqubJwrG8e9DeW59n6wTblIDwFg==} + dependencies: + '@types/node': 14.18.43 + dev: true + + /@types/semver@7.3.13: + resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==} + dev: true + + /@types/stack-utils@2.0.1: + resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} + dev: true + + /@types/ws@6.0.4: + resolution: {integrity: sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==} + dependencies: + '@types/node': 12.20.55 + dev: true + + /@types/yargs-parser@21.0.0: + resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} + dev: true + + /@types/yargs@17.0.24: + resolution: {integrity: sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==} + dependencies: + '@types/yargs-parser': 21.0.0 + dev: true + + /@typescript-eslint/eslint-plugin@5.59.1(@typescript-eslint/parser@5.59.1)(eslint@7.32.0)(typescript@5.0.4): + resolution: {integrity: sha512-AVi0uazY5quFB9hlp2Xv+ogpfpk77xzsgsIEWyVS7uK/c7MZ5tw7ZPbapa0SbfkqE0fsAMkz5UwtgMLVk2BQAg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.5.0 + '@typescript-eslint/parser': 5.59.1(eslint@7.32.0)(typescript@5.0.4) + '@typescript-eslint/scope-manager': 5.59.1 + '@typescript-eslint/type-utils': 5.59.1(eslint@7.32.0)(typescript@5.0.4) + '@typescript-eslint/utils': 5.59.1(eslint@7.32.0)(typescript@5.0.4) + debug: 4.3.4 + eslint: 7.32.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.0 + natural-compare-lite: 1.4.0 + semver: 7.3.8 + tsutils: 3.21.0(typescript@5.0.4) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@5.59.1(eslint@7.32.0)(typescript@5.0.4): + resolution: {integrity: sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.59.1 + '@typescript-eslint/types': 5.59.1 + '@typescript-eslint/typescript-estree': 5.59.1(typescript@5.0.4) + debug: 4.3.4 + eslint: 7.32.0 + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@5.59.1: + resolution: {integrity: sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.59.1 + '@typescript-eslint/visitor-keys': 5.59.1 + dev: true + + /@typescript-eslint/type-utils@5.59.1(eslint@7.32.0)(typescript@5.0.4): + resolution: {integrity: sha512-ZMWQ+Oh82jWqWzvM3xU+9y5U7MEMVv6GLioM3R5NJk6uvP47kZ7YvlgSHJ7ERD6bOY7Q4uxWm25c76HKEwIjZw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.59.1(typescript@5.0.4) + '@typescript-eslint/utils': 5.59.1(eslint@7.32.0)(typescript@5.0.4) + debug: 4.3.4 + eslint: 7.32.0 + tsutils: 3.21.0(typescript@5.0.4) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@5.59.1: + resolution: {integrity: sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree@5.59.1(typescript@5.0.4): + resolution: {integrity: sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.59.1 + '@typescript-eslint/visitor-keys': 5.59.1 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.3.8 + tsutils: 3.21.0(typescript@5.0.4) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@5.59.1(eslint@7.32.0)(typescript@5.0.4): + resolution: {integrity: sha512-MkTe7FE+K1/GxZkP5gRj3rCztg45bEhsd8HYjczBuYm+qFHP5vtZmjx3B0yUCDotceQ4sHgTyz60Ycl225njmA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@7.32.0) + '@types/json-schema': 7.0.11 + '@types/semver': 7.3.13 + '@typescript-eslint/scope-manager': 5.59.1 + '@typescript-eslint/types': 5.59.1 + '@typescript-eslint/typescript-estree': 5.59.1(typescript@5.0.4) + eslint: 7.32.0 + eslint-scope: 5.1.1 + semver: 7.3.8 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@5.59.1: + resolution: {integrity: sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.59.1 + eslint-visitor-keys: 3.4.0 + dev: true + + /@ungap/promise-all-settled@1.1.2: + resolution: {integrity: sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==} + dev: true + + /acorn-jsx@5.3.2(acorn@7.4.1): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 7.4.1 + dev: true + + /acorn-walk@8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + dev: false + + /acorn@7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /acorn@8.8.2: + resolution: {integrity: sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: false + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ajv@8.11.2: + resolution: {integrity: sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==} + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + dev: true + + /ansi-colors@3.2.3: + resolution: {integrity: sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==} + engines: {node: '>=6'} + dev: true + + /ansi-colors@4.1.1: + resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} + engines: {node: '>=6'} + dev: true + + /ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + dev: true + + /ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.21.3 + dev: true + + /ansi-regex@3.0.1: + resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==} + engines: {node: '>=4'} + dev: true + + /ansi-regex@4.1.1: + resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==} + engines: {node: '>=6'} + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + dev: true + + /args@5.0.3: + resolution: {integrity: sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==} + engines: {node: '>= 6.0.0'} + dependencies: + camelcase: 5.0.0 + chalk: 2.4.2 + leven: 2.1.0 + mri: 1.1.4 + dev: false + + /array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + dependencies: + call-bind: 1.0.2 + is-array-buffer: 3.0.2 + dev: true + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /array.prototype.reduce@1.0.5: + resolution: {integrity: sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + es-array-method-boxes-properly: 1.0.0 + is-string: 1.0.7 + dev: true + + /ast-types@0.13.4: + resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} + engines: {node: '>=4'} + dependencies: + tslib: 2.4.1 + dev: false + + /astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + dev: true + + /async-cache@1.1.0: + resolution: {integrity: sha512-YDQc4vBn5NFhY6g6HhVshyi3Fy9+SQ5ePnE7JLDJn1DoL+i7ER+vMwtTNOYk9leZkYMnOwpBCWqyLDPw8Aig8g==} + deprecated: No longer maintained. Use [lru-cache](http://npm.im/lru-cache) version 7.6 or higher, and provide an asynchronous `fetchMethod` option. + dependencies: + lru-cache: 4.1.5 + dev: true + + /async-limiter@1.0.1: + resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} + dev: true + + /async-listen@2.1.0: + resolution: {integrity: sha512-ckf3odtO729FoKt0KKuNA06VaWrsLQ/x8h2rnehC9mRi+vOvGSSjEnsJwT/Ttfhwe5GUp+Y/vBJhAYFiBN/Bnw==} + engines: {node: '>= 14'} + dev: true + + /async@0.2.10: + resolution: {integrity: sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==} + dev: true + + /available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + dev: true + + /babel-jest@29.5.0(@babel/core@7.21.4): + resolution: {integrity: sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + dependencies: + '@babel/core': 7.21.4 + '@jest/transform': 29.5.0 + '@types/babel__core': 7.20.0 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.5.0(@babel/core@7.21.4) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + dependencies: + '@babel/helper-plugin-utils': 7.20.2 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /babel-plugin-jest-hoist@29.5.0: + resolution: {integrity: sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/template': 7.20.7 + '@babel/types': 7.21.4 + '@types/babel__core': 7.20.0 + '@types/babel__traverse': 7.18.5 + dev: true + + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.21.4): + resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.4 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.4) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.4) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.21.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.4) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.4) + dev: true + + /babel-preset-jest@29.5.0(@babel/core@7.21.4): + resolution: {integrity: sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.21.4 + babel-plugin-jest-hoist: 29.5.0 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4) + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /basic-auth-parser@0.0.2-1: + resolution: {integrity: sha512-GFj8iVxo9onSU6BnnQvVwqvxh60UcSHJEDnIk3z4B6iOjsKSmqe+ibW0Rsz7YO7IE1HG3D3tqCNIidP46SZVdQ==} + dev: false + + /basic-ftp@5.0.2: + resolution: {integrity: sha512-NgkBwqp7rkhIUBaxLwL601lvUuBUvShJocrLYdiyTsH1WeP/wofMdooZ4p6hz+4hqiU59PBOj0EkaqELwFJLuQ==} + engines: {node: '>=10.0.0'} + dev: false + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /bl@1.2.3: + resolution: {integrity: sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==} + dependencies: + readable-stream: 2.3.8 + safe-buffer: 5.2.1 + dev: true + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + dev: true + + /browserslist@4.21.5: + resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001481 + electron-to-chromium: 1.4.373 + node-releases: 2.0.10 + update-browserslist-db: 1.0.11(browserslist@4.21.5) + dev: true + + /bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + dependencies: + fast-json-stable-stringify: 2.1.0 + dev: true + + /bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + dependencies: + node-int64: 0.4.0 + dev: true + + /buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + dev: true + + /cacheable-lookup@6.1.0: + resolution: {integrity: sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==} + engines: {node: '>=10.6.0'} + dev: true + + /call-bind@1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.2.0 + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /camelcase@5.0.0: + resolution: {integrity: sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==} + engines: {node: '>=6'} + dev: false + + /camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: true + + /camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + dev: true + + /caniuse-lite@1.0.30001481: + resolution: {integrity: sha512-KCqHwRnaa1InZBtqXzP98LPg0ajCVujMKjqKDhZEthIpAsJl/YEIa3YvXjGXPVqzZVguccuu7ga9KOE1J9rKPQ==} + dev: true + + /chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /ci-info@3.8.0: + resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} + engines: {node: '>=8'} + dev: true + + /cjs-module-lexer@1.2.2: + resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} + dev: true + + /cli@0.4.5: + resolution: {integrity: sha512-dbn5HyeJWSOU58RwOEiF1VWrl7HRvDsKLpu0uiI/vExH6iNoyUzjB5Mr3IJY5DVUfnbpe9793xw4DFJVzC9nWQ==} + engines: {node: '>=0.2.5'} + dependencies: + glob: 10.2.2 + dev: true + + /cliff@0.1.10: + resolution: {integrity: sha512-roZWcC2Cxo/kKjRXw7YUpVNtxJccbvcl7VzTjUYgLQk6Ot0R8bm2netbhSZYWWNrKlOO/7HD6GXHl8dtzE6SiQ==} + engines: {node: '>= 0.4.0'} + dependencies: + colors: 1.0.3 + eyes: 0.1.8 + winston: 0.8.3 + dev: true + + /cliui@5.0.0: + resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==} + dependencies: + string-width: 3.1.0 + strip-ansi: 5.2.0 + wrap-ansi: 5.1.0 + dev: true + + /cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + dev: true + + /collect-v8-coverage@1.0.1: + resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==} + dev: true + + /color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /colors@0.6.2: + resolution: {integrity: sha512-OsSVtHK8Ir8r3+Fxw/b4jS1ZLPXkV6ZxDRJQzeD7qo0SqMXWrHDM71DgYzPMHY8SFJ0Ao+nNU2p1MmwdzKqPrw==} + engines: {node: '>=0.1.90'} + dev: true + + /colors@1.0.3: + resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} + engines: {node: '>=0.1.90'} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + dev: true + + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + + /core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /cycle@1.0.3: + resolution: {integrity: sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==} + engines: {node: '>=0.4.0'} + dev: true + + /dateformat@1.0.7-1.2.3: + resolution: {integrity: sha512-rzAoghPcnojOgYhvqb9ZYR5Ws9hDM2SGB6xLSUX9pEAKYnbxyexHZypDLgx3+SF3SR+at6waVRCxKDm6q2Z6+g==} + dev: true + + /debug@3.2.6(supports-color@6.0.0): + resolution: {integrity: sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==} + deprecated: Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797) + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + supports-color: 6.0.0 + dev: true + + /debug@4.3.3(supports-color@8.1.1): + resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + supports-color: 8.1.1 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + + /decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + dev: true + + /decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + dev: true + + /dedent@0.7.0: + resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} + dev: true + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + dev: true + + /define-properties@1.2.0: + resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} + engines: {node: '>= 0.4'} + dependencies: + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + dev: true + + /detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + dev: true + + /diff-sequences@27.5.1: + resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dev: true + + /diff-sequences@29.4.3: + resolution: {integrity: sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /diff@3.5.0: + resolution: {integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==} + engines: {node: '>=0.3.1'} + dev: true + + /diff@5.0.0: + resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} + engines: {node: '>=0.3.1'} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /dns2@2.1.0: + resolution: {integrity: sha512-m27K11aQalRbmUs7RLaz6aPyceLjAoqjPRNTdE7qUouQpl+PC8Bi67O+i9SuJUPbQC8dxFrczAxfmTPuTKHNkw==} + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /electron-to-chromium@1.4.373: + resolution: {integrity: sha512-whGyixOVSRlyOBQDsRH9xltFaMij2/+DQRdaYahCq0P/fiVnAVGaW7OVsFnEjze/qUo298ez9C46gnALpo6ukg==} + dev: true + + /emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + dev: true + + /emoji-regex@7.0.3: + resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} + dev: true + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /enquirer@2.3.6: + resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} + engines: {node: '>=8.6'} + dependencies: + ansi-colors: 4.1.3 + dev: true + + /error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + dependencies: + is-arrayish: 0.2.1 + dev: true + + /es-abstract@1.21.2: + resolution: {integrity: sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==} + engines: {node: '>= 0.4'} + dependencies: + array-buffer-byte-length: 1.0.0 + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + es-set-tostringtag: 2.0.1 + es-to-primitive: 1.2.1 + function.prototype.name: 1.1.5 + get-intrinsic: 1.2.0 + get-symbol-description: 1.0.0 + globalthis: 1.0.3 + gopd: 1.0.1 + has: 1.0.3 + has-property-descriptors: 1.0.0 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.5 + is-array-buffer: 3.0.2 + is-callable: 1.2.7 + is-negative-zero: 2.0.2 + is-regex: 1.1.4 + is-shared-array-buffer: 1.0.2 + is-string: 1.0.7 + is-typed-array: 1.1.10 + is-weakref: 1.0.2 + object-inspect: 1.12.3 + object-keys: 1.1.1 + object.assign: 4.1.4 + regexp.prototype.flags: 1.4.3 + safe-regex-test: 1.0.0 + string.prototype.trim: 1.2.7 + string.prototype.trimend: 1.0.6 + string.prototype.trimstart: 1.0.6 + typed-array-length: 1.0.4 + unbox-primitive: 1.0.2 + which-typed-array: 1.1.9 + dev: true + + /es-array-method-boxes-properly@1.0.0: + resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} + dev: true + + /es-set-tostringtag@2.0.1: + resolution: {integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.0 + has: 1.0.3 + has-tostringtag: 1.0.0 + dev: true + + /es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + dependencies: + is-callable: 1.2.7 + is-date-object: 1.0.5 + is-symbol: 1.0.4 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + /escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + dev: true + + /escodegen@1.14.3: + resolution: {integrity: sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==} + engines: {node: '>=4.0'} + hasBin: true + dependencies: + esprima: 4.0.1 + estraverse: 4.3.0 + esutils: 2.0.3 + optionator: 0.8.3 + optionalDependencies: + source-map: 0.6.1 + dev: false + + /eslint-config-prettier@8.8.0(eslint@7.32.0): + resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 7.32.0 + dev: true + + /eslint-config-turbo@1.9.3(eslint@7.32.0): + resolution: {integrity: sha512-QG6jxFQkrGSpQqlFKefPdtgUfr20EbU0s4tGGIuGFOcPuJEdsY6VYZpZUxNJvmMcTGqPgMyOPjAFBKhy/DPHLA==} + peerDependencies: + eslint: '>6.6.0' + dependencies: + eslint: 7.32.0 + eslint-plugin-turbo: 1.9.3(eslint@7.32.0) + dev: true + + /eslint-plugin-turbo@1.9.3(eslint@7.32.0): + resolution: {integrity: sha512-ZsRtksdzk3v+z5/I/K4E50E4lfZ7oYmLX395gkrUMBz4/spJlYbr+GC8hP9oVNLj9s5Pvnm9rLv/zoj5PVYaVw==} + peerDependencies: + eslint: '>6.6.0' + dependencies: + eslint: 7.32.0 + dev: true + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-utils@2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + dependencies: + eslint-visitor-keys: 1.3.0 + dev: true + + /eslint-visitor-keys@1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + dev: true + + /eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + + /eslint-visitor-keys@3.4.0: + resolution: {integrity: sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@7.32.0: + resolution: {integrity: sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==} + engines: {node: ^10.12.0 || >=12.0.0} + hasBin: true + dependencies: + '@babel/code-frame': 7.12.11 + '@eslint/eslintrc': 0.4.3 + '@humanwhocodes/config-array': 0.5.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + enquirer: 2.3.6 + escape-string-regexp: 4.0.0 + eslint-scope: 5.1.1 + eslint-utils: 2.1.0 + eslint-visitor-keys: 2.1.0 + espree: 7.3.1 + esquery: 1.4.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + functional-red-black-tree: 1.0.1 + glob-parent: 5.1.2 + globals: 13.18.0 + ignore: 4.0.6 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + js-yaml: 3.14.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.1 + progress: 2.0.3 + regexpp: 3.2.0 + semver: 7.3.8 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + table: 6.8.1 + text-table: 0.2.0 + v8-compile-cache: 2.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /espree@7.3.1: + resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + acorn: 7.4.1 + acorn-jsx: 5.3.2(acorn@7.4.1) + eslint-visitor-keys: 1.3.0 + dev: true + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + /esquery@1.4.0: + resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + /execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + dev: true + + /exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + dev: true + + /expect@29.5.0: + resolution: {integrity: sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.5.0 + jest-get-type: 29.4.3 + jest-matcher-utils: 29.5.0 + jest-message-util: 29.5.0 + jest-util: 29.5.0 + dev: true + + /eyes@0.1.8: + resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} + engines: {node: '> 0.1.90'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.2.12: + resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + /fastq@1.13.0: + resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==} + dependencies: + reusify: 1.0.4 + dev: true + + /fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + dependencies: + bser: 2.1.1 + dev: true + + /fd@0.0.3: + resolution: {integrity: sha512-iAHrIslQb3U68OcMSP0kkNWabp7sSN6d2TBSb2JO3gcLJVDd4owr/hKM4SFJovFOUeeXeItjYgouEDTMWiVAnA==} + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.0.4 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + dependencies: + locate-path: 3.0.0 + dev: true + + /find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.0.4: + resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flatted: 3.2.7 + rimraf: 3.0.2 + dev: true + + /flat@4.1.1: + resolution: {integrity: sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==} + hasBin: true + dependencies: + is-buffer: 2.0.5 + dev: true + + /flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + dev: true + + /flatted@3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + + /for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + dependencies: + is-callable: 1.2.7 + dev: true + + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.0.1 + dev: true + + /fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: false + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /function.prototype.name@1.1.5: + resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + functions-have-names: 1.2.3 + dev: true + + /functional-red-black-tree@1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + dev: true + + /functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + dev: true + + /gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + dev: true + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /get-intrinsic@1.2.0: + resolution: {integrity: sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.3 + dev: true + + /get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + dev: true + + /get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + dev: true + + /get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@10.2.2: + resolution: {integrity: sha512-Xsa0BcxIC6th9UwNjZkhrMtNo/MnyRL8jGCP+uEwhA5oFOCY1f2s1/oNKY47xQ0Bg5nkjsfAEIej1VeH62bDDQ==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.1.0 + minimatch: 9.0.0 + minipass: 5.0.0 + path-scurry: 1.7.0 + dev: true + + /glob@7.1.3: + resolution: {integrity: sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /glob@7.2.0: + resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + dev: true + + /globals@13.18.0: + resolution: {integrity: sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.0 + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.2.12 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + dependencies: + get-intrinsic: 1.2.0 + dev: true + + /graceful-fs@4.1.15: + resolution: {integrity: sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==} + requiresBuild: true + dev: true + optional: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + /grapheme-splitter@1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + + /growl@1.10.5: + resolution: {integrity: sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==} + engines: {node: '>=4.x'} + dev: true + + /has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + dev: true + + /has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has-property-descriptors@1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + dependencies: + get-intrinsic: 1.2.0 + dev: true + + /has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + dev: true + + /has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + dev: true + + /has-tostringtag@1.0.0: + resolution: {integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + dev: true + + /html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + dev: true + + /human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + dev: true + + /ignore@4.0.6: + resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} + engines: {node: '>= 4'} + dev: true + + /ignore@5.2.0: + resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} + engines: {node: '>= 4'} + dev: true + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-local@3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /internal-slot@1.0.5: + resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + engines: {node: '>= 0.4'} + dependencies: + get-intrinsic: 1.2.0 + has: 1.0.3 + side-channel: 1.0.4 + dev: true + + /ip@1.1.8: + resolution: {integrity: sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==} + dev: false + + /ip@2.0.0: + resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} + dev: false + + /ipv6@3.1.3: + resolution: {integrity: sha512-TmLbUIURMAZ161GZDddTtAAb3aceRNLn7PRmP8fANp8xDRCW9oIQva8eenA48bRvw347jBqSREXMI38DybbUiQ==} + hasBin: true + dependencies: + cli: 0.4.5 + cliff: 0.1.10 + sprintf: 0.1.5 + dev: true + + /is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + is-typed-array: 1.1.10 + dev: true + + /is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + dev: true + + /is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + dependencies: + has-bigints: 1.0.2 + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + dev: true + + /is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + dev: true + + /is-core-module@2.12.0: + resolution: {integrity: sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==} + dependencies: + has: 1.0.3 + dev: true + + /is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point@2.0.0: + resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} + engines: {node: '>=4'} + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + dev: true + + /is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + dev: true + + /is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + + /is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + dependencies: + call-bind: 1.0.2 + dev: true + + /is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + dev: true + + /is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + + /is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + dependencies: + has-symbols: 1.0.3 + dev: true + + /is-typed-array@1.1.10: + resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + dev: true + + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + + /is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + dependencies: + call-bind: 1.0.2 + dev: true + + /isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + dev: true + + /isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /isstream@0.1.2: + resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} + dev: true + + /istanbul-lib-coverage@3.2.0: + resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} + engines: {node: '>=8'} + dev: true + + /istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + dependencies: + '@babel/core': 7.21.4 + '@babel/parser': 7.21.4 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.0 + semver: 6.3.0 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-lib-report@3.0.0: + resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} + engines: {node: '>=8'} + dependencies: + istanbul-lib-coverage: 3.2.0 + make-dir: 3.1.0 + supports-color: 7.2.0 + dev: true + + /istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + dependencies: + debug: 4.3.4 + istanbul-lib-coverage: 3.2.0 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + dev: true + + /istanbul-reports@3.1.5: + resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} + engines: {node: '>=8'} + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.0 + dev: true + + /jackspeak@2.1.0: + resolution: {integrity: sha512-DiEwVPqsieUzZBNxQ2cxznmFzfg/AMgJUjYw5xl6rSmCxAQXECcbSdwcLM6Ds6T09+SBfSNCGPhYUoQ96P4h7A==} + engines: {node: '>=14'} + dependencies: + cliui: 7.0.4 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: true + + /jest-changed-files@29.5.0: + resolution: {integrity: sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + execa: 5.1.1 + p-limit: 3.1.0 + dev: true + + /jest-circus@29.5.0: + resolution: {integrity: sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.5.0 + '@jest/expect': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 14.18.43 + chalk: 4.1.2 + co: 4.6.0 + dedent: 0.7.0 + is-generator-fn: 2.1.0 + jest-each: 29.5.0 + jest-matcher-utils: 29.5.0 + jest-message-util: 29.5.0 + jest-runtime: 29.5.0 + jest-snapshot: 29.5.0 + jest-util: 29.5.0 + p-limit: 3.1.0 + pretty-format: 29.5.0 + pure-rand: 6.0.2 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-cli@29.5.0(@types/node@14.18.43): + resolution: {integrity: sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/types': 29.5.0 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + import-local: 3.1.0 + jest-config: 29.5.0(@types/node@14.18.43) + jest-util: 29.5.0 + jest-validate: 29.5.0 + prompts: 2.4.2 + yargs: 17.7.1 + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + + /jest-config@29.5.0(@types/node@14.18.43): + resolution: {integrity: sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + dependencies: + '@babel/core': 7.21.4 + '@jest/test-sequencer': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 14.18.43 + babel-jest: 29.5.0(@babel/core@7.21.4) + chalk: 4.1.2 + ci-info: 3.8.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.5.0 + jest-environment-node: 29.5.0 + jest-get-type: 29.4.3 + jest-regex-util: 29.4.3 + jest-resolve: 29.5.0 + jest-runner: 29.5.0 + jest-util: 29.5.0 + jest-validate: 29.5.0 + micromatch: 4.0.5 + parse-json: 5.2.0 + pretty-format: 29.5.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-diff@27.5.1: + resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 27.5.1 + jest-get-type: 27.5.1 + pretty-format: 27.5.1 + dev: true + + /jest-diff@29.5.0: + resolution: {integrity: sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + diff-sequences: 29.4.3 + jest-get-type: 29.4.3 + pretty-format: 29.5.0 + dev: true + + /jest-docblock@29.4.3: + resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + + /jest-each@29.5.0: + resolution: {integrity: sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + chalk: 4.1.2 + jest-get-type: 29.4.3 + jest-util: 29.5.0 + pretty-format: 29.5.0 + dev: true + + /jest-environment-node@29.5.0: + resolution: {integrity: sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.5.0 + '@jest/fake-timers': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 14.18.43 + jest-mock: 29.5.0 + jest-util: 29.5.0 + dev: true + + /jest-get-type@27.5.1: + resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dev: true + + /jest-get-type@29.4.3: + resolution: {integrity: sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-haste-map@29.5.0: + resolution: {integrity: sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@types/graceful-fs': 4.1.6 + '@types/node': 14.18.43 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.4.3 + jest-util: 29.5.0 + jest-worker: 29.5.0 + micromatch: 4.0.5 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /jest-leak-detector@29.5.0: + resolution: {integrity: sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-get-type: 29.4.3 + pretty-format: 29.5.0 + dev: true + + /jest-matcher-utils@29.5.0: + resolution: {integrity: sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + jest-diff: 29.5.0 + jest-get-type: 29.4.3 + pretty-format: 29.5.0 + dev: true + + /jest-message-util@29.5.0: + resolution: {integrity: sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/code-frame': 7.21.4 + '@jest/types': 29.5.0 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.5 + pretty-format: 29.5.0 + slash: 3.0.0 + stack-utils: 2.0.6 + dev: true + + /jest-mock@29.5.0: + resolution: {integrity: sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@types/node': 14.18.43 + jest-util: 29.5.0 + dev: true + + /jest-pnp-resolver@1.2.3(jest-resolve@29.5.0): + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + dependencies: + jest-resolve: 29.5.0 + dev: true + + /jest-regex-util@29.4.3: + resolution: {integrity: sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-resolve-dependencies@29.5.0: + resolution: {integrity: sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + jest-regex-util: 29.4.3 + jest-snapshot: 29.5.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-resolve@29.5.0: + resolution: {integrity: sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.5.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.5.0) + jest-util: 29.5.0 + jest-validate: 29.5.0 + resolve: 1.22.2 + resolve.exports: 2.0.2 + slash: 3.0.0 + dev: true + + /jest-runner@29.5.0: + resolution: {integrity: sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/console': 29.5.0 + '@jest/environment': 29.5.0 + '@jest/test-result': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 14.18.43 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.4.3 + jest-environment-node: 29.5.0 + jest-haste-map: 29.5.0 + jest-leak-detector: 29.5.0 + jest-message-util: 29.5.0 + jest-resolve: 29.5.0 + jest-runtime: 29.5.0 + jest-util: 29.5.0 + jest-watcher: 29.5.0 + jest-worker: 29.5.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-runtime@29.5.0: + resolution: {integrity: sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.5.0 + '@jest/fake-timers': 29.5.0 + '@jest/globals': 29.5.0 + '@jest/source-map': 29.4.3 + '@jest/test-result': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 14.18.43 + chalk: 4.1.2 + cjs-module-lexer: 1.2.2 + collect-v8-coverage: 1.0.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.5.0 + jest-message-util: 29.5.0 + jest-mock: 29.5.0 + jest-regex-util: 29.4.3 + jest-resolve: 29.5.0 + jest-snapshot: 29.5.0 + jest-util: 29.5.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-snapshot@29.5.0: + resolution: {integrity: sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@babel/core': 7.21.4 + '@babel/generator': 7.21.4 + '@babel/plugin-syntax-jsx': 7.21.4(@babel/core@7.21.4) + '@babel/plugin-syntax-typescript': 7.21.4(@babel/core@7.21.4) + '@babel/traverse': 7.21.4 + '@babel/types': 7.21.4 + '@jest/expect-utils': 29.5.0 + '@jest/transform': 29.5.0 + '@jest/types': 29.5.0 + '@types/babel__traverse': 7.18.5 + '@types/prettier': 2.7.2 + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.4) + chalk: 4.1.2 + expect: 29.5.0 + graceful-fs: 4.2.11 + jest-diff: 29.5.0 + jest-get-type: 29.4.3 + jest-matcher-utils: 29.5.0 + jest-message-util: 29.5.0 + jest-util: 29.5.0 + natural-compare: 1.4.0 + pretty-format: 29.5.0 + semver: 7.5.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-util@29.5.0: + resolution: {integrity: sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + '@types/node': 14.18.43 + chalk: 4.1.2 + ci-info: 3.8.0 + graceful-fs: 4.2.11 + picomatch: 2.3.1 + dev: true + + /jest-validate@29.5.0: + resolution: {integrity: sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.5.0 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.4.3 + leven: 3.1.0 + pretty-format: 29.5.0 + dev: true + + /jest-watcher@29.5.0: + resolution: {integrity: sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.5.0 + '@jest/types': 29.5.0 + '@types/node': 14.18.43 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.5.0 + string-length: 4.0.2 + dev: true + + /jest-worker@29.5.0: + resolution: {integrity: sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@types/node': 14.18.43 + jest-util: 29.5.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + dev: true + + /jest@29.5.0(@types/node@14.18.43): + resolution: {integrity: sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.5.0 + '@jest/types': 29.5.0 + import-local: 3.1.0 + jest-cli: 29.5.0(@types/node@14.18.43) + transitivePeerDependencies: + - '@types/node' + - supports-color + - ts-node + dev: true + + /js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true + + /js-yaml@3.13.1: + resolution: {integrity: sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + dev: true + + /jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + optionalDependencies: + graceful-fs: 4.2.11 + dev: false + + /kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + dev: true + + /leven@2.1.0: + resolution: {integrity: sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==} + engines: {node: '>=0.10.0'} + dev: false + + /leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + dev: true + + /levn@0.3.0: + resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.1.2 + type-check: 0.3.2 + dev: false + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + dev: true + + /locate-path@3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + dev: true + + /locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + dependencies: + p-locate: 4.1.0 + dev: true + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /log-symbols@2.2.0: + resolution: {integrity: sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==} + engines: {node: '>=4'} + dependencies: + chalk: 2.4.2 + dev: true + + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + dev: true + + /lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 + dev: true + + /lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + dependencies: + yallist: 3.1.1 + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /lru-cache@9.1.1: + resolution: {integrity: sha512-65/Jky17UwSb0BuB9V+MyDpsOtXKmYwzhyl+cOa9XUiI4uV2Ouy/2voFP3+al0BjZbJgMBD8FojMpAf+Z+qn4A==} + engines: {node: 14 || >=16.14} + + /make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + dependencies: + semver: 6.3.0 + dev: true + + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + + /makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + dependencies: + tmpl: 1.0.5 + dev: true + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime@1.4.1: + resolution: {integrity: sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==} + hasBin: true + dev: true + + /mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + dev: true + + /minimatch@3.0.4: + resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@4.2.1: + resolution: {integrity: sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@9.0.0: + resolution: {integrity: sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + dev: true + + /mkdirp@0.5.4: + resolution: {integrity: sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==} + deprecated: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.) + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + + /mocha@6.2.3: + resolution: {integrity: sha512-0R/3FvjIGH3eEuG17ccFPk117XL2rWxatr81a57D+r/x2uTYZRbdZ4oVidEUMh2W2TJDa7MdAb12Lm2/qrKajg==} + engines: {node: '>= 6.0.0'} + hasBin: true + dependencies: + ansi-colors: 3.2.3 + browser-stdout: 1.3.1 + debug: 3.2.6(supports-color@6.0.0) + diff: 3.5.0 + escape-string-regexp: 1.0.5 + find-up: 3.0.0 + glob: 7.1.3 + growl: 1.10.5 + he: 1.2.0 + js-yaml: 3.13.1 + log-symbols: 2.2.0 + minimatch: 3.0.4 + mkdirp: 0.5.4 + ms: 2.1.1 + node-environment-flags: 1.0.5 + object.assign: 4.1.0 + strip-json-comments: 2.0.1 + supports-color: 6.0.0 + which: 1.3.1 + wide-align: 1.1.3 + yargs: 13.3.2 + yargs-parser: 13.1.2 + yargs-unparser: 1.6.0 + dev: true + + /mocha@9.2.1: + resolution: {integrity: sha512-T7uscqjJVS46Pq1XDXyo9Uvey9gd3huT/DD9cYBb4K2Xc/vbKRPUWK067bxDQRK0yIz6Jxk73IrnimvASzBNAQ==} + engines: {node: '>= 12.0.0'} + hasBin: true + dependencies: + '@ungap/promise-all-settled': 1.1.2 + ansi-colors: 4.1.1 + browser-stdout: 1.3.1 + chokidar: 3.5.3 + debug: 4.3.3(supports-color@8.1.1) + diff: 5.0.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 7.2.0 + growl: 1.10.5 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 3.0.4 + ms: 2.1.3 + nanoid: 3.2.0 + serialize-javascript: 6.0.0 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + which: 2.0.2 + workerpool: 6.2.0 + yargs: 16.2.0 + yargs-parser: 20.2.4 + yargs-unparser: 2.0.0 + dev: true + + /mocha@9.2.2: + resolution: {integrity: sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==} + engines: {node: '>= 12.0.0'} + hasBin: true + dependencies: + '@ungap/promise-all-settled': 1.1.2 + ansi-colors: 4.1.1 + browser-stdout: 1.3.1 + chokidar: 3.5.3 + debug: 4.3.3(supports-color@8.1.1) + diff: 5.0.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 7.2.0 + growl: 1.10.5 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 4.2.1 + ms: 2.1.3 + nanoid: 3.3.1 + serialize-javascript: 6.0.0 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + which: 2.0.2 + workerpool: 6.2.0 + yargs: 16.2.0 + yargs-parser: 20.2.4 + yargs-unparser: 2.0.0 + dev: true + + /mri@1.1.4: + resolution: {integrity: sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==} + engines: {node: '>=4'} + dev: false + + /ms@2.1.1: + resolution: {integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==} + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: true + + /nanoid@3.2.0: + resolution: {integrity: sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /nanoid@3.3.1: + resolution: {integrity: sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: true + + /netmask@2.0.2: + resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} + engines: {node: '>= 0.4.0'} + dev: false + + /node-environment-flags@1.0.5: + resolution: {integrity: sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==} + dependencies: + object.getownpropertydescriptors: 2.1.6 + semver: 5.7.1 + dev: true + + /node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + dev: true + + /node-releases@2.0.10: + resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + dependencies: + path-key: 3.1.1 + dev: true + + /object-inspect@1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + dev: true + + /object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + dev: true + + /object.assign@4.1.0: + resolution: {integrity: sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.0 + function-bind: 1.1.1 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + has-symbols: 1.0.3 + object-keys: 1.1.1 + dev: true + + /object.getownpropertydescriptors@2.1.6: + resolution: {integrity: sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ==} + engines: {node: '>= 0.8'} + dependencies: + array.prototype.reduce: 1.0.5 + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + safe-array-concat: 1.0.0 + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + dependencies: + mimic-fn: 2.1.0 + dev: true + + /optionator@0.8.3: + resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.3.0 + prelude-ls: 1.1.2 + type-check: 0.3.2 + word-wrap: 1.2.3 + dev: false + + /optionator@0.9.1: + resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==} + engines: {node: '>= 0.8.0'} + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.3 + dev: true + + /p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + dependencies: + '@babel/code-frame': 7.21.4 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + dev: true + + /path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + dev: true + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-scurry@1.7.0: + resolution: {integrity: sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 9.1.1 + minipass: 5.0.0 + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /pirates@4.0.5: + resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} + engines: {node: '>= 6'} + dev: true + + /pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + dev: true + + /pkginfo@0.3.1: + resolution: {integrity: sha512-yO5feByMzAp96LtP58wvPKSbaKAi/1C4kV9XpTctr6EepnP6F33RBNOiVrdz9BrPA98U2BMFsTNHo44TWcbQ2A==} + engines: {node: '>= 0.4.0'} + dev: true + + /prelude-ls@1.1.2: + resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} + engines: {node: '>= 0.8.0'} + dev: false + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier@2.8.0: + resolution: {integrity: sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + dev: true + + /pretty-format@29.5.0: + resolution: {integrity: sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.4.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + dev: true + + /progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + dev: true + + /prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + dev: true + + /proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + dev: false + + /pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + dev: true + + /punycode@2.1.1: + resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==} + engines: {node: '>=6'} + dev: true + + /pure-rand@6.0.2: + resolution: {integrity: sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + dev: true + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /regexp.prototype.flags@1.4.3: + resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + functions-have-names: 1.2.3 + dev: true + + /regexpp@3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true + + /require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + dev: true + + /require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + dev: true + + /resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + dependencies: + resolve-from: 5.0.0 + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + dev: true + + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: true + + /resolve@1.22.2: + resolution: {integrity: sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==} + hasBin: true + dependencies: + is-core-module: 2.12.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /safe-array-concat@1.0.0: + resolution: {integrity: sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + has-symbols: 1.0.3 + isarray: 2.0.5 + dev: true + + /safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + dev: true + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: true + + /safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + is-regex: 1.1.4 + dev: true + + /semver@5.7.1: + resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + hasBin: true + dev: true + + /semver@6.3.0: + resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} + hasBin: true + dev: true + + /semver@7.3.8: + resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /semver@7.5.0: + resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /serialize-javascript@6.0.0: + resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} + dependencies: + randombytes: 2.1.0 + dev: true + + /set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.2.0 + object-inspect: 1.12.3 + dev: true + + /signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + dev: true + + /signal-exit@4.0.1: + resolution: {integrity: sha512-uUWsN4aOxJAS8KOuf3QMyFtgm1pkb6I+KRZbRF/ghdf5T7sM+B1lLLzPDxswUjkmHyxQAVzEgG35E3NzDM9GVw==} + engines: {node: '>=14'} + dev: true + + /sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + dev: true + + /smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + dev: false + + /socks@2.7.1: + resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} + engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} + dependencies: + ip: 2.0.0 + smart-buffer: 4.2.0 + dev: false + + /socksv5@0.0.6: + resolution: {integrity: sha512-tQpQ0MdNQAsQBDhCXy3OvGGJikh9QOl3PkbwT4POJiQCm/fK4z9AxKQQRG8WLeF6talphnPrSWiZRpTl42rApg==} + engines: {node: '>=0.10.0'} + dependencies: + ipv6: 3.1.3 + dev: true + bundledDependencies: + - ipv6 + + /source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + dev: true + + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + requiresBuild: true + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true + + /sprintf@0.1.5: + resolution: {integrity: sha512-4X5KsuXFQ7f+d7Y+bi4qSb6eI+YoifDTGr0MQJXRoYO7BO7evfRCjds6kk3z7l5CiJYxgDN1x5Er4WiyCt+zTQ==} + engines: {node: '>=0.2.4'} + deprecated: The sprintf package is deprecated in favor of sprintf-js. + dev: true + + /st@1.2.2: + resolution: {integrity: sha512-goKkumvz0BMLs6KjjPf5Fub/3T34tRVQxInUI5lqtbaKD+s4HcRlJYP2GPJ8RgAmrsnYOPGmOFEP6ho0KJ+E8g==} + hasBin: true + dependencies: + async-cache: 1.1.0 + bl: 1.2.3 + fd: 0.0.3 + mime: 1.4.1 + negotiator: 0.6.3 + optionalDependencies: + graceful-fs: 4.1.15 + dev: true + + /stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + dev: true + + /stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + dependencies: + escape-string-regexp: 2.0.0 + dev: true + + /stat-mode@0.2.2: + resolution: {integrity: sha512-o+7DC0OM5Jt3+gratXXqfXf62V/CBoqQbT7Kp7jCxTYW2PLOB2/ZSGIfm9T5/QZe1Vw1MCbu6DoB6JnhVtxcJw==} + dev: true + + /string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + dependencies: + char-regex: 1.0.2 + strip-ansi: 6.0.1 + dev: true + + /string-width@2.1.1: + resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==} + engines: {node: '>=4'} + dependencies: + is-fullwidth-code-point: 2.0.0 + strip-ansi: 4.0.0 + dev: true + + /string-width@3.1.0: + resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==} + engines: {node: '>=6'} + dependencies: + emoji-regex: 7.0.3 + is-fullwidth-code-point: 2.0.0 + strip-ansi: 5.2.0 + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /string.prototype.trim@1.2.7: + resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /string.prototype.trimend@1.0.6: + resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /string.prototype.trimstart@1.0.6: + resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + dependencies: + call-bind: 1.0.2 + define-properties: 1.2.0 + es-abstract: 1.21.2 + dev: true + + /string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + dependencies: + safe-buffer: 5.1.2 + dev: true + + /strip-ansi@4.0.0: + resolution: {integrity: sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==} + engines: {node: '>=4'} + dependencies: + ansi-regex: 3.0.1 + dev: true + + /strip-ansi@5.2.0: + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} + dependencies: + ansi-regex: 4.1.1 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + dev: true + + /strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + dev: true + + /strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + dependencies: + has-flag: 3.0.0 + + /supports-color@6.0.0: + resolution: {integrity: sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==} + engines: {node: '>=6'} + dependencies: + has-flag: 3.0.0 + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /table@6.8.1: + resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} + engines: {node: '>=10.0.0'} + dependencies: + ajv: 8.11.2 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + dev: true + + /to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /ts-jest@29.1.0(@babel/core@7.21.4)(jest@29.5.0)(typescript@5.0.4): + resolution: {integrity: sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.21.4 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.5.0(@types/node@14.18.43) + jest-util: 29.5.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.5.0 + typescript: 5.0.4 + yargs-parser: 21.1.1 + dev: true + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + + /tslib@2.4.1: + resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==} + dev: false + + /tsutils@3.21.0(typescript@5.0.4): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.0.4 + dev: true + + /turbo-darwin-64@1.9.3: + resolution: {integrity: sha512-0dFc2cWXl82kRE4Z+QqPHhbEFEpUZho1msHXHWbz5+PqLxn8FY0lEVOHkq5tgKNNEd5KnGyj33gC/bHhpZOk5g==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /turbo-darwin-arm64@1.9.3: + resolution: {integrity: sha512-1cYbjqLBA2zYE1nbf/qVnEkrHa4PkJJbLo7hnuMuGM0bPzh4+AnTNe98gELhqI1mkTWBu/XAEeF5u6dgz0jLNA==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /turbo-linux-64@1.9.3: + resolution: {integrity: sha512-UuBPFefawEwpuxh5pM9Jqq3q4C8M0vYxVYlB3qea/nHQ80pxYq7ZcaLGEpb10SGnr3oMUUs1zZvkXWDNKCJb8Q==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /turbo-linux-arm64@1.9.3: + resolution: {integrity: sha512-vUrNGa3hyDtRh9W0MkO+l1dzP8Co2gKnOVmlJQW0hdpOlWlIh22nHNGGlICg+xFa2f9j4PbQlWTsc22c019s8Q==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /turbo-windows-64@1.9.3: + resolution: {integrity: sha512-0BZ7YaHs6r+K4ksqWus1GKK3W45DuDqlmfjm/yuUbTEVc8szmMCs12vugU2Zi5GdrdJSYfoKfEJ/PeegSLIQGQ==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /turbo-windows-arm64@1.9.3: + resolution: {integrity: sha512-QJUYLSsxdXOsR1TquiOmLdAgtYcQ/RuSRpScGvnZb1hY0oLc7JWU0llkYB81wVtWs469y8H9O0cxbKwCZGR4RQ==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /turbo@1.9.3: + resolution: {integrity: sha512-ID7mxmaLUPKG/hVkp+h0VuucB1U99RPCJD9cEuSEOdIPoSIuomcIClEJtKamUsdPLhLCud+BvapBNnhgh58Nzw==} + hasBin: true + requiresBuild: true + optionalDependencies: + turbo-darwin-64: 1.9.3 + turbo-darwin-arm64: 1.9.3 + turbo-linux-64: 1.9.3 + turbo-linux-arm64: 1.9.3 + turbo-windows-64: 1.9.3 + turbo-windows-arm64: 1.9.3 + dev: true + + /type-check@0.3.2: + resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.1.2 + dev: false + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + dev: true + + /typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + dependencies: + call-bind: 1.0.2 + for-each: 0.3.3 + is-typed-array: 1.1.10 + dev: true + + /typescript@5.0.4: + resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} + engines: {node: '>=12.20'} + hasBin: true + dev: true + + /ultron@1.1.1: + resolution: {integrity: sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==} + dev: true + + /unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + dependencies: + call-bind: 1.0.2 + has-bigints: 1.0.2 + has-symbols: 1.0.3 + which-boxed-primitive: 1.0.2 + dev: true + + /universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: false + + /update-browserslist-db@1.0.11(browserslist@4.21.5): + resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.21.5 + escalade: 3.1.1 + picocolors: 1.0.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.1.1 + dev: true + + /util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: true + + /v8-compile-cache@2.3.0: + resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} + dev: true + + /v8-to-istanbul@9.1.0: + resolution: {integrity: sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==} + engines: {node: '>=10.12.0'} + dependencies: + '@jridgewell/trace-mapping': 0.3.18 + '@types/istanbul-lib-coverage': 2.0.4 + convert-source-map: 1.9.0 + dev: true + + /vm2@3.9.17: + resolution: {integrity: sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==} + engines: {node: '>=6.0'} + hasBin: true + dependencies: + acorn: 8.8.2 + acorn-walk: 8.2.0 + dev: false + + /walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + dependencies: + makeerror: 1.0.12 + dev: true + + /which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + dependencies: + is-bigint: 1.0.4 + is-boolean-object: 1.1.2 + is-number-object: 1.0.7 + is-string: 1.0.7 + is-symbol: 1.0.4 + dev: true + + /which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + dev: true + + /which-typed-array@1.1.9: + resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.5 + call-bind: 1.0.2 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.0 + is-typed-array: 1.1.10 + dev: true + + /which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /wide-align@1.1.3: + resolution: {integrity: sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==} + dependencies: + string-width: 2.1.1 + dev: true + + /winston@0.8.3: + resolution: {integrity: sha512-fPoamsHq8leJ62D1M9V/f15mjQ1UHe4+7j1wpAT3fqgA5JqhJkk4aIfPEjfMTI9x6ZTjaLOpMAjluLtmgO5b6g==} + engines: {node: '>= 0.6.0'} + dependencies: + async: 0.2.10 + colors: 0.6.2 + cycle: 1.0.3 + eyes: 0.1.8 + isstream: 0.1.2 + pkginfo: 0.3.1 + stack-trace: 0.0.10 + dev: true + + /word-wrap@1.2.3: + resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} + engines: {node: '>=0.10.0'} + + /workerpool@6.2.0: + resolution: {integrity: sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==} + dev: true + + /wrap-ansi@5.1.0: + resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==} + engines: {node: '>=6'} + dependencies: + ansi-styles: 3.2.1 + string-width: 3.1.0 + strip-ansi: 5.2.0 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + dev: true + + /ws@3.3.3: + resolution: {integrity: sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dependencies: + async-limiter: 1.0.1 + safe-buffer: 5.1.2 + ultron: 1.1.1 + dev: true + + /y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + dev: true + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + dev: true + + /yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yargs-parser@13.1.2: + resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: true + + /yargs-parser@20.2.4: + resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} + engines: {node: '>=10'} + dev: true + + /yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: true + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true + + /yargs-unparser@1.6.0: + resolution: {integrity: sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==} + engines: {node: '>=6'} + dependencies: + flat: 4.1.1 + lodash: 4.17.21 + yargs: 13.3.2 + dev: true + + /yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + dependencies: + camelcase: 6.3.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + dev: true + + /yargs@13.3.2: + resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==} + dependencies: + cliui: 5.0.0 + find-up: 3.0.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 3.1.0 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 13.1.2 + dev: true + + /yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + dependencies: + cliui: 7.0.4 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + dev: true + + /yargs@17.7.1: + resolution: {integrity: sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true + + '@files-jg1s1zt9l.n8.io/ftpd-v0.2.14.tgz': + resolution: {tarball: https://files-jg1s1zt9l.n8.io/ftpd-v0.2.14.tgz} + name: ftpd + version: 0.2.14 + engines: {node: '>=0.10.0'} + dependencies: + dateformat: 1.0.7-1.2.3 + stat-mode: 0.2.2 + dev: true + + github.com/TooTallNate/socksv5/d937368b28e929396166d77a06d387a4a902bd51: + resolution: {tarball: https://codeload.github.com/TooTallNate/socksv5/tar.gz/d937368b28e929396166d77a06d387a4a902bd51} + name: socksv5 + version: 0.0.6 + engines: {node: '>=0.10.0'} + dependencies: + ipv6: 3.1.3 + dev: true + bundledDependencies: + - ipv6 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 00000000..3ff5faaa --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - "apps/*" + - "packages/*" diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 9ef0d5d5..00000000 --- a/src/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -import net from 'net'; -import tls from 'tls'; -import { Url } from 'url'; -import { AgentOptions } from 'agent-base'; -import { OutgoingHttpHeaders } from 'http'; -import _HttpsProxyAgent from './agent'; - -function createHttpsProxyAgent( - opts: string | createHttpsProxyAgent.HttpsProxyAgentOptions -): _HttpsProxyAgent { - return new _HttpsProxyAgent(opts); -} - -namespace createHttpsProxyAgent { - interface BaseHttpsProxyAgentOptions { - headers?: OutgoingHttpHeaders; - secureProxy?: boolean; - host?: string | null; - path?: string | null; - port?: string | number | null; - } - - export interface HttpsProxyAgentOptions - extends AgentOptions, - BaseHttpsProxyAgentOptions, - Partial< - Omit< - Url & net.NetConnectOpts & tls.ConnectionOptions, - keyof BaseHttpsProxyAgentOptions - > - > {} - - export type HttpsProxyAgent = _HttpsProxyAgent; - export const HttpsProxyAgent = _HttpsProxyAgent; - - createHttpsProxyAgent.prototype = _HttpsProxyAgent.prototype; -} - -export = createHttpsProxyAgent; diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 36fd26f2..00000000 --- a/test/test.js +++ /dev/null @@ -1,410 +0,0 @@ -/** - * Module dependencies. - */ - -let fs = require('fs'); -let url = require('url'); -let http = require('http'); -let https = require('https'); -let assert = require('assert'); -let Proxy = require('proxy'); -let HttpsProxyAgent = require('../'); - -describe('HttpsProxyAgent', function() { - let server; - let serverPort; - - let sslServer; - let sslServerPort; - - let proxy; - let proxyPort; - - let sslProxy; - let sslProxyPort; - - before(function(done) { - // setup target HTTP server - server = http.createServer(); - server.listen(function() { - serverPort = server.address().port; - done(); - }); - }); - - before(function(done) { - // setup HTTP proxy server - proxy = Proxy(); - proxy.listen(function() { - proxyPort = proxy.address().port; - done(); - }); - }); - - before(function(done) { - // setup target HTTPS server - let options = { - key: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.key`), - cert: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.pem`) - }; - sslServer = https.createServer(options); - sslServer.listen(function() { - sslServerPort = sslServer.address().port; - done(); - }); - }); - - before(function(done) { - // setup SSL HTTP proxy server - let options = { - key: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.key`), - cert: fs.readFileSync(`${__dirname}/ssl-cert-snakeoil.pem`) - }; - sslProxy = Proxy(https.createServer(options)); - sslProxy.listen(function() { - sslProxyPort = sslProxy.address().port; - done(); - }); - }); - - // shut down test HTTP server - after(function(done) { - server.once('close', function() { - done(); - }); - server.close(); - }); - - after(function(done) { - proxy.once('close', function() { - done(); - }); - proxy.close(); - }); - - after(function(done) { - sslServer.once('close', function() { - done(); - }); - sslServer.close(); - }); - - after(function(done) { - sslProxy.once('close', function() { - done(); - }); - sslProxy.close(); - }); - - describe('constructor', function() { - it('should throw an Error if no "proxy" argument is given', function() { - assert.throws(function() { - new HttpsProxyAgent(); - }); - }); - it('should accept a "string" proxy argument', function() { - let agent = new HttpsProxyAgent(`http://localhost:${proxyPort}`); - assert.equal('localhost', agent.proxy.host); - assert.equal(proxyPort, agent.proxy.port); - }); - it('should accept a `url.parse()` result object argument', function() { - let opts = url.parse(`http://localhost:${proxyPort}`); - let agent = new HttpsProxyAgent(opts); - assert.equal('localhost', agent.proxy.host); - assert.equal(proxyPort, agent.proxy.port); - }); - describe('secureProxy', function() { - it('should default to `false`', function() { - let agent = new HttpsProxyAgent({ port: proxyPort }); - assert.equal(false, agent.secureProxy); - }); - it('should be `false` when "http:" protocol is used', function() { - let agent = new HttpsProxyAgent({ - port: proxyPort, - protocol: 'http:' - }); - assert.equal(false, agent.secureProxy); - }); - it('should be `true` when "https:" protocol is used', function() { - let agent = new HttpsProxyAgent({ - port: proxyPort, - protocol: 'https:' - }); - assert.equal(true, agent.secureProxy); - }); - it('should be `true` when "https" protocol is used', function() { - let agent = new HttpsProxyAgent({ - port: proxyPort, - protocol: 'https' - }); - assert.equal(true, agent.secureProxy); - }); - }); - }); - - describe('"http" module', function() { - beforeEach(function() { - delete proxy.authenticate; - }); - - it('should work over an HTTP proxy', function(done) { - server.once('request', function(req, res) { - res.end(JSON.stringify(req.headers)); - }); - - let proxy = - process.env.HTTP_PROXY || - process.env.http_proxy || - `http://localhost:${proxyPort}`; - let agent = new HttpsProxyAgent(proxy); - - let opts = url.parse(`http://localhost:${serverPort}`); - opts.agent = agent; - - let req = http.get(opts, function(res) { - let data = ''; - res.setEncoding('utf8'); - res.on('data', function(b) { - data += b; - }); - res.on('end', function() { - data = JSON.parse(data); - assert.equal(`localhost:${serverPort}`, data.host); - done(); - }); - }); - req.once('error', done); - }); - it('should work over an HTTPS proxy', function(done) { - server.once('request', function(req, res) { - res.end(JSON.stringify(req.headers)); - }); - - let proxy = - process.env.HTTPS_PROXY || - process.env.https_proxy || - `https://localhost:${sslProxyPort}`; - proxy = url.parse(proxy); - proxy.rejectUnauthorized = false; - let agent = new HttpsProxyAgent(proxy); - - let opts = url.parse(`http://localhost:${serverPort}`); - opts.agent = agent; - - http.get(opts, function(res) { - let data = ''; - res.setEncoding('utf8'); - res.on('data', function(b) { - data += b; - }); - res.on('end', function() { - data = JSON.parse(data); - assert.equal(`localhost:${serverPort}`, data.host); - done(); - }); - }); - }); - it('should receive the 407 authorization code on the `http.ClientResponse`', function(done) { - // set a proxy authentication function for this test - proxy.authenticate = function(req, fn) { - // reject all requests - fn(null, false); - }; - - let proxyUri = - process.env.HTTP_PROXY || - process.env.http_proxy || - `http://localhost:${proxyPort}`; - let agent = new HttpsProxyAgent(proxyUri); - - let opts = {}; - // `host` and `port` don't really matter since the proxy will reject anyways - opts.host = 'localhost'; - opts.port = 80; - opts.agent = agent; - - let req = http.get(opts, function(res) { - assert.equal(407, res.statusCode); - assert('proxy-authenticate' in res.headers); - done(); - }); - }); - it('should not error if the proxy responds with 407 and the request is aborted', function(done) { - proxy.authenticate = function(req, fn) { - fn(null, false); - }; - - const proxyUri = - process.env.HTTP_PROXY || - process.env.http_proxy || - `http://localhost:${proxyPort}`; - - const req = http.get( - { - agent: new HttpsProxyAgent(proxyUri) - }, - function(res) { - assert.equal(407, res.statusCode); - req.abort(); - } - ); - - req.on('abort', done); - }); - it('should emit an "end" event on the `http.IncomingMessage` if the proxy responds with non-200 status code', function(done) { - proxy.authenticate = function(req, fn) { - fn(null, false); - }; - - const proxyUri = - process.env.HTTP_PROXY || - process.env.http_proxy || - `http://localhost:${proxyPort}`; - - const req = http.get( - { - agent: new HttpsProxyAgent(proxyUri) - }, - function(res) { - assert.equal(407, res.statusCode); - - res.resume(); - res.on('end', done); - } - ); - }); - it('should emit an "error" event on the `http.ClientRequest` if the proxy does not exist', function(done) { - // port 4 is a reserved, but "unassigned" port - let proxyUri = 'http://localhost:4'; - let agent = new HttpsProxyAgent(proxyUri); - - let opts = url.parse('http://nodejs.org'); - opts.agent = agent; - - let req = http.get(opts); - req.once('error', function(err) { - assert.equal('ECONNREFUSED', err.code); - req.abort(); - done(); - }); - }); - - it('should allow custom proxy "headers"', function(done) { - server.once('connect', function(req, socket, head) { - assert.equal('CONNECT', req.method); - assert.equal('bar', req.headers.foo); - socket.destroy(); - done(); - }); - - let uri = `http://localhost:${serverPort}`; - let proxyOpts = url.parse(uri); - proxyOpts.headers = { - Foo: 'bar' - }; - let agent = new HttpsProxyAgent(proxyOpts); - - let opts = {}; - // `host` and `port` don't really matter since the proxy will reject anyways - opts.host = 'localhost'; - opts.port = 80; - opts.agent = agent; - - http.get(opts); - }); - }); - - describe('"https" module', function() { - it('should work over an HTTP proxy', function(done) { - sslServer.once('request', function(req, res) { - res.end(JSON.stringify(req.headers)); - }); - - let proxy = - process.env.HTTP_PROXY || - process.env.http_proxy || - `http://localhost:${proxyPort}`; - let agent = new HttpsProxyAgent(proxy); - - let opts = url.parse(`https://localhost:${sslServerPort}`); - opts.rejectUnauthorized = false; - opts.agent = agent; - - https.get(opts, function(res) { - let data = ''; - res.setEncoding('utf8'); - res.on('data', function(b) { - data += b; - }); - res.on('end', function() { - data = JSON.parse(data); - assert.equal(`localhost:${sslServerPort}`, data.host); - done(); - }); - }); - }); - - it('should work over an HTTPS proxy', function(done) { - sslServer.once('request', function(req, res) { - res.end(JSON.stringify(req.headers)); - }); - - let proxy = - process.env.HTTPS_PROXY || - process.env.https_proxy || - `https://localhost:${sslProxyPort}`; - proxy = url.parse(proxy); - proxy.rejectUnauthorized = false; - let agent = new HttpsProxyAgent(proxy); - - let opts = url.parse(`https://localhost:${sslServerPort}`); - opts.agent = agent; - opts.rejectUnauthorized = false; - - https.get(opts, function(res) { - let data = ''; - res.setEncoding('utf8'); - res.on('data', function(b) { - data += b; - }); - res.on('end', function() { - data = JSON.parse(data); - assert.equal(`localhost:${sslServerPort}`, data.host); - done(); - }); - }); - }); - - it('should not send a port number for the default port', function(done) { - sslServer.once('request', function(req, res) { - res.end(JSON.stringify(req.headers)); - }); - - let proxy = - process.env.HTTPS_PROXY || - process.env.https_proxy || - `https://localhost:${sslProxyPort}`; - proxy = url.parse(proxy); - proxy.rejectUnauthorized = false; - let agent = new HttpsProxyAgent(proxy); - agent.defaultPort = sslServerPort; - - let opts = url.parse(`https://localhost:${sslServerPort}`); - opts.agent = agent; - opts.rejectUnauthorized = false; - - https.get(opts, function(res) { - let data = ''; - res.setEncoding('utf8'); - res.on('data', function(b) { - data += b; - }); - res.on('end', function() { - data = JSON.parse(data); - assert.equal('localhost', data.host); - done(); - }); - }); - }); - }); -}); diff --git a/turbo.json b/turbo.json new file mode 100644 index 00000000..eeb20769 --- /dev/null +++ b/turbo.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://turbo.build/schema.json", + "globalEnv": ["HTTP_PROXY", "HTTPS_PROXY", "NO_PROXY"], + "pipeline": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + }, + "test": { + "dependsOn": ["build"] + }, + "lint": {} + } +}