proxy.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. package portmapper
  2. import (
  3. "fmt"
  4. "io"
  5. "io/ioutil"
  6. "net"
  7. "os"
  8. "os/exec"
  9. "time"
  10. )
  11. const userlandProxyCommandName = "docker-proxy"
  12. type userlandProxy interface {
  13. Start() error
  14. Stop() error
  15. }
  16. // proxyCommand wraps an exec.Cmd to run the userland TCP and UDP
  17. // proxies as separate processes.
  18. type proxyCommand struct {
  19. cmd *exec.Cmd
  20. }
  21. func (p *proxyCommand) Start() error {
  22. r, w, err := os.Pipe()
  23. if err != nil {
  24. return fmt.Errorf("proxy unable to open os.Pipe %s", err)
  25. }
  26. defer r.Close()
  27. p.cmd.ExtraFiles = []*os.File{w}
  28. if err := p.cmd.Start(); err != nil {
  29. return err
  30. }
  31. w.Close()
  32. errchan := make(chan error, 1)
  33. go func() {
  34. buf := make([]byte, 2)
  35. r.Read(buf)
  36. if string(buf) != "0\n" {
  37. errStr, err := ioutil.ReadAll(r)
  38. if err != nil {
  39. errchan <- fmt.Errorf("Error reading exit status from userland proxy: %v", err)
  40. return
  41. }
  42. errchan <- fmt.Errorf("Error starting userland proxy: %s", errStr)
  43. return
  44. }
  45. errchan <- nil
  46. }()
  47. select {
  48. case err := <-errchan:
  49. return err
  50. case <-time.After(16 * time.Second):
  51. return fmt.Errorf("Timed out proxy starting the userland proxy")
  52. }
  53. }
  54. func (p *proxyCommand) Stop() error {
  55. if p.cmd.Process != nil {
  56. if err := p.cmd.Process.Signal(os.Interrupt); err != nil {
  57. return err
  58. }
  59. return p.cmd.Wait()
  60. }
  61. return nil
  62. }
  63. // dummyProxy just listen on some port, it is needed to prevent accidental
  64. // port allocations on bound port, because without userland proxy we using
  65. // iptables rules and not net.Listen
  66. type dummyProxy struct {
  67. listener io.Closer
  68. addr net.Addr
  69. }
  70. func newDummyProxy(proto string, hostIP net.IP, hostPort int) userlandProxy {
  71. switch proto {
  72. case "tcp":
  73. addr := &net.TCPAddr{IP: hostIP, Port: hostPort}
  74. return &dummyProxy{addr: addr}
  75. case "udp":
  76. addr := &net.UDPAddr{IP: hostIP, Port: hostPort}
  77. return &dummyProxy{addr: addr}
  78. }
  79. return nil
  80. }
  81. func (p *dummyProxy) Start() error {
  82. switch addr := p.addr.(type) {
  83. case *net.TCPAddr:
  84. l, err := net.ListenTCP("tcp", addr)
  85. if err != nil {
  86. return err
  87. }
  88. p.listener = l
  89. case *net.UDPAddr:
  90. l, err := net.ListenUDP("udp", addr)
  91. if err != nil {
  92. return err
  93. }
  94. p.listener = l
  95. default:
  96. return fmt.Errorf("Unknown addr type: %T", p.addr)
  97. }
  98. return nil
  99. }
  100. func (p *dummyProxy) Stop() error {
  101. if p.listener != nil {
  102. return p.listener.Close()
  103. }
  104. return nil
  105. }