k8s.io/kubernetes@v1.29.3/test/images/agnhost/connect/connect.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 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 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package connect 18 19 import ( 20 "fmt" 21 "net" 22 "os" 23 "strings" 24 "syscall" 25 "time" 26 27 "github.com/ishidawataru/sctp" 28 "github.com/spf13/cobra" 29 ) 30 31 // CmdConnect is used by agnhost Cobra. 32 var CmdConnect = &cobra.Command{ 33 Use: "connect [host:port]", 34 Short: "Attempts a TCP, UDP or SCTP connection and returns useful errors", 35 Long: `Tries to open a TCP, UDP or SCTP connection to the given host and port. On error it prints an error message prefixed with a specific fixed string that test cases can check for: 36 37 * UNKNOWN - Generic/unknown (non-network) error (eg, bad arguments) 38 * TIMEOUT - The connection attempt timed out 39 * DNS - An error in DNS resolution 40 * REFUSED - Connection refused 41 * OTHER - Other networking error (eg, "no route to host")`, 42 Args: cobra.ExactArgs(1), 43 Run: main, 44 } 45 46 var ( 47 timeout time.Duration 48 protocol string 49 udpData string 50 ) 51 52 func init() { 53 CmdConnect.Flags().DurationVar(&timeout, "timeout", time.Duration(0), "Maximum time before returning an error") 54 CmdConnect.Flags().StringVar(&protocol, "protocol", "tcp", "The protocol to use to perform the connection, can be tcp, udp or sctp") 55 CmdConnect.Flags().StringVar(&udpData, "udp-data", "hostname", "The UDP payload send to the server") 56 } 57 58 func main(cmd *cobra.Command, args []string) { 59 dest := args[0] 60 switch protocol { 61 case "", "tcp": 62 connectTCP(dest, timeout) 63 case "udp": 64 connectUDP(dest, timeout, udpData) 65 case "sctp": 66 connectSCTP(dest, timeout) 67 default: 68 fmt.Fprint(os.Stderr, "Unsupported protocol\n", protocol) 69 os.Exit(1) 70 } 71 } 72 73 func connectTCP(dest string, timeout time.Duration) { 74 // Redundantly parse and resolve the destination so we can return the correct 75 // errors if there's a problem. 76 if _, _, err := net.SplitHostPort(dest); err != nil { 77 fmt.Fprintf(os.Stderr, "UNKNOWN: %v\n", err) 78 os.Exit(1) 79 } 80 if _, err := net.ResolveTCPAddr("tcp", dest); err != nil { 81 fmt.Fprintf(os.Stderr, "DNS: %v\n", err) 82 os.Exit(1) 83 } 84 85 conn, err := net.DialTimeout("tcp", dest, timeout) 86 if err == nil { 87 conn.Close() 88 os.Exit(0) 89 } 90 if opErr, ok := err.(*net.OpError); ok { 91 if opErr.Timeout() { 92 fmt.Fprintf(os.Stderr, "TIMEOUT\n") 93 os.Exit(1) 94 } else if syscallErr, ok := opErr.Err.(*os.SyscallError); ok { 95 if syscallErr.Err == syscall.ECONNREFUSED { 96 fmt.Fprintf(os.Stderr, "REFUSED\n") 97 os.Exit(1) 98 } 99 } 100 } 101 102 fmt.Fprintf(os.Stderr, "OTHER: %v\n", err) 103 os.Exit(1) 104 } 105 106 func connectSCTP(dest string, timeout time.Duration) { 107 addr, err := sctp.ResolveSCTPAddr("sctp", dest) 108 if err != nil { 109 fmt.Fprintf(os.Stderr, "DNS: %v\n", err) 110 os.Exit(1) 111 } 112 113 timeoutCh := time.After(timeout) 114 errCh := make(chan error) 115 116 go func() { 117 conn, err := sctp.DialSCTP("sctp", nil, addr) 118 if err == nil { 119 conn.Close() 120 } 121 errCh <- err 122 }() 123 124 select { 125 case err := <-errCh: 126 if err != nil { 127 fmt.Fprintf(os.Stderr, "OTHER: %v\n", err) 128 os.Exit(1) 129 } 130 case <-timeoutCh: 131 fmt.Fprint(os.Stderr, "TIMEOUT\n") 132 os.Exit(1) 133 } 134 } 135 136 func connectUDP(dest string, timeout time.Duration, data string) { 137 var ( 138 readBytes int 139 buf = make([]byte, 1024) 140 ) 141 142 if _, err := net.ResolveUDPAddr("udp", dest); err != nil { 143 fmt.Fprintf(os.Stderr, "DNS: %v\n", err) 144 os.Exit(1) 145 } 146 147 conn, err := net.Dial("udp", dest) 148 if err != nil { 149 fmt.Fprintf(os.Stderr, "OTHER: %v\n", err) 150 os.Exit(1) 151 } 152 153 if timeout > 0 { 154 if err = conn.SetDeadline(time.Now().Add(timeout)); err != nil { 155 fmt.Fprintf(os.Stderr, "OTHER: %v\n", err) 156 os.Exit(1) 157 } 158 } 159 160 if _, err = conn.Write([]byte(fmt.Sprintf("%s\n", data))); err != nil { 161 parseUDPErrorAndExit(err) 162 } 163 164 if readBytes, err = conn.Read(buf); err != nil { 165 parseUDPErrorAndExit(err) 166 } 167 168 // ensure the response from UDP server 169 if readBytes == 0 { 170 fmt.Fprintf(os.Stderr, "OTHER: No data received from the server. Cannot guarantee the server received the request.\n") 171 os.Exit(1) 172 } 173 } 174 175 func parseUDPErrorAndExit(err error) { 176 neterr, ok := err.(net.Error) 177 if ok && neterr.Timeout() { 178 fmt.Fprintf(os.Stderr, "TIMEOUT: %v\n", err) 179 } else if strings.Contains(err.Error(), "connection refused") { 180 fmt.Fprintf(os.Stderr, "REFUSED: %v\n", err) 181 } else { 182 fmt.Fprintf(os.Stderr, "UNKNOWN: %v\n", err) 183 } 184 os.Exit(1) 185 }