123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792 |
- /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- "use strict";
- var { Ci, Cc, CC, Cr, Cu } = require("chrome");
- // Ensure PSM is initialized to support TLS sockets
- Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
- var Services = require("Services");
- var promise = require("promise");
- var defer = require("devtools/shared/defer");
- var DevToolsUtils = require("devtools/shared/DevToolsUtils");
- var { dumpn, dumpv } = DevToolsUtils;
- loader.lazyRequireGetter(this, "WebSocketServer",
- "devtools/server/websocket-server");
- loader.lazyRequireGetter(this, "DebuggerTransport",
- "devtools/shared/transport/transport", true);
- loader.lazyRequireGetter(this, "WebSocketDebuggerTransport",
- "devtools/shared/transport/websocket-transport");
- loader.lazyRequireGetter(this, "DebuggerServer",
- "devtools/server/main", true);
- loader.lazyRequireGetter(this, "discovery",
- "devtools/shared/discovery/discovery");
- loader.lazyRequireGetter(this, "cert",
- "devtools/shared/security/cert");
- loader.lazyRequireGetter(this, "Authenticators",
- "devtools/shared/security/auth", true);
- loader.lazyRequireGetter(this, "AuthenticationResult",
- "devtools/shared/security/auth", true);
- DevToolsUtils.defineLazyGetter(this, "nsFile", () => {
- return CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");
- });
- DevToolsUtils.defineLazyGetter(this, "socketTransportService", () => {
- return Cc["@mozilla.org/network/socket-transport-service;1"]
- .getService(Ci.nsISocketTransportService);
- });
- DevToolsUtils.defineLazyGetter(this, "certOverrideService", () => {
- return Cc["@mozilla.org/security/certoverride;1"]
- .getService(Ci.nsICertOverrideService);
- });
- DevToolsUtils.defineLazyGetter(this, "nssErrorsService", () => {
- return Cc["@mozilla.org/nss_errors_service;1"]
- .getService(Ci.nsINSSErrorsService);
- });
- const { Task } = require("devtools/shared/task");
- var DebuggerSocket = {};
- /**
- * Connects to a debugger server socket.
- *
- * @param host string
- * The host name or IP address of the debugger server.
- * @param port number
- * The port number of the debugger server.
- * @param encryption boolean (optional)
- * Whether the server requires encryption. Defaults to false.
- * @param webSocket boolean (optional)
- * Whether to use WebSocket protocol to connect. Defaults to false.
- * @param authenticator Authenticator (optional)
- * |Authenticator| instance matching the mode in use by the server.
- * Defaults to a PROMPT instance if not supplied.
- * @param cert object (optional)
- * The server's cert details. Used with OOB_CERT authentication.
- * @return promise
- * Resolved to a DebuggerTransport instance.
- */
- DebuggerSocket.connect = Task.async(function* (settings) {
- // Default to PROMPT |Authenticator| instance if not supplied
- if (!settings.authenticator) {
- settings.authenticator = new (Authenticators.get().Client)();
- }
- _validateSettings(settings);
- let { host, port, encryption, authenticator, cert } = settings;
- let transport = yield _getTransport(settings);
- yield authenticator.authenticate({
- host,
- port,
- encryption,
- cert,
- transport
- });
- transport.connectionSettings = settings;
- return transport;
- });
- /**
- * Validate that the connection settings have been set to a supported configuration.
- */
- function _validateSettings(settings) {
- let { encryption, webSocket, authenticator } = settings;
- if (webSocket && encryption) {
- throw new Error("Encryption not supported on WebSocket transport");
- }
- authenticator.validateSettings(settings);
- }
- /**
- * Try very hard to create a DevTools transport, potentially making several
- * connect attempts in the process.
- *
- * @param host string
- * The host name or IP address of the debugger server.
- * @param port number
- * The port number of the debugger server.
- * @param encryption boolean (optional)
- * Whether the server requires encryption. Defaults to false.
- * @param webSocket boolean (optional)
- * Whether to use WebSocket protocol to connect to the server. Defaults to false.
- * @param authenticator Authenticator
- * |Authenticator| instance matching the mode in use by the server.
- * Defaults to a PROMPT instance if not supplied.
- * @param cert object (optional)
- * The server's cert details. Used with OOB_CERT authentication.
- * @return transport DebuggerTransport
- * A possible DevTools transport (if connection succeeded and streams
- * are actually alive and working)
- */
- var _getTransport = Task.async(function* (settings) {
- let { host, port, encryption, webSocket } = settings;
- if (webSocket) {
- // Establish a connection and wait until the WebSocket is ready to send and receive
- let socket = yield new Promise((resolve, reject) => {
- let s = new WebSocket(`ws://${host}:${port}`);
- s.onopen = () => resolve(s);
- s.onerror = err => reject(err);
- });
- return new WebSocketDebuggerTransport(socket);
- }
- let attempt = yield _attemptTransport(settings);
- if (attempt.transport) {
- return attempt.transport; // Success
- }
- // If the server cert failed validation, store a temporary override and make
- // a second attempt.
- if (encryption && attempt.certError) {
- _storeCertOverride(attempt.s, host, port);
- } else {
- throw new Error("Connection failed");
- }
- attempt = yield _attemptTransport(settings);
- if (attempt.transport) {
- return attempt.transport; // Success
- }
- throw new Error("Connection failed even after cert override");
- });
- /**
- * Make a single attempt to connect and create a DevTools transport. This could
- * fail if the remote host is unreachable, for example. If there is security
- * error due to the use of self-signed certs, you should make another attempt
- * after storing a cert override.
- *
- * @param host string
- * The host name or IP address of the debugger server.
- * @param port number
- * The port number of the debugger server.
- * @param encryption boolean (optional)
- * Whether the server requires encryption. Defaults to false.
- * @param authenticator Authenticator
- * |Authenticator| instance matching the mode in use by the server.
- * Defaults to a PROMPT instance if not supplied.
- * @param cert object (optional)
- * The server's cert details. Used with OOB_CERT authentication.
- * @return transport DebuggerTransport
- * A possible DevTools transport (if connection succeeded and streams
- * are actually alive and working)
- * @return certError boolean
- * Flag noting if cert trouble caused the streams to fail
- * @return s nsISocketTransport
- * Underlying socket transport, in case more details are needed.
- */
- var _attemptTransport = Task.async(function* (settings) {
- let { authenticator } = settings;
- // _attemptConnect only opens the streams. Any failures at that stage
- // aborts the connection process immedidately.
- let { s, input, output } = yield _attemptConnect(settings);
- // Check if the input stream is alive. If encryption is enabled, we need to
- // watch out for cert errors by testing the input stream.
- let alive, certError;
- try {
- let results = yield _isInputAlive(input);
- alive = results.alive;
- certError = results.certError;
- } catch (e) {
- // For other unexpected errors, like NS_ERROR_CONNECTION_REFUSED, we reach
- // this block.
- input.close();
- output.close();
- throw e;
- }
- dumpv("Server cert accepted? " + !certError);
- // The |Authenticator| examines the connection as well and may determine it
- // should be dropped.
- alive = alive && authenticator.validateConnection({
- host: settings.host,
- port: settings.port,
- encryption: settings.encryption,
- cert: settings.cert,
- socket: s
- });
- let transport;
- if (alive) {
- transport = new DebuggerTransport(input, output);
- } else {
- // Something went wrong, close the streams.
- input.close();
- output.close();
- }
- return { transport, certError, s };
- });
- /**
- * Try to connect to a remote server socket.
- *
- * If successsful, the socket transport and its opened streams are returned.
- * Typically, this will only fail if the host / port is unreachable. Other
- * problems, such as security errors, will allow this stage to succeed, but then
- * fail later when the streams are actually used.
- * @return s nsISocketTransport
- * Underlying socket transport, in case more details are needed.
- * @return input nsIAsyncInputStream
- * The socket's input stream.
- * @return output nsIAsyncOutputStream
- * The socket's output stream.
- */
- var _attemptConnect = Task.async(function* ({ host, port, encryption }) {
- let s;
- if (encryption) {
- s = socketTransportService.createTransport(["ssl"], 1, host, port, null);
- } else {
- s = socketTransportService.createTransport(null, 0, host, port, null);
- }
- // By default the CONNECT socket timeout is very long, 65535 seconds,
- // so that if we race to be in CONNECT state while the server socket is still
- // initializing, the connection is stuck in connecting state for 18.20 hours!
- s.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 2);
- // If encrypting, load the client cert now, so we can deliver it at just the
- // right time.
- let clientCert;
- if (encryption) {
- clientCert = yield cert.local.getOrCreate();
- }
- let deferred = defer();
- let input;
- let output;
- // Delay opening the input stream until the transport has fully connected.
- // The goal is to avoid showing the user a client cert UI prompt when
- // encryption is used. This prompt is shown when the client opens the input
- // stream and does not know which client cert to present to the server. To
- // specify a client cert programmatically, we need to access the transport's
- // nsISSLSocketControl interface, which is not accessible until the transport
- // has connected.
- s.setEventSink({
- onTransportStatus(transport, status) {
- if (status != Ci.nsISocketTransport.STATUS_CONNECTING_TO) {
- return;
- }
- if (encryption) {
- let sslSocketControl =
- transport.securityInfo.QueryInterface(Ci.nsISSLSocketControl);
- sslSocketControl.clientCert = clientCert;
- }
- try {
- input = s.openInputStream(0, 0, 0);
- } catch (e) {
- deferred.reject(e);
- }
- deferred.resolve({ s, input, output });
- }
- }, Services.tm.currentThread);
- // openOutputStream may throw NS_ERROR_NOT_INITIALIZED if we hit some race
- // where the nsISocketTransport gets shutdown in between its instantiation and
- // the call to this method.
- try {
- output = s.openOutputStream(0, 0, 0);
- } catch (e) {
- deferred.reject(e);
- }
- deferred.promise.catch(e => {
- if (input) {
- input.close();
- }
- if (output) {
- output.close();
- }
- DevToolsUtils.reportException("_attemptConnect", e);
- });
- return deferred.promise;
- });
- /**
- * Check if the input stream is alive. For an encrypted connection, it may not
- * be if the client refuses the server's cert. A cert error is expected on
- * first connection to a new host because the cert is self-signed.
- */
- function _isInputAlive(input) {
- let deferred = defer();
- input.asyncWait({
- onInputStreamReady(stream) {
- try {
- stream.available();
- deferred.resolve({ alive: true });
- } catch (e) {
- try {
- // getErrorClass may throw if you pass a non-NSS error
- let errorClass = nssErrorsService.getErrorClass(e.result);
- if (errorClass === Ci.nsINSSErrorsService.ERROR_CLASS_BAD_CERT) {
- deferred.resolve({ certError: true });
- } else {
- deferred.reject(e);
- }
- } catch (nssErr) {
- deferred.reject(e);
- }
- }
- }
- }, 0, 0, Services.tm.currentThread);
- return deferred.promise;
- }
- /**
- * To allow the connection to proceed with self-signed cert, we store a cert
- * override. This implies that we take on the burden of authentication for
- * these connections.
- */
- function _storeCertOverride(s, host, port) {
- let cert = s.securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
- .SSLStatus.serverCert;
- let overrideBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED |
- Ci.nsICertOverrideService.ERROR_MISMATCH;
- certOverrideService.rememberValidityOverride(host, port, cert, overrideBits,
- true /* temporary */);
- }
- /**
- * Creates a new socket listener for remote connections to the DebuggerServer.
- * This helps contain and organize the parts of the server that may differ or
- * are particular to one given listener mechanism vs. another.
- */
- function SocketListener() {}
- SocketListener.prototype = {
- /* Socket Options */
- /**
- * The port or path to listen on.
- *
- * If given an integer, the port to listen on. Use -1 to choose any available
- * port. Otherwise, the path to the unix socket domain file to listen on.
- */
- portOrPath: null,
- /**
- * Controls whether this listener is announced via the service discovery
- * mechanism.
- */
- discoverable: false,
- /**
- * Controls whether this listener's transport uses encryption.
- */
- encryption: false,
- /**
- * Controls the |Authenticator| used, which hooks various socket steps to
- * implement an authentication policy. It is expected that different use
- * cases may override pieces of the |Authenticator|. See auth.js.
- *
- * Here we set the default |Authenticator|, which is |Prompt|.
- */
- authenticator: new (Authenticators.get().Server)(),
- /**
- * Validate that all options have been set to a supported configuration.
- */
- _validateOptions: function () {
- if (this.portOrPath === null) {
- throw new Error("Must set a port / path to listen on.");
- }
- if (this.discoverable && !Number(this.portOrPath)) {
- throw new Error("Discovery only supported for TCP sockets.");
- }
- if (this.encryption && this.webSocket) {
- throw new Error("Encryption not supported on WebSocket transport");
- }
- this.authenticator.validateOptions(this);
- },
- /**
- * Listens on the given port or socket file for remote debugger connections.
- */
- open: function () {
- this._validateOptions();
- DebuggerServer._addListener(this);
- let flags = Ci.nsIServerSocket.KeepWhenOffline;
- // A preference setting can force binding on the loopback interface.
- if (Services.prefs.getBoolPref("devtools.debugger.force-local")) {
- flags |= Ci.nsIServerSocket.LoopbackOnly;
- }
- let self = this;
- return Task.spawn(function* () {
- let backlog = 4;
- self._socket = self._createSocketInstance();
- if (self.isPortBased) {
- let port = Number(self.portOrPath);
- self._socket.initSpecialConnection(port, flags, backlog);
- } else {
- let file = nsFile(self.portOrPath);
- if (file.exists()) {
- file.remove(false);
- }
- self._socket.initWithFilename(file, parseInt("666", 8), backlog);
- }
- yield self._setAdditionalSocketOptions();
- self._socket.asyncListen(self);
- dumpn("Socket listening on: " + (self.port || self.portOrPath));
- }).then(() => {
- this._advertise();
- }).catch(e => {
- dumpn("Could not start debugging listener on '" + this.portOrPath +
- "': " + e);
- this.close();
- });
- },
- _advertise: function () {
- if (!this.discoverable || !this.port) {
- return;
- }
- let advertisement = {
- port: this.port,
- encryption: this.encryption,
- };
- this.authenticator.augmentAdvertisement(this, advertisement);
- discovery.addService("devtools", advertisement);
- },
- _createSocketInstance: function () {
- if (this.encryption) {
- return Cc["@mozilla.org/network/tls-server-socket;1"]
- .createInstance(Ci.nsITLSServerSocket);
- }
- return Cc["@mozilla.org/network/server-socket;1"]
- .createInstance(Ci.nsIServerSocket);
- },
- _setAdditionalSocketOptions: Task.async(function* () {
- if (this.encryption) {
- this._socket.serverCert = yield cert.local.getOrCreate();
- this._socket.setSessionTickets(false);
- let requestCert = Ci.nsITLSServerSocket.REQUEST_NEVER;
- this._socket.setRequestClientCertificate(requestCert);
- }
- this.authenticator.augmentSocketOptions(this, this._socket);
- }),
- /**
- * Closes the SocketListener. Notifies the server to remove the listener from
- * the set of active SocketListeners.
- */
- close: function () {
- if (this.discoverable && this.port) {
- discovery.removeService("devtools");
- }
- if (this._socket) {
- this._socket.close();
- this._socket = null;
- }
- DebuggerServer._removeListener(this);
- },
- get host() {
- if (!this._socket) {
- return null;
- }
- if (Services.prefs.getBoolPref("devtools.debugger.force-local")) {
- return "127.0.0.1";
- }
- return "0.0.0.0";
- },
- /**
- * Gets whether this listener uses a port number vs. a path.
- */
- get isPortBased() {
- return !!Number(this.portOrPath);
- },
- /**
- * Gets the port that a TCP socket listener is listening on, or null if this
- * is not a TCP socket (so there is no port).
- */
- get port() {
- if (!this.isPortBased || !this._socket) {
- return null;
- }
- return this._socket.port;
- },
- get cert() {
- if (!this._socket || !this._socket.serverCert) {
- return null;
- }
- return {
- sha256: this._socket.serverCert.sha256Fingerprint
- };
- },
- // nsIServerSocketListener implementation
- onSocketAccepted:
- DevToolsUtils.makeInfallible(function (socket, socketTransport) {
- new ServerSocketConnection(this, socketTransport);
- }, "SocketListener.onSocketAccepted"),
- onStopListening: function (socket, status) {
- dumpn("onStopListening, status: " + status);
- }
- };
- // Client must complete TLS handshake within this window (ms)
- loader.lazyGetter(this, "HANDSHAKE_TIMEOUT", () => {
- return Services.prefs.getIntPref("devtools.remote.tls-handshake-timeout");
- });
- /**
- * A |ServerSocketConnection| is created by a |SocketListener| for each accepted
- * incoming socket. This is a short-lived object used to implement
- * authentication and verify encryption prior to handing off the connection to
- * the |DebuggerServer|.
- */
- function ServerSocketConnection(listener, socketTransport) {
- this._listener = listener;
- this._socketTransport = socketTransport;
- this._handle();
- }
- ServerSocketConnection.prototype = {
- get authentication() {
- return this._listener.authenticator.mode;
- },
- get host() {
- return this._socketTransport.host;
- },
- get port() {
- return this._socketTransport.port;
- },
- get cert() {
- if (!this._clientCert) {
- return null;
- }
- return {
- sha256: this._clientCert.sha256Fingerprint
- };
- },
- get address() {
- return this.host + ":" + this.port;
- },
- get client() {
- let client = {
- host: this.host,
- port: this.port
- };
- if (this.cert) {
- client.cert = this.cert;
- }
- return client;
- },
- get server() {
- let server = {
- host: this._listener.host,
- port: this._listener.port
- };
- if (this._listener.cert) {
- server.cert = this._listener.cert;
- }
- return server;
- },
- /**
- * This is the main authentication workflow. If any pieces reject a promise,
- * the connection is denied. If the entire process resolves successfully,
- * the connection is finally handed off to the |DebuggerServer|.
- */
- _handle() {
- dumpn("Debugging connection starting authentication on " + this.address);
- let self = this;
- Task.spawn(function* () {
- self._listenForTLSHandshake();
- yield self._createTransport();
- yield self._awaitTLSHandshake();
- yield self._authenticate();
- }).then(() => this.allow()).catch(e => this.deny(e));
- },
- /**
- * We need to open the streams early on, as that is required in the case of
- * TLS sockets to keep the handshake moving.
- */
- _createTransport: Task.async(function* () {
- let input = this._socketTransport.openInputStream(0, 0, 0);
- let output = this._socketTransport.openOutputStream(0, 0, 0);
- if (this._listener.webSocket) {
- let socket = yield WebSocketServer.accept(this._socketTransport, input, output);
- this._transport = new WebSocketDebuggerTransport(socket);
- } else {
- this._transport = new DebuggerTransport(input, output);
- }
- // Start up the transport to observe the streams in case they are closed
- // early. This allows us to clean up our state as well.
- this._transport.hooks = {
- onClosed: reason => {
- this.deny(reason);
- }
- };
- this._transport.ready();
- }),
- /**
- * Set the socket's security observer, which receives an event via the
- * |onHandshakeDone| callback when the TLS handshake completes.
- */
- _setSecurityObserver(observer) {
- if (!this._socketTransport || !this._socketTransport.securityInfo) {
- return;
- }
- let connectionInfo = this._socketTransport.securityInfo
- .QueryInterface(Ci.nsITLSServerConnectionInfo);
- connectionInfo.setSecurityObserver(observer);
- },
- /**
- * When encryption is used, we wait for the client to complete the TLS
- * handshake before proceeding. The handshake details are validated in
- * |onHandshakeDone|.
- */
- _listenForTLSHandshake() {
- this._handshakeDeferred = defer();
- if (!this._listener.encryption) {
- this._handshakeDeferred.resolve();
- return;
- }
- this._setSecurityObserver(this);
- this._handshakeTimeout = setTimeout(this._onHandshakeTimeout.bind(this),
- HANDSHAKE_TIMEOUT);
- },
- _awaitTLSHandshake() {
- return this._handshakeDeferred.promise;
- },
- _onHandshakeTimeout() {
- dumpv("Client failed to complete TLS handshake");
- this._handshakeDeferred.reject(Cr.NS_ERROR_NET_TIMEOUT);
- },
- // nsITLSServerSecurityObserver implementation
- onHandshakeDone(socket, clientStatus) {
- clearTimeout(this._handshakeTimeout);
- this._setSecurityObserver(null);
- dumpv("TLS version: " + clientStatus.tlsVersionUsed.toString(16));
- dumpv("TLS cipher: " + clientStatus.cipherName);
- dumpv("TLS key length: " + clientStatus.keyLength);
- dumpv("TLS MAC length: " + clientStatus.macLength);
- this._clientCert = clientStatus.peerCert;
- /*
- * TODO: These rules should be really be set on the TLS socket directly, but
- * this would need more platform work to expose it via XPCOM.
- *
- * Enforcing cipher suites here would be a bad idea, as we want TLS
- * cipher negotiation to work correctly. The server already allows only
- * Gecko's normal set of cipher suites.
- */
- if (clientStatus.tlsVersionUsed < Ci.nsITLSClientStatus.TLS_VERSION_1_2) {
- this._handshakeDeferred.reject(Cr.NS_ERROR_CONNECTION_REFUSED);
- return;
- }
- this._handshakeDeferred.resolve();
- },
- _authenticate: Task.async(function* () {
- let result = yield this._listener.authenticator.authenticate({
- client: this.client,
- server: this.server,
- transport: this._transport
- });
- switch (result) {
- case AuthenticationResult.DISABLE_ALL:
- DebuggerServer.closeAllListeners();
- Services.prefs.setBoolPref("devtools.debugger.remote-enabled", false);
- return promise.reject(Cr.NS_ERROR_CONNECTION_REFUSED);
- case AuthenticationResult.DENY:
- return promise.reject(Cr.NS_ERROR_CONNECTION_REFUSED);
- case AuthenticationResult.ALLOW:
- case AuthenticationResult.ALLOW_PERSIST:
- return promise.resolve();
- default:
- return promise.reject(Cr.NS_ERROR_CONNECTION_REFUSED);
- }
- }),
- deny(result) {
- if (this._destroyed) {
- return;
- }
- let errorName = result;
- for (let name in Cr) {
- if (Cr[name] === result) {
- errorName = name;
- break;
- }
- }
- dumpn("Debugging connection denied on " + this.address +
- " (" + errorName + ")");
- if (this._transport) {
- this._transport.hooks = null;
- this._transport.close(result);
- }
- this._socketTransport.close(result);
- this.destroy();
- },
- allow() {
- if (this._destroyed) {
- return;
- }
- dumpn("Debugging connection allowed on " + this.address);
- DebuggerServer._onConnection(this._transport);
- this.destroy();
- },
- destroy() {
- this._destroyed = true;
- clearTimeout(this._handshakeTimeout);
- this._setSecurityObserver(null);
- this._listener = null;
- this._socketTransport = null;
- this._transport = null;
- this._clientCert = null;
- }
- };
- DebuggerSocket.createListener = function () {
- return new SocketListener();
- };
- exports.DebuggerSocket = DebuggerSocket;
|