streamlocal.go (2939B)
1 package ssh 2 3 import ( 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 14 type 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. 22 type 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. 29 type streamLocalChannelForwardMsg struct { 30 socketPath string 31 } 32 33 // ListenUnix is similar to ListenTCP but uses a Unix domain socket. 34 func (c *Client) ListenUnix(socketPath string) (net.Listener, error) { 35 c.handleForwardsOnce.Do(c.handleForwards) 36 m := streamLocalChannelForwardMsg{ 37 socketPath, 38 } 39 // send message 40 ok, _, err := c.SendRequest("streamlocal-forward@openssh.com", true, Marshal(&m)) 41 if err != nil { 42 return nil, err 43 } 44 if !ok { 45 return nil, errors.New("ssh: streamlocal-forward@openssh.com request denied by peer") 46 } 47 ch := c.forwards.add(&net.UnixAddr{Name: socketPath, Net: "unix"}) 48 49 return &unixListener{socketPath, c, ch}, nil 50 } 51 52 func (c *Client) dialStreamLocal(socketPath string) (Channel, error) { 53 msg := streamLocalChannelOpenDirectMsg{ 54 socketPath: socketPath, 55 } 56 ch, in, err := c.OpenChannel("direct-streamlocal@openssh.com", Marshal(&msg)) 57 if err != nil { 58 return nil, err 59 } 60 go DiscardRequests(in) 61 return ch, err 62 } 63 64 type unixListener struct { 65 socketPath string 66 67 conn *Client 68 in <-chan forward 69 } 70 71 // Accept waits for and returns the next connection to the listener. 72 func (l *unixListener) Accept() (net.Conn, error) { 73 s, ok := <-l.in 74 if !ok { 75 return nil, io.EOF 76 } 77 ch, incoming, err := s.newCh.Accept() 78 if err != nil { 79 return nil, err 80 } 81 go DiscardRequests(incoming) 82 83 return &chanConn{ 84 Channel: ch, 85 laddr: &net.UnixAddr{ 86 Name: l.socketPath, 87 Net: "unix", 88 }, 89 raddr: &net.UnixAddr{ 90 Name: "@", 91 Net: "unix", 92 }, 93 }, nil 94 } 95 96 // Close closes the listener. 97 func (l *unixListener) Close() error { 98 // this also closes the listener. 99 l.conn.forwards.remove(&net.UnixAddr{Name: l.socketPath, Net: "unix"}) 100 m := streamLocalChannelForwardMsg{ 101 l.socketPath, 102 } 103 ok, _, err := l.conn.SendRequest("cancel-streamlocal-forward@openssh.com", true, Marshal(&m)) 104 if err == nil && !ok { 105 err = errors.New("ssh: cancel-streamlocal-forward@openssh.com failed") 106 } 107 return err 108 } 109 110 // Addr returns the listener's network address. 111 func (l *unixListener) Addr() net.Addr { 112 return &net.UnixAddr{ 113 Name: l.socketPath, 114 Net: "unix", 115 } 116 }