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

     1  package child
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"net"
     8  	"os"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"golang.org/x/sys/unix"
    13  
    14  	"github.com/containernetworking/plugins/pkg/ns"
    15  	"github.com/rootless-containers/rootlesskit/v2/pkg/lowlevelmsgutil"
    16  	"github.com/rootless-containers/rootlesskit/v2/pkg/port"
    17  	"github.com/rootless-containers/rootlesskit/v2/pkg/port/builtin/msg"
    18  	opaquepkg "github.com/rootless-containers/rootlesskit/v2/pkg/port/builtin/opaque"
    19  )
    20  
    21  func NewDriver(logWriter io.Writer) port.ChildDriver {
    22  	return &childDriver{
    23  		logWriter: logWriter,
    24  	}
    25  }
    26  
    27  type childDriver struct {
    28  	logWriter io.Writer
    29  }
    30  
    31  func (d *childDriver) RunChildDriver(opaque map[string]string, quit <-chan struct{}, detachedNetNSPath string) error {
    32  	socketPath := opaque[opaquepkg.SocketPath]
    33  	if socketPath == "" {
    34  		return errors.New("socket path not set")
    35  	}
    36  	childReadyPipePath := opaque[opaquepkg.ChildReadyPipePath]
    37  	if childReadyPipePath == "" {
    38  		return errors.New("child ready pipe path not set")
    39  	}
    40  	childReadyPipeW, err := os.OpenFile(childReadyPipePath, os.O_WRONLY, os.ModeNamedPipe)
    41  	if err != nil {
    42  		return err
    43  	}
    44  	ln, err := net.ListenUnix("unix", &net.UnixAddr{
    45  		Name: socketPath,
    46  		Net:  "unix",
    47  	})
    48  	if err != nil {
    49  		return err
    50  	}
    51  	// write nothing, just close
    52  	if err = childReadyPipeW.Close(); err != nil {
    53  		return err
    54  	}
    55  	stopAccept := make(chan struct{}, 1)
    56  	go func() {
    57  		<-quit
    58  		stopAccept <- struct{}{}
    59  		ln.Close()
    60  	}()
    61  	for {
    62  		c, err := ln.AcceptUnix()
    63  		if err != nil {
    64  			select {
    65  			case <-stopAccept:
    66  				return nil
    67  			default:
    68  			}
    69  			return err
    70  		}
    71  		go func() {
    72  			if rerr := d.routine(c, detachedNetNSPath); rerr != nil {
    73  				rep := msg.Reply{
    74  					Error: rerr.Error(),
    75  				}
    76  				lowlevelmsgutil.MarshalToWriter(c, &rep)
    77  			}
    78  			c.Close()
    79  		}()
    80  	}
    81  }
    82  
    83  func (d *childDriver) routine(c *net.UnixConn, detachedNetNSPath string) error {
    84  	var req msg.Request
    85  	if _, err := lowlevelmsgutil.UnmarshalFromReader(c, &req); err != nil {
    86  		return err
    87  	}
    88  	switch req.Type {
    89  	case msg.RequestTypeInit:
    90  		return d.handleConnectInit(c, &req)
    91  	case msg.RequestTypeConnect:
    92  		if detachedNetNSPath == "" {
    93  			return d.handleConnectRequest(c, &req)
    94  		} else {
    95  			return ns.WithNetNSPath(detachedNetNSPath, func(_ ns.NetNS) error {
    96  				return d.handleConnectRequest(c, &req)
    97  			})
    98  		}
    99  	default:
   100  		return fmt.Errorf("unknown request type %q", req.Type)
   101  	}
   102  }
   103  
   104  func (d *childDriver) handleConnectInit(c *net.UnixConn, req *msg.Request) error {
   105  	_, err := lowlevelmsgutil.MarshalToWriter(c, nil)
   106  	return err
   107  }
   108  
   109  func (d *childDriver) handleConnectRequest(c *net.UnixConn, req *msg.Request) error {
   110  	switch req.Proto {
   111  	case "tcp":
   112  	case "tcp4":
   113  	case "tcp6":
   114  	case "udp":
   115  	case "udp4":
   116  	case "udp6":
   117  	default:
   118  		return fmt.Errorf("unknown proto: %q", req.Proto)
   119  	}
   120  	// dialProto does not need "4", "6" suffix
   121  	dialProto := strings.TrimSuffix(strings.TrimSuffix(req.Proto, "6"), "4")
   122  	var dialer net.Dialer
   123  	ip := req.IP
   124  	if ip == "" {
   125  		ip = "127.0.0.1"
   126  		if req.ParentIP != "" {
   127  			if req.ParentIP != req.HostGatewayIP && req.ParentIP != "0.0.0.0" {
   128  				ip = req.ParentIP
   129  			}
   130  		}
   131  	} else {
   132  		p := net.ParseIP(ip)
   133  		if p == nil {
   134  			return fmt.Errorf("invalid IP: %q", ip)
   135  		}
   136  		ip = p.String()
   137  	}
   138  	targetConn, err := dialer.Dial(dialProto, net.JoinHostPort(ip, strconv.Itoa(req.Port)))
   139  	if err != nil {
   140  		return err
   141  	}
   142  	defer targetConn.Close() // no effect on duplicated FD
   143  	targetConnFiler, ok := targetConn.(filer)
   144  	if !ok {
   145  		return fmt.Errorf("unknown target connection: %+v", targetConn)
   146  	}
   147  	targetConnFile, err := targetConnFiler.File()
   148  	if err != nil {
   149  		return err
   150  	}
   151  	defer targetConnFile.Close()
   152  	oob := unix.UnixRights(int(targetConnFile.Fd()))
   153  	f, err := c.File()
   154  	if err != nil {
   155  		return err
   156  	}
   157  	defer f.Close()
   158  	for {
   159  		err = unix.Sendmsg(int(f.Fd()), []byte("dummy"), oob, nil, 0)
   160  		if err != unix.EINTR {
   161  			break
   162  		}
   163  	}
   164  	return err
   165  }
   166  
   167  // filer is implemented by *net.TCPConn and *net.UDPConn
   168  type filer interface {
   169  	File() (f *os.File, err error)
   170  }