Skip to content

Instantly share code, notes, and snippets.

@Jimmy-Z
Created December 5, 2019 04:09
Show Gist options
  • Save Jimmy-Z/a2f3ef239932110b0febd82c1c5f66f7 to your computer and use it in GitHub Desktop.
Save Jimmy-Z/a2f3ef239932110b0febd82c1c5f66f7 to your computer and use it in GitHub Desktop.
bridge msys2/cygwin openssh to win32-openssh's ssh-agent, in node.js
/* fucking ssh-agent, again
we have at least 4 incompatible ssh-agents on windows
there're numerous bridging tools between them but for the love of God I couldn't find the one I need
this thing bridges msys2/cygwin openssh to win32-openssh's ssh-agent
probably the crudest thing you've ever seen, no error handling what-so-ever
if in any rare case you need this too, run this and:
`export SSH_AUTH_SOCK=$LOCALAPPDATA\\ssh-agent-sock`
*/
"use strict";
const net = require("net");
const crypto = require("crypto");
const path = require("path");
const fs = require("fs");
const child_process = require("child_process");
const SOCK_FILE_NAME = path.join(process.env.LOCALAPPDATA, "ssh-agent-sock");
const LO = "127.0.0.1";
const S_ID = 0;
const S_PID = 1;
// cygwin (emulated) unix domain socket
// https://stackoverflow.com/questions/23086038/what-mechanism-is-used-by-msys-cygwin-to-emulate-unix-domain-sockets
function c2w(id){
return net.createServer(c => {
console.log(`new connection from ${c.remoteAddress}:${c.remotePort}`);
let state = S_ID;
c.on("data", data => {
console.log(`received ${data.length} bytes from ${c.remoteAddress}:${c.remotePort}`);
// this is not a correct way of handling tcp data,
if(state === S_ID && data.length === 16 && id.compare(data) === 0){
console.log("\tgot correct id");
state = S_PID;
c.write(data);
}else if(state === S_PID && data.length === 12){
const pid = data.readUInt32LE(0);
const uid = data.readUInt32LE(4);
const gid = data.readUInt32LE(8);
console.log(`\tgot pid=${pid}, uid=${uid}, gid=${gid}`);
data.writeUInt32LE(process.pid, 0);
c.removeAllListeners("data");
c.write(data);
const upstream = net.connect("\\\\?\\pipe\\openssh-ssh-agent");
upstream.on("connect", () => {
console.log("\tconnected to upstream named pipe");
upstream.pipe(c);
c.pipe(upstream);
});
}else{
console.error("\tunexpected data");
c.end();
}
});
c.on("close", () => {
console.log(`connection from ${c.remoteAddress}:${c.remotePort} closed`);
});
});
}
// entry point
// I couldn't find a way to reliably do something(delete the socket desc file) on exit
fs.exists(SOCK_FILE_NAME, exists => {
if(exists){
const sock_desc = parse_cygwin_unix_domain_socket_desc(fs.readFileSync(SOCK_FILE_NAME, {encoding: "ascii"}));
if(sock_desc === null){
throw new Error("invalid socket file");
}else{
const d = c2w(sock_desc.id);
d.on("error", (e) => {
if(e.code === "EADDRINUSE"){
console.log("socket address in use, presumably there is another instance running, will now quit");
}
});
d.listen(sock_desc.port, LO, () => {
// or should we start on a new port with a new id instead?
console.log(`reusing previous socket file, listening on ${sock_desc.port}`);
});
}
}else{
const id = crypto.randomBytes(16);
const d = c2w(id);
d.listen(0, LO, () => {
const port = d.address().port;
console.log(`listening on ${port}`);
fs.writeFile(SOCK_FILE_NAME,
generate_cygwin_unix_domain_socket_desc(port, id),
{encoding: "ascii"},
() => {
child_process.exec(`attrib +s \"${SOCK_FILE_NAME}\"`);
});
});
}
});
// helper functions
function parse_cygwin_unix_domain_socket_desc(s){
const m = /^!<socket >(\d+) s ([0-9a-f]{8})-([0-9a-f]{8})-([0-9a-f]{8})-([0-9a-f]{8})/i.exec(s);
if (m === null){
return null;
}else{
const id = Buffer.alloc(16);
[0, 4, 8, 12].forEach((offset, index) => {
id.writeUInt32LE(parseInt(m[2 + index], 16), offset);
});
return { port: parseInt(m[1]), id: id };
}
}
function generate_cygwin_unix_domain_socket_desc(port, id){
const id_str = [0, 4, 8, 12].map(o => {
const s = id.readUInt32LE(o).toString(16);
return "00000000".slice(s.length).concat(s);
}).join("-").toUpperCase()
return `!<socket >${port} s ${id_str}\0`;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment