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