streamlocal.go 2.8 KB

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