streamlocal.go 2.9 KB

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