171 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // +build windows
 | |
| 
 | |
| /*
 | |
|    Copyright The containerd Authors.
 | |
| 
 | |
|    Licensed under the Apache License, Version 2.0 (the "License");
 | |
|    you may not use this file except in compliance with the License.
 | |
|    You may obtain a copy of the License at
 | |
| 
 | |
|        http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
|    Unless required by applicable law or agreed to in writing, software
 | |
|    distributed under the License is distributed on an "AS IS" BASIS,
 | |
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|    See the License for the specific language governing permissions and
 | |
|    limitations under the License.
 | |
| */
 | |
| 
 | |
| /*
 | |
| Copyright 2017 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package util
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"net/url"
 | |
| 	"strings"
 | |
| 	"syscall"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/Microsoft/go-winio"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	tcpProtocol   = "tcp"
 | |
| 	npipeProtocol = "npipe"
 | |
| )
 | |
| 
 | |
| // CreateListener creates a listener on the specified endpoint.
 | |
| func CreateListener(endpoint string) (net.Listener, error) {
 | |
| 	protocol, addr, err := parseEndpoint(endpoint)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	switch protocol {
 | |
| 	case tcpProtocol:
 | |
| 		return net.Listen(tcpProtocol, addr)
 | |
| 
 | |
| 	case npipeProtocol:
 | |
| 		return winio.ListenPipe(addr, nil)
 | |
| 
 | |
| 	default:
 | |
| 		return nil, fmt.Errorf("only support tcp and npipe endpoint")
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // GetAddressAndDialer returns the address parsed from the given endpoint and a context dialer.
 | |
| func GetAddressAndDialer(endpoint string) (string, func(ctx context.Context, addr string) (net.Conn, error), error) {
 | |
| 	protocol, addr, err := parseEndpoint(endpoint)
 | |
| 	if err != nil {
 | |
| 		return "", nil, err
 | |
| 	}
 | |
| 
 | |
| 	if protocol == tcpProtocol {
 | |
| 		return addr, tcpDial, nil
 | |
| 	}
 | |
| 
 | |
| 	if protocol == npipeProtocol {
 | |
| 		return addr, npipeDial, nil
 | |
| 	}
 | |
| 
 | |
| 	return "", nil, fmt.Errorf("only support tcp and npipe endpoint")
 | |
| }
 | |
| 
 | |
| func tcpDial(ctx context.Context, addr string) (net.Conn, error) {
 | |
| 	return (&net.Dialer{}).DialContext(ctx, tcpProtocol, addr)
 | |
| }
 | |
| 
 | |
| func npipeDial(ctx context.Context, addr string) (net.Conn, error) {
 | |
| 	return winio.DialPipeContext(ctx, addr)
 | |
| }
 | |
| 
 | |
| func parseEndpoint(endpoint string) (string, string, error) {
 | |
| 	// url.Parse doesn't recognize \, so replace with / first.
 | |
| 	endpoint = strings.Replace(endpoint, "\\", "/", -1)
 | |
| 	u, err := url.Parse(endpoint)
 | |
| 	if err != nil {
 | |
| 		return "", "", err
 | |
| 	}
 | |
| 
 | |
| 	if u.Scheme == "tcp" {
 | |
| 		return "tcp", u.Host, nil
 | |
| 	} else if u.Scheme == "npipe" {
 | |
| 		if strings.HasPrefix(u.Path, "//./pipe") {
 | |
| 			return "npipe", u.Path, nil
 | |
| 		}
 | |
| 
 | |
| 		// fallback host if not provided.
 | |
| 		host := u.Host
 | |
| 		if host == "" {
 | |
| 			host = "."
 | |
| 		}
 | |
| 		return "npipe", fmt.Sprintf("//%s%s", host, u.Path), nil
 | |
| 	} else if u.Scheme == "" {
 | |
| 		return "", "", fmt.Errorf("Using %q as endpoint is deprecated, please consider using full url format", endpoint)
 | |
| 	} else {
 | |
| 		return u.Scheme, "", fmt.Errorf("protocol %q not supported", u.Scheme)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // LocalEndpoint empty implementation
 | |
| func LocalEndpoint(path, file string) (string, error) {
 | |
| 	return "", fmt.Errorf("LocalEndpoints are unsupported in this build")
 | |
| }
 | |
| 
 | |
| var tickCount = syscall.NewLazyDLL("kernel32.dll").NewProc("GetTickCount64")
 | |
| 
 | |
| // GetBootTime returns the time at which the machine was started, truncated to the nearest second
 | |
| func GetBootTime() (time.Time, error) {
 | |
| 	currentTime := time.Now()
 | |
| 	output, _, err := tickCount.Call()
 | |
| 	if errno, ok := err.(syscall.Errno); !ok || errno != 0 {
 | |
| 		return time.Time{}, err
 | |
| 	}
 | |
| 	return currentTime.Add(-time.Duration(output) * time.Millisecond).Truncate(time.Second), nil
 | |
| }
 | |
| 
 | |
| // IsUnixDomainSocket returns whether a given file is a AF_UNIX socket file
 | |
| func IsUnixDomainSocket(filePath string) (bool, error) {
 | |
| 	// Due to the absence of golang support for os.ModeSocket in Windows (https://github.com/golang/go/issues/33357)
 | |
| 	// we need to dial the file and check if we receive an error to determine if a file is Unix Domain Socket file.
 | |
| 
 | |
| 	// Note that querrying for the Reparse Points (https://docs.microsoft.com/en-us/windows/win32/fileio/reparse-points)
 | |
| 	// for the file (using FSCTL_GET_REPARSE_POINT) and checking for reparse tag: reparseTagSocket
 | |
| 	// does NOT work in 1809 if the socket file is created within a bind mounted directory by a container
 | |
| 	// and the FSCTL is issued in the host by the kubelet.
 | |
| 
 | |
| 	c, err := net.Dial("unix", filePath)
 | |
| 	if err == nil {
 | |
| 		c.Close()
 | |
| 		return true, nil
 | |
| 	}
 | |
| 	return false, nil
 | |
| }
 | |
| 
 | |
| // NormalizePath converts FS paths returned by certain go frameworks (like fsnotify)
 | |
| // to native Windows paths that can be passed to Windows specific code
 | |
| func NormalizePath(path string) string {
 | |
| 	path = strings.ReplaceAll(path, "/", "\\")
 | |
| 	if strings.HasPrefix(path, "\\") {
 | |
| 		path = "c:" + path
 | |
| 	}
 | |
| 	return path
 | |
| }
 | 
