github.com/rootless-containers/rootlesskit/v2@v2.3.4/pkg/port/builtin/parent/tcp/tcp.go (about)

     1  package tcp
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"net"
     7  	"os"
     8  	"strconv"
     9  	"sync"
    10  
    11  	"github.com/rootless-containers/rootlesskit/v2/pkg/port"
    12  	"github.com/rootless-containers/rootlesskit/v2/pkg/port/builtin/msg"
    13  )
    14  
    15  func Run(socketPath string, spec port.Spec, stopCh <-chan struct{}, stoppedCh chan error, logWriter io.Writer) error {
    16  	ln, err := net.Listen(spec.Proto, net.JoinHostPort(spec.ParentIP, strconv.Itoa(spec.ParentPort)))
    17  	if err != nil {
    18  		fmt.Fprintf(logWriter, "listen: %v\n", err)
    19  		return err
    20  	}
    21  	newConns := make(chan net.Conn)
    22  	go func() {
    23  		for {
    24  			c, err := ln.Accept()
    25  			if err != nil {
    26  				fmt.Fprintf(logWriter, "accept: %v\n", err)
    27  				close(newConns)
    28  				return
    29  			}
    30  			newConns <- c
    31  		}
    32  	}()
    33  	go func() {
    34  		defer func() {
    35  			stoppedCh <- ln.Close()
    36  			close(stoppedCh)
    37  		}()
    38  		for {
    39  			select {
    40  			case c, ok := <-newConns:
    41  				if !ok {
    42  					return
    43  				}
    44  				go func() {
    45  					if err := copyConnToChild(c, socketPath, spec, stopCh); err != nil {
    46  						fmt.Fprintf(logWriter, "copyConnToChild: %v\n", err)
    47  						return
    48  					}
    49  				}()
    50  			case <-stopCh:
    51  				return
    52  			}
    53  		}
    54  	}()
    55  	// no wait
    56  	return nil
    57  }
    58  
    59  func copyConnToChild(c net.Conn, socketPath string, spec port.Spec, stopCh <-chan struct{}) error {
    60  	defer c.Close()
    61  	// get fd from the child as an SCM_RIGHTS cmsg
    62  	fd, err := msg.ConnectToChildWithRetry(socketPath, spec, 10)
    63  	if err != nil {
    64  		return err
    65  	}
    66  	f := os.NewFile(uintptr(fd), "")
    67  	defer f.Close()
    68  	fc, err := net.FileConn(f)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	defer fc.Close()
    73  	bicopy(c, fc, stopCh)
    74  	return nil
    75  }
    76  
    77  // bicopy is based on libnetwork/cmd/proxy/tcp_proxy.go .
    78  // NOTE: sendfile(2) cannot be used for sockets
    79  func bicopy(x, y net.Conn, quit <-chan struct{}) {
    80  	var wg sync.WaitGroup
    81  	var broker = func(to, from net.Conn) {
    82  		io.Copy(to, from)
    83  		if fromTCP, ok := from.(*net.TCPConn); ok {
    84  			fromTCP.CloseRead()
    85  		}
    86  		if toTCP, ok := to.(*net.TCPConn); ok {
    87  			toTCP.CloseWrite()
    88  		}
    89  		wg.Done()
    90  	}
    91  
    92  	wg.Add(2)
    93  	go broker(x, y)
    94  	go broker(y, x)
    95  	finish := make(chan struct{})
    96  	go func() {
    97  		wg.Wait()
    98  		close(finish)
    99  	}()
   100  
   101  	select {
   102  	case <-quit:
   103  	case <-finish:
   104  	}
   105  	x.Close()
   106  	y.Close()
   107  	<-finish
   108  }