Last active
April 26, 2024 07:34
-
-
Save brianm/4741956 to your computer and use it in GitHub Desktop.
Go net/rpc over ssh+netcat and unix domain sockets It would be nice if ssh.Session implemented io.ReaderWriter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local_client | |
server | |
client | |
ext |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"code.google.com/p/go.crypto/ssh" | |
"fmt" | |
"io" | |
"log" | |
"net" | |
"net/rpc" | |
"os" | |
"strings" | |
) | |
// RPC response container | |
type Response struct { | |
Greeting string | |
} | |
// RPC request container | |
type Request struct { | |
Name string | |
} | |
// It would be nice if ssh.Session was an io.ReaderWriter | |
// proposal submitted :-) | |
type NetCatSession struct { | |
*ssh.Session // define Close() | |
writer io.Writer | |
reader io.Reader | |
} | |
// io.Reader | |
func (s NetCatSession) Read(p []byte) (n int, err error) { | |
return s.reader.Read(p) | |
} | |
// io.Writer | |
func (s NetCatSession) Write(p []byte) (n int, err error) { | |
return s.writer.Write(p) | |
} | |
// given the established ssh connection, start a session against netcat and | |
// return a io.ReaderWriterCloser appropriate for rpc.NewClient(...) | |
func StartNetCat(client *ssh.ClientConn, path string) (rwc *NetCatSession, err error) { | |
session, err := client.NewSession() | |
if err != nil { | |
return | |
} | |
cmd := fmt.Sprintf("/usr/bin/nc -U %s", path) | |
in, err := session.StdinPipe() | |
if err != nil { | |
return nil, fmt.Errorf("unable to get stdin: %s", err) | |
} | |
out, err := session.StdoutPipe() | |
if err != nil { | |
return nil, fmt.Errorf("unable to get stdout: %s", err) | |
} | |
err = session.Start(cmd) | |
if err != nil { | |
return nil, fmt.Errorf("unable to start '%s': %s", cmd, err) | |
} | |
return &NetCatSession{session, in, out}, nil | |
} | |
// ./client localhost:/tmp/foo Brian | |
func main() { | |
parts := strings.Split(os.Args[1], ":") | |
host := parts[0] | |
path := parts[1] | |
name := os.Args[2] | |
// SSH setup, we assume current username and use the ssh agent | |
// for auth | |
agent_sock, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")) | |
if err != nil { | |
log.Fatalf("sorry, this example requires the ssh agent: %s", err) | |
} | |
defer agent_sock.Close() | |
config := &ssh.ClientConfig{ | |
User: os.Getenv("USER"), | |
Auth: []ssh.ClientAuth{ | |
ssh.ClientAuthAgent(ssh.NewAgentClient(agent_sock)), | |
}, | |
} | |
ssh_client, err := ssh.Dial("tcp", fmt.Sprintf("%s:22", host), config) | |
if err != nil { | |
panic("Failed to dial: " + err.Error()) | |
} | |
defer ssh_client.Close() | |
// Establish sesstion to netcat talking to the domain socket | |
s, err := StartNetCat(ssh_client, path) | |
if err != nil { | |
log.Fatalf("unable to start netcat session: %s", err) | |
} | |
// now comes the RPC! | |
client := rpc.NewClient(s) | |
defer client.Close() | |
req := &Request{name} | |
var res Response | |
err = client.Call("Greeter.Greet", req, &res) | |
if err != nil { | |
log.Fatalf("error in rpc: %s", err) | |
} | |
fmt.Println(res.Greeting) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"net/rpc" | |
"os" | |
) | |
type Response struct { | |
Greeting string | |
} | |
type Request struct { | |
Name string | |
} | |
// ./local_client /tmp/foo Brian | |
func main() { | |
client, err := rpc.Dial("unix", os.Args[1]) | |
if err != nil { | |
log.Fatalf("failed: %s", err) | |
} | |
req := &Request{os.Args[2]} | |
var res Response | |
err = client.Call("Greeter.Greet", req, &res) | |
if err != nil { | |
log.Fatalf("error in rpc: %s", err) | |
} | |
fmt.Println(res.Greeting) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
build: setup | |
GOPATH=$(PWD):$(PWD)/ext go build server.go | |
GOPATH=$(PWD):$(PWD)/ext go build client.go | |
GOPATH=$(PWD):$(PWD)/ext go build local_client.go | |
fmt: | |
GOPATH=$(PWD) go fmt *.go | |
clean: | |
rm -rf server client local_client ext/pkg | |
setup: | |
GOPATH=$(PWD)/ext go get code.google.com/p/go.crypto/ssh | |
godoc: | |
GOPATH=$(PWD):$(PWD)/ext godoc -http=:6060 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log" | |
"net" | |
"net/rpc" | |
"os" | |
"os/signal" | |
"syscall" | |
) | |
// rpc response | |
type Response struct { | |
Greeting string | |
} | |
// rpc request | |
type Request struct { | |
Name string | |
} | |
// rpc host struct thing | |
type Greeter struct{} | |
// our remotely invocable function | |
func (g *Greeter) Greet(req Request, res *Response) (err error) { | |
res.Greeting = fmt.Sprintf("Hello %s", req.Name) | |
return | |
} | |
// start up rpc listener at path | |
func ServeAt(path string) (err error) { | |
rpc.Register(&Greeter{}) | |
listener, err := net.Listen("unix", path) | |
if err != nil { | |
return fmt.Errorf("unable to listen at %s: %s", path, err) | |
} | |
go rpc.Accept(listener) | |
return | |
} | |
// ./server /tmp/foo | |
func main() { | |
path := os.Args[1] | |
err := ServeAt(path) | |
if err != nil { | |
log.Fatalf("failed: %s", err) | |
} | |
defer os.Remove(path) | |
// block until we are signalled to quit | |
wait() | |
} | |
func wait() { | |
signals := make(chan os.Signal) | |
signal.Notify(signals, syscall.SIGINT, syscall.SIGKILL, syscall.SIGHUP) | |
<-signals | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment