util_windows.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // +build windows
  2. /*
  3. Copyright 2017 The Kubernetes Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package util
  15. import (
  16. "context"
  17. "fmt"
  18. "net"
  19. "net/url"
  20. "strings"
  21. "syscall"
  22. "time"
  23. "github.com/Microsoft/go-winio"
  24. )
  25. const (
  26. tcpProtocol = "tcp"
  27. npipeProtocol = "npipe"
  28. )
  29. // CreateListener creates a listener on the specified endpoint.
  30. func CreateListener(endpoint string) (net.Listener, error) {
  31. protocol, addr, err := parseEndpoint(endpoint)
  32. if err != nil {
  33. return nil, err
  34. }
  35. switch protocol {
  36. case tcpProtocol:
  37. return net.Listen(tcpProtocol, addr)
  38. case npipeProtocol:
  39. return winio.ListenPipe(addr, nil)
  40. default:
  41. return nil, fmt.Errorf("only support tcp and npipe endpoint")
  42. }
  43. }
  44. // GetAddressAndDialer returns the address parsed from the given endpoint and a context dialer.
  45. func GetAddressAndDialer(endpoint string) (string, func(ctx context.Context, addr string) (net.Conn, error), error) {
  46. protocol, addr, err := parseEndpoint(endpoint)
  47. if err != nil {
  48. return "", nil, err
  49. }
  50. if protocol == tcpProtocol {
  51. return addr, tcpDial, nil
  52. }
  53. if protocol == npipeProtocol {
  54. return addr, npipeDial, nil
  55. }
  56. return "", nil, fmt.Errorf("only support tcp and npipe endpoint")
  57. }
  58. func tcpDial(ctx context.Context, addr string) (net.Conn, error) {
  59. return (&net.Dialer{}).DialContext(ctx, tcpProtocol, addr)
  60. }
  61. func npipeDial(ctx context.Context, addr string) (net.Conn, error) {
  62. return winio.DialPipeContext(ctx, addr)
  63. }
  64. func parseEndpoint(endpoint string) (string, string, error) {
  65. // url.Parse doesn't recognize \, so replace with / first.
  66. endpoint = strings.Replace(endpoint, "\\", "/", -1)
  67. u, err := url.Parse(endpoint)
  68. if err != nil {
  69. return "", "", err
  70. }
  71. if u.Scheme == "tcp" {
  72. return "tcp", u.Host, nil
  73. } else if u.Scheme == "npipe" {
  74. if strings.HasPrefix(u.Path, "//./pipe") {
  75. return "npipe", u.Path, nil
  76. }
  77. // fallback host if not provided.
  78. host := u.Host
  79. if host == "" {
  80. host = "."
  81. }
  82. return "npipe", fmt.Sprintf("//%s%s", host, u.Path), nil
  83. } else if u.Scheme == "" {
  84. return "", "", fmt.Errorf("Using %q as endpoint is deprecated, please consider using full url format", endpoint)
  85. } else {
  86. return u.Scheme, "", fmt.Errorf("protocol %q not supported", u.Scheme)
  87. }
  88. }
  89. // LocalEndpoint empty implementation
  90. func LocalEndpoint(path, file string) (string, error) {
  91. return "", fmt.Errorf("LocalEndpoints are unsupported in this build")
  92. }
  93. var tickCount = syscall.NewLazyDLL("kernel32.dll").NewProc("GetTickCount64")
  94. // GetBootTime returns the time at which the machine was started, truncated to the nearest second
  95. func GetBootTime() (time.Time, error) {
  96. currentTime := time.Now()
  97. output, _, err := tickCount.Call()
  98. if errno, ok := err.(syscall.Errno); !ok || errno != 0 {
  99. return time.Time{}, err
  100. }
  101. return currentTime.Add(-time.Duration(output) * time.Millisecond).Truncate(time.Second), nil
  102. }
  103. // IsUnixDomainSocket returns whether a given file is a AF_UNIX socket file
  104. func IsUnixDomainSocket(filePath string) (bool, error) {
  105. // Due to the absence of golang support for os.ModeSocket in Windows (https://github.com/golang/go/issues/33357)
  106. // we need to dial the file and check if we receive an error to determine if a file is Unix Domain Socket file.
  107. // Note that querrying for the Reparse Points (https://docs.microsoft.com/en-us/windows/win32/fileio/reparse-points)
  108. // for the file (using FSCTL_GET_REPARSE_POINT) and checking for reparse tag: reparseTagSocket
  109. // does NOT work in 1809 if the socket file is created within a bind mounted directory by a container
  110. // and the FSCTL is issued in the host by the kubelet.
  111. c, err := net.Dial("unix", filePath)
  112. if err == nil {
  113. c.Close()
  114. return true, nil
  115. }
  116. return false, nil
  117. }
  118. // NormalizePath converts FS paths returned by certain go frameworks (like fsnotify)
  119. // to native Windows paths that can be passed to Windows specific code
  120. func NormalizePath(path string) string {
  121. path = strings.ReplaceAll(path, "/", "\\")
  122. if strings.HasPrefix(path, "\\") {
  123. path = "c:" + path
  124. }
  125. return path
  126. }