node-ssh-tunnel
Version:
Node-SSH-Tunnel
2 lines (1 loc) • 6.48 kB
JavaScript
!function(e,o){"object"==typeof exports&&"undefined"!=typeof module?o(exports,require("net"),require("ssh2")):"function"==typeof define&&define.amd?define(["exports","net","ssh2"],o):o((e="undefined"!=typeof globalThis?globalThis:e||self).NodeSshTunnel={},e.net,e.ssh2)}(this,(function(e,o,n){"use strict";const t=async e=>{let n=Object.assign({},e);return n.port||n.host||(n=null),new Promise(((e,t)=>{let s=o.createServer(),r=e=>{t(e)};s.on("error",r),process.on("uncaughtException",r),s.listen(n),s.on("listening",(()=>{process.removeListener("uncaughtException",r),e(s)}))}))},s=async e=>new Promise(((o,t)=>{let s=new n.Client;s.on("ready",(()=>{s.removeListener("error",t),o(s)})),s.on("error",(e=>{t(e)})),s.connect(e)}));let r=!1;const c=async e=>(r=!0,new Promise((async(o,n)=>{try{console.log("ReCreateSSHConnection");const n=await s(e);r=!1,o(n)}catch(n){setTimeout((()=>{o(c(e))}),1e3)}})));class l extends Error{code;constructor(e,o){super(e),this.code=o}}class i{connection;sshOptions;servers;reconnecting=!1;constructor(){this.connection=null,this.sshOptions=null,this.servers=null}getConnection(){const{connection:e}=this;if(null===e)throw new Error("Not connected to server");return e}isConnected(){return null!=this.connection}async connect(e){const o=Object.assign({port:22,username:"root"},e);this.sshOptions=o;const t=new n.Client;return await new Promise(((e,n)=>{t.on("error",n),t.on("ready",(()=>{this.connection=t,t.removeListener("error",n),e(!0)})),t.on("end",(()=>{this.connection===t&&(this.connection=null)})),t.on("close",(()=>{this.connection===t&&(this.connection=null),n(new l("No response from server","ETIMEDOUT"))})),t.connect(o)})),this}async reConnect(){return this.reconnecting=!0,new Promise((async(e,o)=>{try{console.log("ReCreateSSHConnection");const o=await s(this.sshOptions);this.reconnecting=!1,e(o)}catch(o){setTimeout((()=>{e(c(this.sshOptions))}),1e3)}}))}async disconnect(){const{connection:e}=this;if(null!==e){try{e.removeAllListeners(),e.end(),e.destroy()}catch(e){console.log(e)}this.connection=null}}async close(){this.disconnect(),this.closeTunnel()}async closeTunnel(){null!==this.servers&&(this.servers.forEach((e=>{if(e)try{e.close()}catch(e){console.log(e)}})),this.servers=null)}async createTunnel(e,o){if(null===this.connection)throw new Error("Not connected to server");if(null!==this.servers)throw new Error("Tunnel already created");const n=()=>{this.connection?.on("error",(async e=>{this.connection?.removeAllListeners(),this.connection=null,console.log("sshConnection","error"),this.reconnecting||(r.reconnectOnError?(console.log("ReconnectOnError","start reconnect"),this.connection=await this.reConnect(),n(),console.log("sshConnection","reconnected")):console.log("Error",e))})),this.connection?.on("close",(async()=>{this.connection?.removeAllListeners(),this.connection=null,console.log("sshConnection","close"),this.reconnecting||(r.reconnectOnError?(console.log("ReconnectOnClose","start reconnect"),this.connection=await this.reConnect(),n(),console.log("sshConnection","reconnected")):console.log("close"))}))};n();const s=(Array.isArray(e)?e:[e]).map((e=>Object.assign({dstAddr:"127.0.0.1",srcAddr:"0.0.0.0"},e))),r=Object.assign({autoClose:!1,reconnectOnError:!0},o||{});this.servers=await Promise.all(s.map((async e=>{const o={host:e.srcAddr,port:e.srcPort};let n;const s=(o,n=0)=>{if(null!==this.getConnection()){const n=this.getConnection();try{n.forwardOut(e.srcAddr,e.srcPort,e.dstAddr,e.dstPort,((e,n)=>{if(e){console.log(e.message),o.on("close",(()=>{})),o.on("error",(()=>{}));try{o.end(),o.destroy()}catch(e){console.log(e)}}else o.on("close",(()=>{n.end()})),o.on("error",(()=>{n.end()})),o.pipe(n).pipe(o)}))}catch(e){o.on("close",(()=>{})),o.on("error",(()=>{}));try{o.end(),o.destroy()}catch(e){console.log(e)}}}else if(n<20)setTimeout((()=>{s(o,n+1)}),500);else try{o.end(),o.destroy()}catch(e){console.log(e)}};try{return n=await t(o),console.log("create tunel success: ",`${e.srcAddr}:${e.srcPort} => ${this.sshOptions?.host}:${e.dstPort}`),n.on("connection",s),n}catch(e){return void console.log(e)}})))}}const a=async(e,o,n)=>{const l=Object.assign({port:22,username:"root"},e),i=(Array.isArray(o)?o:[o]).map((e=>Object.assign({dstAddr:"127.0.0.1",srcAddr:"0.0.0.0"},e))),a=Object.assign({autoClose:!1,reconnectOnError:!0},n||{});let d;const h=()=>{d?.on("error",(async e=>{d?.removeAllListeners(),d=void 0,console.log("sshConnection","error"),r||(a.reconnectOnError?(console.log("ReconnectOnError","start reconnect"),d=await c(l),h(),console.log("sshConnection","reconnected")):console.log("Error",e))})),d?.on("close",(async()=>{d?.removeAllListeners(),d=void 0,console.log("sshConnection","close"),r||(a.reconnectOnError?(console.log("ReconnectOnClose","start reconnect"),d=await c(l),h(),console.log("sshConnection","reconnected")):console.log("close"))}))};try{d=await s(l),h()}catch(e){return Promise.reject("用户名或密码错误, 请检查你的配置信息")}const u=await Promise.all(i.map((async o=>{const n={host:o.srcAddr,port:o.srcPort};let s;const r=l=>{a.reconnectOnError&&l.on("error",(async()=>{s=await t(n),r(s)})),l.on("connection",c),l.on("close",(()=>{console.log("close tunel: ",`${o.srcAddr}:${o.srcPort} => ${e.host}:${o.dstPort}`)}))},c=(e,n=0)=>{if(a.autoClose&&((e,o)=>{o.on("close",(()=>{e.getConnections(((o,n)=>{0===n&&e.close()}))}))})(s,e),d)try{d.forwardOut(e.remoteAddress??o.srcAddr,e.remotePort??o.srcPort,o.dstAddr,o.dstPort,((o,n)=>{if(o){console.log(o.message),e.on("close",(()=>{})),e.on("error",(()=>{}));try{e.end(),e.destroy()}catch(e){console.log(e)}}else e.on("close",(()=>{n.end()})),e.on("error",(()=>{n.end()})),e.pipe(n).pipe(e)}))}catch(o){e.on("close",(()=>{})),e.on("error",(()=>{}));try{e.end(),e.destroy()}catch(e){console.log(e)}}else if(n<20)setTimeout((()=>{c(e,n+1)}),500);else{e.on("close",(()=>{})),e.on("error",(()=>{}));try{e.end(),e.destroy()}catch(e){console.log(e)}}};try{return s=await t(n),r(s),console.log("create tunel success: ",`${o.srcAddr}:${o.srcPort} => ${e.host}:${o.dstPort}`),s}catch(e){return void console.log(e)}})));return{servers:u,sshConnection:d,close:()=>{if(u.forEach((e=>{if(e)try{e.close()}catch(e){console.log(e)}})),d)try{d.end(),d.destroy()}catch(e){console.log(e)}}}};e.NodeSSHTunnel=i,e.SSHError=l,e.createTunnel=a,e.createTunnelEx=async(e,o,n)=>{const t=new i;return await t.connect(e),await t.createTunnel(o,n),t},e.default=a,Object.defineProperty(e,"__esModule",{value:!0})}));