github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/cmd/podman/shared/parse/parse.go (about)

     1  //nolint
     2  // most of these validate and parse functions have been taken from projectatomic/docker
     3  // and modified for cri-o
     4  package parse
     5  
     6  import (
     7  	"bufio"
     8  	"fmt"
     9  	"net"
    10  	"net/url"
    11  	"os"
    12  	"regexp"
    13  	"strings"
    14  
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  const (
    19  	Protocol_TCP Protocol = 0
    20  	Protocol_UDP Protocol = 1
    21  )
    22  
    23  type Protocol int32
    24  
    25  // PortMapping specifies the port mapping configurations of a sandbox.
    26  type PortMapping struct {
    27  	// Protocol of the port mapping.
    28  	Protocol Protocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=runtime.Protocol" json:"protocol,omitempty"`
    29  	// Port number within the container. Default: 0 (not specified).
    30  	ContainerPort int32 `protobuf:"varint,2,opt,name=container_port,json=containerPort,proto3" json:"container_port,omitempty"`
    31  	// Port number on the host. Default: 0 (not specified).
    32  	HostPort int32 `protobuf:"varint,3,opt,name=host_port,json=hostPort,proto3" json:"host_port,omitempty"`
    33  	// Host IP.
    34  	HostIp string `protobuf:"bytes,4,opt,name=host_ip,json=hostIp,proto3" json:"host_ip,omitempty"`
    35  }
    36  
    37  // Note: for flags that are in the form <number><unit>, use the RAMInBytes function
    38  // from the units package in docker/go-units/size.go
    39  
    40  var (
    41  	whiteSpaces  = " \t"
    42  	alphaRegexp  = regexp.MustCompile(`[a-zA-Z]`)
    43  	domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
    44  )
    45  
    46  // validateExtraHost validates that the specified string is a valid extrahost and returns it.
    47  // ExtraHost is in the form of name:ip where the ip has to be a valid ip (ipv4 or ipv6).
    48  // for add-host flag
    49  func ValidateExtraHost(val string) (string, error) { //nolint
    50  	// allow for IPv6 addresses in extra hosts by only splitting on first ":"
    51  	arr := strings.SplitN(val, ":", 2)
    52  	if len(arr) != 2 || len(arr[0]) == 0 {
    53  		return "", fmt.Errorf("bad format for add-host: %q", val)
    54  	}
    55  	if _, err := validateIPAddress(arr[1]); err != nil {
    56  		return "", fmt.Errorf("invalid IP address in add-host: %q", arr[1])
    57  	}
    58  	return val, nil
    59  }
    60  
    61  // validateIPAddress validates an Ip address.
    62  // for dns, ip, and ip6 flags also
    63  func validateIPAddress(val string) (string, error) {
    64  	var ip = net.ParseIP(strings.TrimSpace(val))
    65  	if ip != nil {
    66  		return ip.String(), nil
    67  	}
    68  	return "", fmt.Errorf("%s is not an ip address", val)
    69  }
    70  
    71  func ValidateDomain(val string) (string, error) {
    72  	if alphaRegexp.FindString(val) == "" {
    73  		return "", fmt.Errorf("%s is not a valid domain", val)
    74  	}
    75  	ns := domainRegexp.FindSubmatch([]byte(val))
    76  	if len(ns) > 0 && len(ns[1]) < 255 {
    77  		return string(ns[1]), nil
    78  	}
    79  	return "", fmt.Errorf("%s is not a valid domain", val)
    80  }
    81  
    82  // GetAllLabels retrieves all labels given a potential label file and a number
    83  // of labels provided from the command line.
    84  func GetAllLabels(labelFile, inputLabels []string) (map[string]string, error) {
    85  	labels := make(map[string]string)
    86  	for _, file := range labelFile {
    87  		// Use of parseEnvFile still seems safe, as it's missing the
    88  		// extra parsing logic of parseEnv.
    89  		// There's an argument that we SHOULD be doing that parsing for
    90  		// all environment variables, even those sourced from files, but
    91  		// that would require a substantial rework.
    92  		if err := parseEnvFile(labels, file); err != nil {
    93  			// FIXME: parseEnvFile is using parseEnv, so we need to add extra
    94  			// logic for labels.
    95  			return nil, err
    96  		}
    97  	}
    98  	for _, label := range inputLabels {
    99  		split := strings.SplitN(label, "=", 2)
   100  		if split[0] == "" {
   101  			return nil, errors.Errorf("invalid label format: %q", label)
   102  		}
   103  		value := ""
   104  		if len(split) > 1 {
   105  			value = split[1]
   106  		}
   107  		labels[split[0]] = value
   108  	}
   109  	return labels, nil
   110  }
   111  
   112  func parseEnv(env map[string]string, line string) error {
   113  	data := strings.SplitN(line, "=", 2)
   114  
   115  	// catch invalid variables such as "=" or "=A"
   116  	if data[0] == "" {
   117  		return errors.Errorf("invalid environment variable: %q", line)
   118  	}
   119  
   120  	// trim the front of a variable, but nothing else
   121  	name := strings.TrimLeft(data[0], whiteSpaces)
   122  	if strings.ContainsAny(name, whiteSpaces) {
   123  		return errors.Errorf("name %q has white spaces, poorly formatted name", name)
   124  	}
   125  
   126  	if len(data) > 1 {
   127  		env[name] = data[1]
   128  	} else {
   129  		if strings.HasSuffix(name, "*") {
   130  			name = strings.TrimSuffix(name, "*")
   131  			for _, e := range os.Environ() {
   132  				part := strings.SplitN(e, "=", 2)
   133  				if len(part) < 2 {
   134  					continue
   135  				}
   136  				if strings.HasPrefix(part[0], name) {
   137  					env[part[0]] = part[1]
   138  				}
   139  			}
   140  		} else {
   141  			// if only a pass-through variable is given, clean it up.
   142  			if val, ok := os.LookupEnv(name); ok {
   143  				env[name] = val
   144  			}
   145  		}
   146  	}
   147  	return nil
   148  }
   149  
   150  // parseEnvFile reads a file with environment variables enumerated by lines
   151  func parseEnvFile(env map[string]string, filename string) error {
   152  	fh, err := os.Open(filename)
   153  	if err != nil {
   154  		return err
   155  	}
   156  	defer fh.Close()
   157  
   158  	scanner := bufio.NewScanner(fh)
   159  	for scanner.Scan() {
   160  		// trim the line from all leading whitespace first
   161  		line := strings.TrimLeft(scanner.Text(), whiteSpaces)
   162  		// line is not empty, and not starting with '#'
   163  		if len(line) > 0 && !strings.HasPrefix(line, "#") {
   164  			if err := parseEnv(env, line); err != nil {
   165  				return err
   166  			}
   167  		}
   168  	}
   169  	return scanner.Err()
   170  }
   171  
   172  // ValidateFileName returns an error if filename contains ":"
   173  // as it is currently not supported
   174  func ValidateFileName(filename string) error {
   175  	if strings.Contains(filename, ":") {
   176  		return errors.Errorf("invalid filename (should not contain ':') %q", filename)
   177  	}
   178  	return nil
   179  }
   180  
   181  // ValidURL checks a string urlStr is a url or not
   182  func ValidURL(urlStr string) error {
   183  	_, err := url.ParseRequestURI(urlStr)
   184  	if err != nil {
   185  		return errors.Wrapf(err, "invalid url path: %q", urlStr)
   186  	}
   187  	return nil
   188  }