1package ssh 2 3import ( 4 "errors" 5 "io" 6 "net" 7) 8 9// streamLocalChannelOpenDirectMsg is a struct used for SSH_MSG_CHANNEL_OPEN message 10// with "direct-streamlocal@openssh.com" string. 11// 12// See openssh-portable/PROTOCOL, section 2.4. connection: Unix domain socket forwarding 13// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL#L235 14type streamLocalChannelOpenDirectMsg struct { 15 socketPath string 16 reserved0 string 17 reserved1 uint32 18} 19 20// forwardedStreamLocalPayload is a struct used for SSH_MSG_CHANNEL_OPEN message 21// with "forwarded-streamlocal@openssh.com" string. 22type forwardedStreamLocalPayload struct { 23 SocketPath string 24 Reserved0 string 25} 26 27// streamLocalChannelForwardMsg is a struct used for SSH2_MSG_GLOBAL_REQUEST message 28// with "streamlocal-forward@openssh.com"/"cancel-streamlocal-forward@openssh.com" string. 29type streamLocalChannelForwardMsg struct { 30 socketPath string 31} 32 33// ListenUnix is similar to ListenTCP but uses a Unix domain socket. 34func (c *Client) ListenUnix(socketPath string) (net.Listener, error) { 35 m := streamLocalChannelForwardMsg{ 36 socketPath, 37 } 38 // send message 39 ok, _, err := c.SendRequest("streamlocal-forward@openssh.com", true, Marshal(&m)) 40 if err != nil { 41 return nil, err 42 } 43 if !ok { 44 return nil, errors.New("ssh: streamlocal-forward@openssh.com request denied by peer") 45 } 46 ch := c.forwards.add(&net.UnixAddr{Name: socketPath, Net: "unix"}) 47 48 return &unixListener{socketPath, c, ch}, nil 49} 50 51func (c *Client) dialStreamLocal(socketPath string) (Channel, error) { 52 msg := streamLocalChannelOpenDirectMsg{ 53 socketPath: socketPath, 54 } 55 ch, in, err := c.OpenChannel("direct-streamlocal@openssh.com", Marshal(&msg)) 56 if err != nil { 57 return nil, err 58 } 59 go DiscardRequests(in) 60 return ch, err 61} 62 63type unixListener struct { 64 socketPath string 65 66 conn *Client 67 in <-chan forward 68} 69 70// Accept waits for and returns the next connection to the listener. 71func (l *unixListener) Accept() (net.Conn, error) { 72 s, ok := <-l.in 73 if !ok { 74 return nil, io.EOF 75 } 76 ch, incoming, err := s.newCh.Accept() 77 if err != nil { 78 return nil, err 79 } 80 go DiscardRequests(incoming) 81 82 return &chanConn{ 83 Channel: ch, 84 laddr: &net.UnixAddr{ 85 Name: l.socketPath, 86 Net: "unix", 87 }, 88 raddr: &net.UnixAddr{ 89 Name: "@", 90 Net: "unix", 91 }, 92 }, nil 93} 94 95// Close closes the listener. 96func (l *unixListener) Close() error { 97 // this also closes the listener. 98 l.conn.forwards.remove(&net.UnixAddr{Name: l.socketPath, Net: "unix"}) 99 m := streamLocalChannelForwardMsg{ 100 l.socketPath, 101 } 102 ok, _, err := l.conn.SendRequest("cancel-streamlocal-forward@openssh.com", true, Marshal(&m)) 103 if err == nil && !ok { 104 err = errors.New("ssh: cancel-streamlocal-forward@openssh.com failed") 105 } 106 return err 107} 108 109// Addr returns the listener's network address. 110func (l *unixListener) Addr() net.Addr { 111 return &net.UnixAddr{ 112 Name: l.socketPath, 113 Net: "unix", 114 } 115} 116