package host import ( "context" "encoding/binary" "fmt" "net" "net/http" "os" "path/filepath" "strconv" "strings" "time" "github.com/carlosframework/platform/internal/instancectl" ) func claimProcTCPAddress(a *net.TCPAddr, ipv6 bool) string { ip := a.IP.To4() if ipv6 { ip = a.IP.To16() } if ip == nil { return "" } var b strings.Builder for i := 0; i < len(ip); i += 4 { fmt.Fprintf(&b, "%08X", binary.NativeEndian.Uint32(ip[i:i+4])) } return fmt.Sprintf("%s:%04X", b.String(), a.Port) } func claimTCPPeer(conn net.Conn, group string) error { local, ok := conn.LocalAddr().(*net.TCPAddr) if !ok { return os.ErrPermission } remote, ok := conn.RemoteAddr().(*net.TCPAddr) if !ok { return os.ErrPermission } pids, err := os.ReadFile(filepath.Join("/sys/fs/cgroup", group, "cgroup.procs")) if err != nil { return err } inodes := map[string]bool{} for _, network := range []string{"tcp", "tcp6"} { raw, err := os.ReadFile("/proc/net/" + network) if err != nil { return err } for _, line := range strings.Split(string(raw), "\n") { fields := strings.Fields(line) if len(fields) > 9 && fields[1] == claimProcTCPAddress(remote, network == "tcp6") && fields[2] == claimProcTCPAddress(local, network == "tcp6") && fields[3] == "01" && fields[9] != "0" { inodes["socket:["+fields[9]+"]"] = true } } } for _, pid := range strings.Fields(string(pids)) { n, err := strconv.Atoi(pid) if err != nil || n <= 1 { return os.ErrPermission } dir := filepath.Join("/proc", pid, "fd") entries, err := os.ReadDir(dir) if err != nil { return err } for _, entry := range entries { target, err := os.Readlink(filepath.Join(dir, entry.Name())) if err == nil && inodes[target] { return nil } } } return os.ErrPermission } func claimBoundUnitReady(ctx context.Context, s instancectl.ClaimUnitStarted, shape claimUnitShape) error { transport := &http.Transport{DisableKeepAlives: true, DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { network, endpoint := "unix", claimSocketTarget(shape) if shape.Addr != "" { network, endpoint = "tcp", shape.Addr } conn, err := (&net.Dialer{}).DialContext(ctx, network, endpoint) if err != nil { return nil, err } if network == "unix" { err = claimProbePeer(conn, s.MainPID, "0::/"+s.ControlGroup) } else { for until := time.Now().Add(500 * time.Millisecond); ; { err = claimTCPPeer(conn, s.ControlGroup) if err == nil || time.Now().After(until) { break } select { case <-ctx.Done(): conn.Close() return nil, ctx.Err() case <-time.After(10 * time.Millisecond): } } } if err != nil { conn.Close() return nil, err } return conn, nil }} defer transport.CloseIdleConnections() request, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://carlos/", nil) if err != nil { return err } client := &http.Client{Transport: transport, Timeout: time.Second, CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }} response, err := client.Do(request) if err != nil { return err } defer response.Body.Close() if response.StatusCode >= 500 { return os.ErrPermission } return nil }