UNPKG

node-ssh-tunnel

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