github.com/pwn-term/docker@v0.0.0-20210616085119-6e977cce2565/libnetwork/portmapper/proxy.go (about)

     1  package portmapper
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"net"
     8  	"os"
     9  	"os/exec"
    10  	"time"
    11  
    12  	"github.com/ishidawataru/sctp"
    13  )
    14  
    15  var userlandProxyCommandName = "docker-proxy"
    16  
    17  type userlandProxy interface {
    18  	Start() error
    19  	Stop() error
    20  }
    21  
    22  // ipVersion refers to IP version - v4 or v6
    23  type ipVersion string
    24  
    25  const (
    26  	// IPv4 is version 4
    27  	ipv4 ipVersion = "4"
    28  	// IPv4 is version 6
    29  	ipv6 ipVersion = "6"
    30  )
    31  
    32  // proxyCommand wraps an exec.Cmd to run the userland TCP and UDP
    33  // proxies as separate processes.
    34  type proxyCommand struct {
    35  	cmd *exec.Cmd
    36  }
    37  
    38  func (p *proxyCommand) Start() error {
    39  	r, w, err := os.Pipe()
    40  	if err != nil {
    41  		return fmt.Errorf("proxy unable to open os.Pipe %s", err)
    42  	}
    43  	defer r.Close()
    44  	p.cmd.ExtraFiles = []*os.File{w}
    45  	if err := p.cmd.Start(); err != nil {
    46  		return err
    47  	}
    48  	w.Close()
    49  
    50  	errchan := make(chan error, 1)
    51  	go func() {
    52  		buf := make([]byte, 2)
    53  		r.Read(buf)
    54  
    55  		if string(buf) != "0\n" {
    56  			errStr, err := ioutil.ReadAll(r)
    57  			if err != nil {
    58  				errchan <- fmt.Errorf("Error reading exit status from userland proxy: %v", err)
    59  				return
    60  			}
    61  
    62  			errchan <- fmt.Errorf("Error starting userland proxy: %s", errStr)
    63  			return
    64  		}
    65  		errchan <- nil
    66  	}()
    67  
    68  	select {
    69  	case err := <-errchan:
    70  		return err
    71  	case <-time.After(16 * time.Second):
    72  		return fmt.Errorf("Timed out proxy starting the userland proxy")
    73  	}
    74  }
    75  
    76  func (p *proxyCommand) Stop() error {
    77  	if p.cmd.Process != nil {
    78  		if err := p.cmd.Process.Signal(os.Interrupt); err != nil {
    79  			return err
    80  		}
    81  		return p.cmd.Wait()
    82  	}
    83  	return nil
    84  }
    85  
    86  // dummyProxy just listen on some port, it is needed to prevent accidental
    87  // port allocations on bound port, because without userland proxy we using
    88  // iptables rules and not net.Listen
    89  type dummyProxy struct {
    90  	listener  io.Closer
    91  	addr      net.Addr
    92  	ipVersion ipVersion
    93  }
    94  
    95  func newDummyProxy(proto string, hostIP net.IP, hostPort int) (userlandProxy, error) {
    96  	// detect version of hostIP to bind only to correct version
    97  	version := ipv4
    98  	if hostIP.To4() == nil {
    99  		version = ipv6
   100  	}
   101  	switch proto {
   102  	case "tcp":
   103  		addr := &net.TCPAddr{IP: hostIP, Port: hostPort}
   104  		return &dummyProxy{addr: addr, ipVersion: version}, nil
   105  	case "udp":
   106  		addr := &net.UDPAddr{IP: hostIP, Port: hostPort}
   107  		return &dummyProxy{addr: addr, ipVersion: version}, nil
   108  	case "sctp":
   109  		addr := &sctp.SCTPAddr{IPAddrs: []net.IPAddr{{IP: hostIP}}, Port: hostPort}
   110  		return &dummyProxy{addr: addr, ipVersion: version}, nil
   111  	default:
   112  		return nil, fmt.Errorf("Unknown addr type: %s", proto)
   113  	}
   114  }
   115  
   116  func (p *dummyProxy) Start() error {
   117  	switch addr := p.addr.(type) {
   118  	case *net.TCPAddr:
   119  		l, err := net.ListenTCP("tcp"+string(p.ipVersion), addr)
   120  		if err != nil {
   121  			return err
   122  		}
   123  		p.listener = l
   124  	case *net.UDPAddr:
   125  		l, err := net.ListenUDP("udp"+string(p.ipVersion), addr)
   126  		if err != nil {
   127  			return err
   128  		}
   129  		p.listener = l
   130  	case *sctp.SCTPAddr:
   131  		l, err := sctp.ListenSCTP("sctp"+string(p.ipVersion), addr)
   132  		if err != nil {
   133  			return err
   134  		}
   135  		p.listener = l
   136  	default:
   137  		return fmt.Errorf("Unknown addr type: %T", p.addr)
   138  	}
   139  	return nil
   140  }
   141  
   142  func (p *dummyProxy) Stop() error {
   143  	if p.listener != nil {
   144  		return p.listener.Close()
   145  	}
   146  	return nil
   147  }