Secure Client for exposing TLS (aka SSL) secured services as plain-text connections locally, and for multiplexing a single port with multiple protocols using SNI
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

sclient.go 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. package sclient
  2. import (
  3. "crypto/tls"
  4. "fmt"
  5. "io"
  6. "net"
  7. "os"
  8. "strconv"
  9. "strings"
  10. )
  11. // Tunnel specifies which remote encrypted connection to make available as a plain connection locally.
  12. type Tunnel struct {
  13. RemoteAddress string
  14. RemotePort int
  15. LocalAddress string
  16. LocalPort int
  17. InsecureSkipVerify bool
  18. ServerName string
  19. }
  20. // DialAndListen will create a test TLS connection to the remote address and then
  21. // begin listening locally. Each local connection will result in a separate remote connection.
  22. func (t *Tunnel) DialAndListen() error {
  23. remote := t.RemoteAddress + ":" + strconv.Itoa(t.RemotePort)
  24. conn, err := tls.Dial("tcp", remote,
  25. &tls.Config{
  26. ServerName: t.ServerName,
  27. InsecureSkipVerify: t.InsecureSkipVerify,
  28. })
  29. if err != nil {
  30. fmt.Fprintf(os.Stderr, "[warn] '%s' may not be accepting connections: %s\n", remote, err)
  31. } else {
  32. conn.Close()
  33. }
  34. // use stdin/stdout
  35. if "-" == t.LocalAddress || "|" == t.LocalAddress {
  36. var name string
  37. network := "stdio"
  38. if "|" == t.LocalAddress {
  39. name = "pipe"
  40. } else {
  41. name = "stdin"
  42. }
  43. conn := &stdnet{os.Stdin, os.Stdout, &stdaddr{net.UnixAddr{name, network}}}
  44. t.handleConnection(remote, conn)
  45. return nil
  46. }
  47. // use net.Conn
  48. local := t.LocalAddress + ":" + strconv.Itoa(t.LocalPort)
  49. ln, err := net.Listen("tcp", local)
  50. if err != nil {
  51. return err
  52. }
  53. fmt.Fprintf(os.Stdout, "[listening] %s:%d <= %s:%d\n",
  54. t.RemoteAddress, t.RemotePort, t.LocalAddress, t.LocalPort)
  55. for {
  56. conn, err := ln.Accept()
  57. if nil != err {
  58. fmt.Fprintf(os.Stderr, "[error] %s\n", err)
  59. continue
  60. }
  61. go t.handleConnection(remote, conn)
  62. }
  63. }
  64. // I wonder if I can get this to exactly mirror UnixAddr without passing it in
  65. type stdaddr struct {
  66. net.UnixAddr
  67. }
  68. type stdnet struct {
  69. in *os.File // os.Stdin
  70. out *os.File // os.Stdout
  71. addr *stdaddr
  72. }
  73. func (rw *stdnet) Read(buf []byte) (n int, err error) {
  74. return rw.in.Read(buf)
  75. }
  76. func (rw *stdnet) Write(buf []byte) (n int, err error) {
  77. return rw.out.Write(buf)
  78. }
  79. func (rw *stdnet) Close() error {
  80. return rw.in.Close()
  81. }
  82. func (rw *stdnet) RemoteAddr() net.Addr {
  83. return rw.addr
  84. }
  85. // not all of net.Conn, just RWC and RemoteAddr()
  86. type netReadWriteCloser interface {
  87. io.ReadWriteCloser
  88. RemoteAddr() net.Addr
  89. }
  90. func pipe(r netReadWriteCloser, w netReadWriteCloser, t string) {
  91. buffer := make([]byte, 2048)
  92. for {
  93. done := false
  94. // NOTE: count may be > 0 even if there's an err
  95. //fmt.Fprintf(os.Stdout, "[debug] (%s) reading\n", t)
  96. count, err := r.Read(buffer)
  97. if nil != err {
  98. //fmt.Fprintf(os.Stdout, "[debug] (%s:%d) error reading %s\n", t, count, err)
  99. if io.EOF != err {
  100. fmt.Fprintf(os.Stderr, "[read error] (%s:%s) %s\n", t, count, err)
  101. }
  102. r.Close()
  103. //w.Close()
  104. done = true
  105. }
  106. if 0 == count {
  107. break
  108. }
  109. _, err = w.Write(buffer[:count])
  110. if nil != err {
  111. //fmt.Fprintf(os.Stdout, "[debug] %s error writing\n", t)
  112. if io.EOF != err {
  113. fmt.Fprintf(os.Stderr, "[write error] (%s) %s\n", t, err)
  114. }
  115. // TODO handle error closing?
  116. r.Close()
  117. //w.Close()
  118. done = true
  119. }
  120. if done {
  121. break
  122. }
  123. }
  124. }
  125. func (t *Tunnel) handleConnection(remote string, conn netReadWriteCloser) {
  126. sclient, err := tls.Dial("tcp", remote,
  127. &tls.Config{
  128. ServerName: t.ServerName,
  129. InsecureSkipVerify: t.InsecureSkipVerify,
  130. })
  131. if err != nil {
  132. fmt.Fprintf(os.Stderr, "[error] (remote) %s\n", err)
  133. conn.Close()
  134. return
  135. }
  136. if "stdio" == conn.RemoteAddr().Network() {
  137. fmt.Fprintf(os.Stdout, "(connected to %s:%d and reading from %s)\n",
  138. t.RemoteAddress, t.RemotePort, conn.RemoteAddr().String())
  139. } else {
  140. fmt.Fprintf(os.Stdout, "[connect] %s => %s:%d\n",
  141. strings.Replace(conn.RemoteAddr().String(), "[::1]:", "localhost:", 1), t.RemoteAddress, t.RemotePort)
  142. }
  143. go pipe(conn, sclient, "local")
  144. pipe(sclient, conn, "remote")
  145. }