Secure Client for exposing TLS (aka SSL) secured services as plain-text connections locally. Also ideal for multiplexing a single port with multiple protocols using SNI.

sclient.go 3.6KB

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