Skip to content

Commit

Permalink
add support for SOCKS5 proxy and add fix for Juniper/issues/34
Browse files Browse the repository at this point in the history
  • Loading branch information
emasean committed May 11, 2018
1 parent c7fa29a commit 06571de
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 0 deletions.
20 changes: 20 additions & 0 deletions netconf/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,26 @@ type RPCReply struct {
RawReply string `xml:"-"`
}

func (r *RPCReply) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
// make a new type to prevent recurision, but be exported to make
// encoding/xml happy.
type Reply RPCReply
reply := struct {
Reply
Ok *struct{} `xml:"ok"`
}{}

err := d.DecodeElement(&reply, &start)
if err != nil {
return err
}

*r = (RPCReply)(reply.Reply)
// If `ok` doesn't exist it will be nil
r.Ok = reply.Ok != nil
return nil
}

// RPCError defines an error reply to a RPC request
type RPCError struct {
Type string `xml:"error-type"`
Expand Down
57 changes: 57 additions & 0 deletions netconf/transport_ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
"golang.org/x/net/proxy"
)

const (
Expand Down Expand Up @@ -75,6 +76,51 @@ func (t *TransportSSH) Dial(target string, config *ssh.ClientConfig) error {
return nil
}

// DialWithSOCKS5Proxy connects and establishes SSH sessions
//
// proxyAddress must specify a port with the following format <host>:<port> (e.g 172.16.1.1:8080)
//
// auth takes a proxy.Auth which contains username and password
//
// target can be an IP address (e.g.) 172.16.1.1 which utlizes the default
// NETCONF over SSH port of 830. Target can also specify a port with the
// following format <host>:<port> (e.g 172.16.1.1:22)
//
// config takes a ssh.ClientConfig connection. See documentation for
// go.crypto/ssh for documenation. There is a helper function SSHConfigPassword
// thar returns a ssh.ClientConfig for simple username/password authentication
func (t *TransportSSH) DialWithSOCKS5Proxy(proxyAddress string, auth *proxy.Auth, target string, config *ssh.ClientConfig) error {
if !strings.Contains(target, ":") {
target = fmt.Sprintf("%s:%d", target, sshDefaultPort)
}

var err error

dialer, err := proxy.SOCKS5("tcp", proxyAddress, auth, proxy.Direct)
if err != nil {
return nil, err
}

conn, err := dialer.Dial("tcp", target)
if err != nil {
return nil, err
}

c, chans, reqs, err := ssh.NewClientConn(conn, target, config)
if err != nil {
return nil, err
}

t.sshClient = ssh.NewClient(c, chans, reqs)

err = t.setupSession()
if err != nil {
return err
}

return nil
}

func (t *TransportSSH) setupSession() error {
var err error

Expand Down Expand Up @@ -152,6 +198,17 @@ func DialSSHTimeout(target string, config *ssh.ClientConfig, timeout time.Durati
return NewSession(t), nil
}

// DialSSH creates a new NETCONF session using a SSH Transport.
// See TransportSSH.Dial for arguments.
func DialSSHWithSOCKS5Proxy(proxy, auth *proxy.Auth, target string, config *ssh.ClientConfig) (*Session, error) {
var t TransportSSH
err := t.DialWithSOCKS5Proxy(proxy, auth, target, config)
if err != nil {
return nil, err
}
return NewSession(&t), nil
}

// SSHConfigPassword is a convenience function that takes a username and password
// and returns a new ssh.ClientConfig setup to pass that username and password.
// Convenience means that HostKey checks are disabled so it's probably less secure
Expand Down

0 comments on commit 06571de

Please sign in to comment.