UNPKG

hivessh

Version:

HiveSsh is an innovative library designed to streamline SSH2 connections and simplify task execution on Linux servers.

149 lines 21.1 kB
import net from "net"; /** * Returns true if the given options are for a local socket (i.e. path is present). * * @param options - The options to check. * @returns True if the options are for a local socket, otherwise false. */ export function isSshTunnelOutLocalSocketOption(options) { return typeof options == "object" && options != null && typeof options.localPath == "string"; } /** * Returns true if the given options are for a remote socket (i.e. remotePath is present). * * @param options - The options to check. * @returns True if the options are for a remote socket, otherwise false. */ export function isSshTunnelOutRemoteSocketOption(options) { return typeof options == "object" && options != null && typeof options.remotePath == "string"; } /** * @experimental This function is experimental and may not be stable. * Tunnels incoming server socket conntions to a remote host and port bind (not linux socket). * * This function establishes an SSH tunnel by forwarding * a local server's address and port to a specified remote * host and port. It manages the connection and lifecycle of * the tunnel, ensuring that resources are cleaned up upon * closure of the server or socket. * * @param sshClient - The SSH client instance used to establish the tunnel. * @param server - The local server for which the tunnel is being created. * @param socket - The socket associated with the server. * @param tunnelOptions - Options specifying remote host and port details * and optional forwarding source host and port. * * @throws {Error} If the server's socket address is invalid. */ export function handleRemoteHostPortOutTunnel(sshClient, server, socket, tunnelOptions) { const address = server.address(); if (typeof address.address != "string" || typeof address.family != "string" || typeof address.port != "number") { throw new Error("Invalid server socket address: " + JSON.stringify(address, null, 2)); } sshClient.forwardOut(tunnelOptions.forwardSourceHost ?? address.address, tunnelOptions.forwardSourcePort ?? address.port, tunnelOptions.remoteHost, tunnelOptions.remotePort, (err, clientChannel) => { if (err) { server.emit("error", err); return; } server.on('close', () => { if (!clientChannel.closed) { clientChannel.end(); } if (!socket.closed) { socket.end(); } }); socket.pipe(clientChannel).pipe(socket); }); } /** * @experimental This function is experimental and may not be stable. * Tunnels incoming server socket conntions to a remote socket (not address and port bind) using openssh. * * This function establishes an SSH tunnel by forwarding * a local server's address and port to a specified remote * socket path. It manages the connection and lifecycle of * the tunnel, ensuring that resources are cleaned up upon * closure of the server or socket. * * @param sshClient - The SSH client instance used to establish the tunnel. * @param server - The local server for which the tunnel is being created. * @param socket - The socket associated with the server. * @param tunnelOptions - Options specifying remote socket path details * and optional forwarding source host and port. * * @throws {Error} If the server's socket address is invalid. */ export function handleRemoteSocketOutTunnel(sshClient, server, socket, tunnelOptions) { const address = server.address(); if (typeof address.address != "string" || typeof address.family != "string" || typeof address.port != "number") { throw new Error("Invalid server socket address: " + JSON.stringify(address, null, 2)); } sshClient.openssh_forwardOutStreamLocal(tunnelOptions.remotePath, (err, clientChannel) => { if (err) { server.emit("error", err); return; } server.on('close', () => { if (!clientChannel.closed) { clientChannel.end(); } if (!socket.closed) { socket.end(); } }); socket.pipe(clientChannel).pipe(socket); }); } /** * @experimental This function is experimental and may not be stable. * Creates a local server that tunnels incoming connections to a remote linux socket or host and port bind. * * You need to close the server to stop tunneling! * * This function creates a server that listens for incoming connections and forwards them to the remote SSH host. * * @param sshClient - The SSH client instance used to establish the tunnel. * @param tunnelOptions - Options specifying remote linux socket or host and port details. * * @returns A promise that resolves to the created server that need to be closed. */ export function tunnelOut(sshClient, tunnelOptions) { return new Promise((res, rej) => { let server; server = net.createServer(); server.on('error', (err) => { server.close(); rej(err); }); server.on('connection', isSshTunnelOutRemoteSocketOption(tunnelOptions) ? (socket) => handleRemoteSocketOutTunnel(sshClient, server, socket, tunnelOptions) : (socket) => handleRemoteHostPortOutTunnel(sshClient, server, socket, tunnelOptions)); const baseTunnelServerOptions = isSshTunnelOutLocalSocketOption(tunnelOptions) ? { path: tunnelOptions.localPath, } : { host: tunnelOptions.localHost, port: tunnelOptions.localPort, }; server.listen({ ...tunnelOptions, ...baseTunnelServerOptions, }); res(server); }); } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU3NoVHVubmVsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1NzaFR1bm5lbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEdBQTRDLE1BQU0sS0FBSyxDQUFDO0FBeUMvRDs7Ozs7R0FLRztBQUNILE1BQU0sVUFBVSwrQkFBK0IsQ0FDM0MsT0FBNEI7SUFFNUIsT0FBTyxPQUFPLE9BQU8sSUFBSSxRQUFRO1FBQzdCLE9BQU8sSUFBSSxJQUFJO1FBQ2YsT0FBUSxPQUFlLENBQUMsU0FBUyxJQUFJLFFBQVEsQ0FBQTtBQUNyRCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUsZ0NBQWdDLENBQzVDLE9BQTRCO0lBRTVCLE9BQU8sT0FBTyxPQUFPLElBQUksUUFBUTtRQUM3QixPQUFPLElBQUksSUFBSTtRQUNmLE9BQVEsT0FBZSxDQUFDLFVBQVUsSUFBSSxRQUFRLENBQUE7QUFDdEQsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7Ozs7OztHQWlCRztBQUNILE1BQU0sVUFBVSw2QkFBNkIsQ0FDekMsU0FBaUIsRUFDakIsTUFBa0IsRUFDbEIsTUFBa0IsRUFDbEIsYUFBeUM7SUFFekMsTUFBTSxPQUFPLEdBQUcsTUFBTSxDQUFDLE9BQU8sRUFBcUIsQ0FBQTtJQUNuRCxJQUNJLE9BQU8sT0FBTyxDQUFDLE9BQU8sSUFBSSxRQUFRO1FBQ2xDLE9BQU8sT0FBTyxDQUFDLE1BQU0sSUFBSSxRQUFRO1FBQ2pDLE9BQU8sT0FBTyxDQUFDLElBQUksSUFBSSxRQUFRLEVBQ2pDLENBQUM7UUFDQyxNQUFNLElBQUksS0FBSyxDQUNYLGlDQUFpQztZQUNqQyxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQ25DLENBQUE7SUFDTCxDQUFDO0lBRUQsU0FBUyxDQUFDLFVBQVUsQ0FDaEIsYUFBYSxDQUFDLGlCQUFpQjtRQUMvQixPQUFPLENBQUMsT0FBTyxFQUNmLGFBQWEsQ0FBQyxp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QzdDLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQywyQkFBMkIsQ0FDbkMsU0FBUyxFQUNULE1BQU0sRUFDTixNQUFNLEVBQ04sYUFBYSxDQUNoQixDQUFDLENBQUM7WUFDSCxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUMsNkJBQTZCLENBQ3JDLFNBQVMsRUFDVCxNQUFNLEVBQ04sTUFBTSxFQUNOLGFBQWEsQ0FDaEIsQ0FDUixDQUFBO1FBRUQsTUFBTSx1QkFBdUIsR0FDekIsK0JBQStCLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQztZQUM1QztnQkFDSSxJQUFJLEVBQUUsYUFBYSxDQUFDLFNBQVM7YUFDaEMsQ0FBQyxDQUFDO1lBQ0g7Z0JBQ0ksSUFBSSxFQUFFLGFBQWEsQ0FBQyxTQUFTO2dCQUM3QixJQUFJLEVBQUUsYUFBYSxDQUFDLFNBQVM7YUFDaEMsQ0FBQTtRQUVULE1BQU0sQ0FBQyxNQUFNLENBQUM7WUFDVixHQUFHLGFBQWE7WUFDaEIsR0FBRyx1QkFBdUI7U0FDN0IsQ0FBQyxDQUFBO1FBRUYsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFBO0lBQ2YsQ0FBQyxDQUFDLENBQUE7QUFDTixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IG5ldCwgeyB0eXBlIExpc3Rlbk9wdGlvbnMgYXMgU2VydmVyT3B0aW9ucyB9IGZyb20gXCJuZXRcIjtcbmltcG9ydCB7IENsaWVudCB9IGZyb20gXCJzc2gyXCI7XG5cbmV4cG9ydCB0eXBlIExvY2FsSG9zdFBvcnRFeHRyYU9wdGlvbnMgPVxuICAgIE9taXQ8XG4gICAgICAgIFBhcnRpYWw8U2VydmVyT3B0aW9ucz4sXG4gICAgICAgIFwiaG9zdFwiIHwgXCJwb3J0XCIgfCBcInBhdGhcIlxuICAgID5cblxuZXhwb3J0IHR5cGUgU3NoVHVubmVsT3V0TG9jYWxIb3N0UG9ydCA9IHtcbiAgICBsb2NhbEhvc3Q6IHN0cmluZyxcbiAgICBsb2NhbFBvcnQ6IG51bWJlcixcbn0gJiBMb2NhbEhvc3RQb3J0RXh0cmFPcHRpb25zXG5cbmV4cG9ydCB0eXBlIFNzaFR1bm5lbE91dExvY2FsU29ja2V0ID0ge1xuICAgIGxvY2FsUGF0aDogc3RyaW5nLFxufSAmIExvY2FsSG9zdFBvcnRFeHRyYU9wdGlvbnNcblxuZXhwb3J0IGludGVyZmFjZSBTc2hUdW5uZWxPdXRSZW1vdGVIb3N0UG9ydCB7XG4gICAgcmVtb3RlSG9zdDogc3RyaW5nLFxuICAgIHJlbW90ZVBvcnQ6IG51bWJlcixcbiAgICBmb3J3YXJkU291cmNlSG9zdD86IHN0cmluZ1xuICAgIGZvcndhcmRTb3VyY2VQb3J0PzogbnVtYmVyXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU3NoVHVubmVsT3V0UmVtb3RlU29ja2V0IHtcbiAgICByZW1vdGVQYXRoOiBzdHJpbmcsXG59XG5cbmV4cG9ydCB0eXBlIFNzaFR1bm5lbExvY2FsT3V0T3B0aW9ucyA9XG4gICAgU3NoVHVubmVsT3V0TG9jYWxIb3N0UG9ydCB8XG4gICAgU3NoVHVubmVsT3V0TG9jYWxTb2NrZXRcblxuZXhwb3J0IHR5cGUgU3NoVHVubmVsUmVtb3RlT3V0T3B0aW9ucyA9XG4gICAgU3NoVHVubmVsT3V0UmVtb3RlSG9zdFBvcnQgfFxuICAgIFNzaFR1bm5lbE91dFJlbW90ZVNvY2tldFxuXG5leHBvcnQgdHlwZSBTc2hUdW5uZWxPdXRPcHRpb25zID1cbiAgICBTc2hUdW5uZWxMb2NhbE91dE9wdGlvbnMgJlxuICAgIFNzaFR1bm5lbFJlbW90ZU91dE9wdGlvbnNcblxuLyoqXG4gKiBSZXR1cm5zIHRydWUgaWYgdGhlIGdpdmVuIG9wdGlvbnMgYXJlIGZvciBhIGxvY2FsIHNvY2tldCAoaS5lLiBwYXRoIGlzIHByZXNlbnQpLlxuICpcbiAqIEBwYXJhbSBvcHRpb25zIC0gVGhlIG9wdGlvbnMgdG8gY2hlY2suXG4gKiBAcmV0dXJucyBUcnVlIGlmIHRoZSBvcHRpb25zIGFyZSBmb3IgYSBsb2NhbCBzb2NrZXQsIG90aGVyd2lzZSBmYWxzZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzU3NoVHVubmVsT3V0TG9jYWxTb2NrZXRPcHRpb24oXG4gICAgb3B0aW9uczogU3NoVHVubmVsT3V0T3B0aW9ucyxcbik6IG9wdGlvbnMgaXMgU3NoVHVubmVsT3V0TG9jYWxTb2NrZXQgJiBTc2hUdW5uZWxSZW1vdGVPdXRPcHRpb25zIHtcbiAgICByZXR1cm4gdHlwZW9mIG9wdGlvbnMgPT0gXCJvYmplY3RcIiAmJlxuICAgICAgICBvcHRpb25zICE9IG51bGwgJiZcbiAgICAgICAgdHlwZW9mIChvcHRpb25zIGFzIGFueSkubG9jYWxQYXRoID09IFwic3RyaW5nXCJcbn1cblxuLyoqXG4gKiBSZXR1cm5zIHRydWUgaWYgdGhlIGdpdmVuIG9wdGlvbnMgYXJlIGZvciBhIHJlbW90ZSBzb2NrZXQgKGkuZS4gcmVtb3RlUGF0aCBpcyBwcmVzZW50KS5cbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIHRvIGNoZWNrLlxuICogQHJldHVybnMgVHJ1ZSBpZiB0aGUgb3B0aW9ucyBhcmUgZm9yIGEgcmVtb3RlIHNvY2tldCwgb3RoZXJ3aXNlIGZhbHNlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNTc2hUdW5uZWxPdXRSZW1vdGVTb2NrZXRPcHRpb24oXG4gICAgb3B0aW9uczogU3NoVHVubmVsT3V0T3B0aW9ucyxcbik6IG9wdGlvbnMgaXMgU3NoVHVubmVsTG9jYWxPdXRPcHRpb25zICYgU3NoVHVubmVsT3V0UmVtb3RlU29ja2V0IHtcbiAgICByZXR1cm4gdHlwZW9mIG9wdGlvbnMgPT0gXCJvYmplY3RcIiAmJlxuICAgICAgICBvcHRpb25zICE9IG51bGwgJiZcbiAgICAgICAgdHlwZW9mIChvcHRpb25zIGFzIGFueSkucmVtb3RlUGF0aCA9PSBcInN0cmluZ1wiXG59XG5cbi8qKlxuICogQGV4cGVyaW1lbnRhbCBUaGlzIGZ1bmN0aW9uIGlzIGV4cGVyaW1lbnRhbCBhbmQgbWF5IG5vdCBiZSBzdGFibGUuXG4gKiBUdW5uZWxzIGluY29taW5nIHNlcnZlciBzb2NrZXQgY29ubnRpb25zIHRvIGEgcmVtb3RlIGhvc3QgYW5kIHBvcnQgYmluZCAobm90IGxpbnV4IHNvY2tldCkuXG4gKlxuICogVGhpcyBmdW5jdGlvbiBlc3RhYmxpc2hlcyBhbiBTU0ggdHVubmVsIGJ5IGZvcndhcmRpbmdcbiAqIGEgbG9jYWwgc2VydmVyJ3MgYWRkcmVzcyBhbmQgcG9ydCB0byBhIHNwZWNpZmllZCByZW1vdGVcbiAqIGhvc3QgYW5kIHBvcnQuIEl0IG1hbmFnZXMgdGhlIGNvbm5lY3Rpb24gYW5kIGxpZmVjeWNsZSBvZiBcbiAqIHRoZSB0dW5uZWwsIGVuc3VyaW5nIHRoYXQgcmVzb3VyY2VzIGFyZSBjbGVhbmVkIHVwIHVwb24gXG4gKiBjbG9zdXJlIG9mIHRoZSBzZXJ2ZXIgb3Igc29ja2V0LlxuICpcbiAqIEBwYXJhbSBzc2hDbGllbnQgLSBUaGUgU1NIIGNsaWVudCBpbnN0YW5jZSB1c2VkIHRvIGVzdGFibGlzaCB0aGUgdHVubmVsLlxuICogQHBhcmFtIHNlcnZlciAtIFRoZSBsb2NhbCBzZXJ2ZXIgZm9yIHdoaWNoIHRoZSB0dW5uZWwgaXMgYmVpbmcgY3JlYXRlZC5cbiAqIEBwYXJhbSBzb2NrZXQgLSBUaGUgc29ja2V0IGFzc29jaWF0ZWQgd2l0aCB0aGUgc2VydmVyLlxuICogQHBhcmFtIHR1bm5lbE9wdGlvbnMgLSBPcHRpb25zIHNwZWNpZnlpbmcgcmVtb3RlIGhvc3QgYW5kIHBvcnQgZGV0YWlscyBcbiAqICAgICAgICAgICAgICAgICAgICAgICAgYW5kIG9wdGlvbmFsIGZvcndhcmRpbmcgc291cmNlIGhvc3QgYW5kIHBvcnQuXG4gKlxuICogQHRocm93cyB7RXJyb3J9IElmIHRoZSBzZXJ2ZXIncyBzb2NrZXQgYWRkcmVzcyBpcyBpbnZhbGlkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gaGFuZGxlUmVtb3RlSG9zdFBvcnRPdXRUdW5uZWwoXG4gICAgc3NoQ2xpZW50OiBDbGllbnQsXG4gICAgc2VydmVyOiBuZXQuU2VydmVyLFxuICAgIHNvY2tldDogbmV0LlNvY2tldCxcbiAgICB0dW5uZWxPcHRpb25zOiBTc2hUdW5uZWxPdXRSZW1vdGVIb3N0UG9ydFxuKSB7XG4gICAgY29uc3QgYWRkcmVzcyA9IHNlcnZlci5hZGRyZXNzKCkgYXMgbmV0LkFkZHJlc3NJbmZvXG4gICAgaWYgKFxuICAgICAgICB0eXBlb2YgYWRkcmVzcy5hZGRyZXNzICE9IFwic3RyaW5nXCIgfHxcbiAgICAgICAgdHlwZW9mIGFkZHJlc3MuZmFtaWx5ICE9IFwic3RyaW5nXCIgfHxcbiAgICAgICAgdHlwZW9mIGFkZHJlc3MucG9ydCAhPSBcIm51bWJlclwiXG4gICAgKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgIFwiSW52YWxpZCBzZXJ2ZXIgc29ja2V0IGFkZHJlc3M6IFwiICtcbiAgICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGFkZHJlc3MsIG51bGwsIDIpXG4gICAgICAgIClcbiAgICB9XG5cbiAgICBzc2hDbGllbnQuZm9yd2FyZE91dChcbiAgICAgICAgdHVubmVsT3B0aW9ucy5mb3J3YXJkU291cmNlSG9zdCA/P1xuICAgICAgICBhZGRyZXNzLmFkZHJlc3MsXG4gICAgICAgIHR1bm5lbE9wdGlvbnMuZm9yd2FyZFNvdXJjZVBvcnQgPz9cbiAgICAgICAgYWRkcmVzcy5wb3J0LFxuICAgICAgICB0dW5uZWxPcHRpb25zLnJlbW90ZUhvc3QsXG4gICAgICAgIHR1bm5lbE9wdGlvbnMucmVtb3RlUG9ydCwgKGVyciwgY2xpZW50Q2hhbm5lbCkgPT4ge1xuICAgICAgICAgICAgaWYgKGVycikge1xuICAgICAgICAgICAgICAgIHNlcnZlci5lbWl0KFwiZXJyb3JcIiwgZXJyKVxuICAgICAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBzZXJ2ZXIub24oJ2Nsb3NlJywgKCkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICghY2xpZW50Q2hhbm5lbC5jbG9zZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgY2xpZW50Q2hhbm5lbC5lbmQoKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBpZiAoIXNvY2tldC5jbG9zZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgc29ja2V0LmVuZCgpXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIHNvY2tldC5waXBlKGNsaWVudENoYW5uZWwpLnBpcGUoc29ja2V0KVxuICAgICAgICB9KVxufVxuXG4vKipcbiAqIEBleHBlcmltZW50YWwgVGhpcyBmdW5jdGlvbiBpcyBleHBlcmltZW50YWwgYW5kIG1heSBub3QgYmUgc3RhYmxlLlxuICogVHVubmVscyBpbmNvbWluZyBzZXJ2ZXIgc29ja2V0IGNvbm50aW9ucyB0byBhIHJlbW90ZSBzb2NrZXQgKG5vdCBhZGRyZXNzIGFuZCBwb3J0IGJpbmQpIHVzaW5nIG9wZW5zc2guXG4gKlxuICogVGhpcyBmdW5jdGlvbiBlc3RhYmxpc2hlcyBhbiBTU0ggdHVubmVsIGJ5IGZvcndhcmRpbmdcbiAqIGEgbG9jYWwgc2VydmVyJ3MgYWRkcmVzcyBhbmQgcG9ydCB0byBhIHNwZWNpZmllZCByZW1vdGVcbiAqIHNvY2tldCBwYXRoLiBJdCBtYW5hZ2VzIHRoZSBjb25uZWN0aW9uIGFuZCBsaWZlY3ljbGUgb2YgXG4gKiB0aGUgdHVubmVsLCBlbnN1cmluZyB0aGF0IHJlc291cmNlcyBhcmUgY2xlYW5lZCB1cCB1cG9uIFxuICogY2xvc3VyZSBvZiB0aGUgc2VydmVyIG9yIHNvY2tldC5cbiAqXG4gKiBAcGFyYW0gc3NoQ2xpZW50IC0gVGhlIFNTSCBjbGllbnQgaW5zdGFuY2UgdXNlZCB0byBlc3RhYmxpc2ggdGhlIHR1bm5lbC5cbiAqIEBwYXJhbSBzZXJ2ZXIgLSBUaGUgbG9jYWwgc2VydmVyIGZvciB3aGljaCB0aGUgdHVubmVsIGlzIGJlaW5nIGNyZWF0ZWQuXG4gKiBAcGFyYW0gc29ja2V0IC0gVGhlIHNvY2tldCBhc3NvY2lhdGVkIHdpdGggdGhlIHNlcnZlci5cbiAqIEBwYXJhbSB0dW5uZWxPcHRpb25zIC0gT3B0aW9ucyBzcGVjaWZ5aW5nIHJlbW90ZSBzb2NrZXQgcGF0aCBkZXRhaWxzIFxuICogICAgICAgICAgICAgICAgICAgICAgICBhbmQgb3B0aW9uYWwgZm9yd2FyZGluZyBzb3VyY2UgaG9zdCBhbmQgcG9ydC5cbiAqXG4gKiBAdGhyb3dzIHtFcnJvcn0gSWYgdGhlIHNlcnZlcidzIHNvY2tldCBhZGRyZXNzIGlzIGludmFsaWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBoYW5kbGVSZW1vdGVTb2NrZXRPdXRUdW5uZWwoXG4gICAgc3NoQ2xpZW50OiBDbGllbnQsXG4gICAgc2VydmVyOiBuZXQuU2VydmVyLFxuICAgIHNvY2tldDogbmV0LlNvY2tldCxcbiAgICB0dW5uZWxPcHRpb25zOiBTc2hUdW5uZWxPdXRSZW1vdGVTb2NrZXRcbikge1xuICAgIGNvbnN0IGFkZHJlc3MgPSBzZXJ2ZXIuYWRkcmVzcygpIGFzIG5ldC5BZGRyZXNzSW5mb1xuICAgIGlmIChcbiAgICAgICAgdHlwZW9mIGFkZHJlc3MuYWRkcmVzcyAhPSBcInN0cmluZ1wiIHx8XG4gICAgICAgIHR5cGVvZiBhZGRyZXNzLmZhbWlseSAhPSBcInN0cmluZ1wiIHx8XG4gICAgICAgIHR5cGVvZiBhZGRyZXNzLnBvcnQgIT0gXCJudW1iZXJcIlxuICAgICkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICBcIkludmFsaWQgc2VydmVyIHNvY2tldCBhZGRyZXNzOiBcIiArXG4gICAgICAgICAgICBKU09OLnN0cmluZ2lmeShhZGRyZXNzLCBudWxsLCAyKVxuICAgICAgICApXG4gICAgfVxuXG4gICAgc3NoQ2xpZW50Lm9wZW5zc2hfZm9yd2FyZE91dFN0cmVhbUxvY2FsKFxuICAgICAgICB0dW5uZWxPcHRpb25zLnJlbW90ZVBhdGgsXG4gICAgICAgIChlcnIsIGNsaWVudENoYW5uZWwpID0+IHtcbiAgICAgICAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgICAgICAgICBzZXJ2ZXIuZW1pdChcImVycm9yXCIsIGVycilcbiAgICAgICAgICAgICAgICByZXR1cm5cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgc2VydmVyLm9uKCdjbG9zZScsICgpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAoIWNsaWVudENoYW5uZWwuY2xvc2VkKSB7XG4gICAgICAgICAgICAgICAgICAgIGNsaWVudENoYW5uZWwuZW5kKClcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKCFzb2NrZXQuY2xvc2VkKSB7XG4gICAgICAgICAgICAgICAgICAgIHNvY2tldC5lbmQoKVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgICBzb2NrZXQucGlwZShjbGllbnRDaGFubmVsKS5waXBlKHNvY2tldClcbiAgICAgICAgfVxuICAgIClcbn1cblxuLyoqXG4gKiBAZXhwZXJpbWVudGFsIFRoaXMgZnVuY3Rpb24gaXMgZXhwZXJpbWVudGFsIGFuZCBtYXkgbm90IGJlIHN0YWJsZS5cbiAqIENyZWF0ZXMgYSBsb2NhbCBzZXJ2ZXIgdGhhdCB0dW5uZWxzIGluY29taW5nIGNvbm5lY3Rpb25zIHRvIGEgcmVtb3RlIGxpbnV4IHNvY2tldCBvciBob3N0IGFuZCBwb3J0IGJpbmQuXG4gKlxuICogWW91IG5lZWQgdG8gY2xvc2UgdGhlIHNlcnZlciB0byBzdG9wIHR1bm5lbGluZyFcbiAqIFxuICogVGhpcyBmdW5jdGlvbiBjcmVhdGVzIGEgc2VydmVyIHRoYXQgbGlzdGVucyBmb3IgaW5jb21pbmcgY29ubmVjdGlvbnMgYW5kIGZvcndhcmRzIHRoZW0gdG8gdGhlIHJlbW90ZSBTU0ggaG9zdC4gXG4gKiBcbiAqIEBwYXJhbSBzc2hDbGllbnQgLSBUaGUgU1NIIGNsaWVudCBpbnN0YW5jZSB1c2VkIHRvIGVzdGFibGlzaCB0aGUgdHVubmVsLlxuICogQHBhcmFtIHR1bm5lbE9wdGlvbnMgLSBPcHRpb25zIHNwZWNpZnlpbmcgcmVtb3RlIGxpbnV4IHNvY2tldCBvciBob3N0IGFuZCBwb3J0IGRldGFpbHMuXG4gKiBcbiAqIEByZXR1cm5zIEEgcHJvbWlzZSB0aGF0IHJlc29sdmVzIHRvIHRoZSBjcmVhdGVkIHNlcnZlciB0aGF0IG5lZWQgdG8gYmUgY2xvc2VkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdHVubmVsT3V0KFxuICAgIHNzaENsaWVudDogQ2xpZW50LFxuICAgIHR1bm5lbE9wdGlvbnM6IFNzaFR1bm5lbE91dE9wdGlvbnMsXG4pOiBQcm9taXNlPG5ldC5TZXJ2ZXI+IHtcbiAgICByZXR1cm4gbmV3IFByb21pc2U8bmV0LlNlcnZlcj4oKHJlcywgcmVqKSA9PiB7XG4gICAgICAgIGxldCBzZXJ2ZXI6IG5ldC5TZXJ2ZXJcblxuICAgICAgICBzZXJ2ZXIgPSBuZXQuY3JlYXRlU2VydmVyKClcbiAgICAgICAgc2VydmVyLm9uKCdlcnJvcicsIChlcnIpID0+IHtcbiAgICAgICAgICAgIHNlcnZlci5jbG9zZSgpXG4gICAgICAgICAgICByZWooZXJyKVxuICAgICAgICB9KVxuXG4gICAgICAgIHNlcnZlci5vbignY29ubmVjdGlvbicsXG4gICAgICAgICAgICBpc1NzaFR1bm5lbE91dFJlbW90ZVNvY2tldE9wdGlvbih0dW5uZWxPcHRpb25zKSA/XG4gICAgICAgICAgICAgICAgKHNvY2tldCkgPT4gaGFuZGxlUmVtb3RlU29ja2V0T3V0VHVubmVsKFxuICAgICAgICAgICAgICAgICAgICBzc2hDbGllbnQsXG4gICAgICAgICAgICAgICAgICAgIHNlcnZlcixcbiAgICAgICAgICAgICAgICAgICAgc29ja2V0LFxuICAgICAgICAgICAgICAgICAgICB0dW5uZWxPcHRpb25zLFxuICAgICAgICAgICAgICAgICkgOlxuICAgICAgICAgICAgICAgIChzb2NrZXQpID0+IGhhbmRsZVJlbW90ZUhvc3RQb3J0T3V0VHVubmVsKFxuICAgICAgICAgICAgICAgICAgICBzc2hDbGllbnQsXG4gICAgICAgICAgICAgICAgICAgIHNlcnZlcixcbiAgICAgICAgICAgICAgICAgICAgc29ja2V0LFxuICAgICAgICAgICAgICAgICAgICB0dW5uZWxPcHRpb25zLFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgKVxuXG4gICAgICAgIGNvbnN0IGJhc2VUdW5uZWxTZXJ2ZXJPcHRpb25zID1cbiAgICAgICAgICAgIGlzU3NoVHVubmVsT3V0TG9jYWxTb2NrZXRPcHRpb24odHVubmVsT3B0aW9ucykgP1xuICAgICAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICAgICAgcGF0aDogdHVubmVsT3B0aW9ucy5sb2NhbFBhdGgsXG4gICAgICAgICAgICAgICAgfSA6XG4gICAgICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgICAgICBob3N0OiB0dW5uZWxPcHRpb25zLmxvY2FsSG9zdCxcbiAgICAgICAgICAgICAgICAgICAgcG9ydDogdHVubmVsT3B0aW9ucy5sb2NhbFBvcnQsXG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgIHNlcnZlci5saXN0ZW4oe1xuICAgICAgICAgICAgLi4udHVubmVsT3B0aW9ucyxcbiAgICAgICAgICAgIC4uLmJhc2VUdW5uZWxTZXJ2ZXJPcHRpb25zLFxuICAgICAgICB9KVxuXG4gICAgICAgIHJlcyhzZXJ2ZXIpXG4gICAgfSlcbn1cbiJdfQ==