k8s.io/kubernetes@v1.29.3/test/images/agnhost/netexec/netexec.go (about) 1 /* 2 Copyright 2014 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 netexec 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "io" 24 "log" 25 "net" 26 "net/http" 27 "net/url" 28 "os" 29 "os/exec" 30 "os/signal" 31 "strconv" 32 "strings" 33 "sync/atomic" 34 "syscall" 35 "time" 36 37 "github.com/ishidawataru/sctp" 38 "github.com/spf13/cobra" 39 40 utilnet "k8s.io/apimachinery/pkg/util/net" 41 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 42 "k8s.io/apimachinery/pkg/util/sets" 43 netutils "k8s.io/utils/net" 44 ) 45 46 var ( 47 httpPort = 8080 48 udpPort = 8081 49 sctpPort = -1 50 shellPath = "/bin/sh" 51 serverReady = &atomicBool{0} 52 certFile = "" 53 privKeyFile = "" 54 httpOverride = "" 55 udpListenAddresses = "" 56 delayShutdown = 0 57 ) 58 59 const bindToAny = "" 60 61 // CmdNetexec is used by agnhost Cobra. 62 var CmdNetexec = &cobra.Command{ 63 Use: "netexec", 64 Short: "Creates HTTP(S), UDP, and (optionally) SCTP servers with various endpoints", 65 Long: `Starts a HTTP(S) server on given port with the following endpoints: 66 67 - /: Returns the request's timestamp. 68 - /clientip: Returns the request's IP address. 69 - /header: Returns the request's header value corresponding to the key provided or the entire 70 header marshalled as json, if no form value (key) is provided. 71 ("/header?key=X-Forwarded-For" or /header) 72 - /dial: Creates a given number of requests to the given host and port using the given protocol, 73 and returns a JSON with the fields "responses" (successful request responses) and "errors" ( 74 failed request responses). Returns "200 OK" status code if the last request succeeded, 75 "417 Expectation Failed" if it did not, or "400 Bad Request" if any of the endpoint's parameters 76 is invalid. The endpoint's parameters are: 77 - "host": The host that will be dialed. 78 - "port": The port that will be dialed. 79 - "request": The HTTP endpoint or data to be sent through UDP. If not specified, it will result 80 in a "400 Bad Request" status code being returned. 81 - "protocol": The protocol which will be used when making the request. Default value: "http". 82 Acceptable values: "http", "udp", "sctp". 83 - "tries": The number of times the request will be performed. Default value: "1". 84 - "/echo": Returns the given "msg" ("/echo?msg=echoed_msg"), with the optional status "code". 85 - "/exit": Closes the server with the given code and graceful shutdown. The endpoint's parameters 86 are: 87 - "code": The exit code for the process. Default value: 0. Allows an integer [0-127]. 88 - "timeout": The amount of time to wait for connections to close before shutting down. 89 Acceptable values are golang durations. If 0 the process will exit immediately without 90 shutdown. 91 - "wait": The amount of time to wait before starting shutdown. Acceptable values are 92 golang durations. If 0 the process will start shutdown immediately. 93 - "/healthz": Returns "200 OK" if the server is healthy, "412 Status Precondition Failed" 94 otherwise. The server is considered not ready if the UDP server did not start yet or 95 it exited. 96 - "/readyz": Returns "200 OK" if the server is ready to receive traffic, "412 Status Precondition Failed", if the 97 server is not yet ready to receive traffic, but may be ready later, and "503" if the server is shutting down. 98 When a sig-term is observed, the /readyz will report 503, but healthz will report 200 to indicate that the 99 server is healthy (don't kill it), but the it should not be sent traffic (remove from endpoints). 100 - "/hostname": Returns the server's hostname. 101 - "/hostName": Returns the server's hostname. 102 - "/redirect": Returns a redirect response to the given "location", with the optional status "code" 103 ("/redirect?location=/echo%3Fmsg=foobar&code=307"). 104 - "/shell": Executes the given "shellCommand" or "cmd" ("/shell?cmd=some-command") and 105 returns a JSON containing the fields "output" (command's output) and "error" (command's 106 error message). Returns "200 OK" if the command succeeded, "417 Expectation Failed" if not. 107 - "/shutdown": Closes the server with the exit code 0. 108 - "/upload": Accepts a file to be uploaded, writing it in the "/uploads" folder on the host. 109 Returns a JSON with the fields "output" (containing the file's name on the server) and 110 "error" containing any potential server side errors. 111 112 If "--tls-cert-file" is added (ideally in conjunction with "--tls-private-key-file", the HTTP server 113 will be upgraded to HTTPS. The image has default, "localhost"-based cert/privkey files at 114 "/localhost.crt" and "/localhost.key" (see: "porter" subcommand) 115 116 If "--http-override" is set, the HTTP(S) server will always serve the override path & options, 117 ignoring the request URL. 118 119 It will also start a UDP server on the indicated UDP port and addresses that responds to the following commands: 120 121 - "hostname": Returns the server's hostname 122 - "echo <msg>": Returns the given <msg> 123 - "clientip": Returns the request's IP address 124 125 The UDP server can be disabled by setting --udp-port to -1. 126 127 Additionally, if (and only if) --sctp-port is passed, it will start an SCTP server on that port, 128 responding to the same commands as the UDP server. 129 `, 130 Args: cobra.MaximumNArgs(0), 131 Run: main, 132 } 133 134 func init() { 135 CmdNetexec.Flags().IntVar(&httpPort, "http-port", 8080, "HTTP Listen Port") 136 CmdNetexec.Flags().StringVar(&certFile, "tls-cert-file", "", 137 "File containing an x509 certificate for HTTPS. (CA cert, if any, concatenated after server cert)") 138 CmdNetexec.Flags().StringVar(&privKeyFile, "tls-private-key-file", "", 139 "File containing an x509 private key matching --tls-cert-file") 140 CmdNetexec.Flags().IntVar(&udpPort, "udp-port", 8081, "UDP Listen Port") 141 CmdNetexec.Flags().IntVar(&sctpPort, "sctp-port", -1, "SCTP Listen Port") 142 CmdNetexec.Flags().StringVar(&httpOverride, "http-override", "", "Override the HTTP handler to always respond as if it were a GET with this path & params") 143 CmdNetexec.Flags().StringVar(&udpListenAddresses, "udp-listen-addresses", "", "A comma separated list of ip addresses the udp servers listen from") 144 CmdNetexec.Flags().IntVar(&delayShutdown, "delay-shutdown", 0, "Number of seconds to delay shutdown when receiving SIGTERM.") 145 } 146 147 // atomicBool uses load/store operations on an int32 to simulate an atomic boolean. 148 type atomicBool struct { 149 v int32 150 } 151 152 // set sets the int32 to the given boolean. 153 func (a *atomicBool) set(value bool) { 154 if value { 155 atomic.StoreInt32(&a.v, 1) 156 return 157 } 158 atomic.StoreInt32(&a.v, 0) 159 } 160 161 // get returns true if the int32 == 1 162 func (a *atomicBool) get() bool { 163 return atomic.LoadInt32(&a.v) == 1 164 } 165 166 func main(cmd *cobra.Command, args []string) { 167 exitCh := make(chan shutdownRequest) 168 169 sigTermReceived := make(chan struct{}) 170 go func() { 171 termCh := make(chan os.Signal, 1) 172 signal.Notify(termCh, syscall.SIGTERM) 173 174 <-termCh 175 close(sigTermReceived) 176 }() 177 178 go func() { 179 <-sigTermReceived 180 if delayShutdown > 0 { 181 log.Printf("Sleeping %d seconds before terminating...", delayShutdown) 182 time.Sleep(time.Duration(delayShutdown) * time.Second) 183 } 184 os.Exit(0) 185 }() 186 187 if httpOverride != "" { 188 mux := http.NewServeMux() 189 addRoutes(mux, sigTermReceived, exitCh) 190 191 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 192 overrideReq, err := http.NewRequestWithContext(r.Context(), "GET", httpOverride, nil) 193 if err != nil { 194 http.Error(w, fmt.Sprintf("override request failed: %v", err), http.StatusInternalServerError) 195 return 196 } 197 mux.ServeHTTP(w, overrideReq) 198 }) 199 } else { 200 addRoutes(http.DefaultServeMux, sigTermReceived, exitCh) 201 } 202 203 // UDP server 204 if udpPort != -1 { 205 udpBindTo, err := parseAddresses(udpListenAddresses) 206 if err != nil { 207 log.Fatal(err) 208 } 209 210 for _, address := range udpBindTo { 211 go startUDPServer(address, udpPort) 212 } 213 } 214 215 // SCTP server 216 if sctpPort != -1 { 217 go startSCTPServer(sctpPort) 218 } 219 220 server := &http.Server{Addr: fmt.Sprintf(":%d", httpPort)} 221 if len(certFile) > 0 { 222 startServer(server, exitCh, func() error { return server.ListenAndServeTLS(certFile, privKeyFile) }) 223 } else { 224 startServer(server, exitCh, server.ListenAndServe) 225 } 226 } 227 228 func addRoutes(mux *http.ServeMux, sigTermReceived chan struct{}, exitCh chan shutdownRequest) { 229 mux.HandleFunc("/", rootHandler) 230 mux.HandleFunc("/clientip", clientIPHandler) 231 mux.HandleFunc("/header", headerHandler) 232 mux.HandleFunc("/dial", dialHandler) 233 mux.HandleFunc("/echo", echoHandler) 234 mux.HandleFunc("/exit", func(w http.ResponseWriter, req *http.Request) { exitHandler(w, req, exitCh) }) 235 mux.HandleFunc("/healthz", healthzHandler) 236 mux.HandleFunc("/readyz", readyzHandler(sigTermReceived)) 237 mux.HandleFunc("/hostname", hostnameHandler) 238 mux.HandleFunc("/redirect", redirectHandler) 239 mux.HandleFunc("/shell", shellHandler) 240 mux.HandleFunc("/upload", uploadHandler) 241 // older handlers 242 mux.HandleFunc("/hostName", hostNameHandler) 243 mux.HandleFunc("/shutdown", shutdownHandler) 244 } 245 246 func startServer(server *http.Server, exitCh chan shutdownRequest, fn func() error) { 247 log.Printf("Started HTTP server on port %d", httpPort) 248 go func() { 249 re := <-exitCh 250 ctx, cancelFn := context.WithTimeout(context.Background(), re.timeout) 251 defer cancelFn() 252 err := server.Shutdown(ctx) 253 log.Printf("Graceful shutdown completed with: %v", err) 254 os.Exit(re.code) 255 }() 256 257 if err := fn(); err != nil { 258 if err == http.ErrServerClosed { 259 // wait until the goroutine calls os.Exit() 260 select {} 261 } 262 log.Fatal(err) 263 } 264 } 265 266 func rootHandler(w http.ResponseWriter, r *http.Request) { 267 log.Printf("GET /") 268 fmt.Fprintf(w, "NOW: %v", time.Now()) 269 } 270 271 func echoHandler(w http.ResponseWriter, r *http.Request) { 272 msg := r.FormValue("msg") 273 codeString := r.FormValue("code") 274 log.Printf("GET /echo?msg=%s&code=%s", msg, codeString) 275 if codeString != "" { 276 code, err := strconv.Atoi(codeString) 277 if err != nil && codeString != "" { 278 fmt.Fprintf(w, "argument 'code' must be an integer or empty, got %q\n", codeString) 279 return 280 } 281 w.WriteHeader(code) 282 } 283 fmt.Fprintf(w, "%s", msg) 284 } 285 286 func clientIPHandler(w http.ResponseWriter, r *http.Request) { 287 log.Printf("GET /clientip") 288 fmt.Fprintf(w, r.RemoteAddr) 289 } 290 func headerHandler(w http.ResponseWriter, r *http.Request) { 291 key := r.FormValue("key") 292 if key != "" { 293 log.Printf("GET /header?key=%s", key) 294 fmt.Fprintf(w, "%s", r.Header.Get(key)) 295 } else { 296 log.Printf("GET /header") 297 data, err := json.Marshal(r.Header) 298 if err != nil { 299 fmt.Fprintf(w, "error marshalling header, err: %v", err) 300 return 301 } 302 fmt.Fprintf(w, "%s", string(data)) 303 } 304 } 305 306 type shutdownRequest struct { 307 code int 308 timeout time.Duration 309 } 310 311 func exitHandler(w http.ResponseWriter, r *http.Request, exitCh chan<- shutdownRequest) { 312 waitString := r.FormValue("wait") 313 timeoutString := r.FormValue("timeout") 314 codeString := r.FormValue("code") 315 log.Printf("GET /exit?code=%s&timeout=%s&wait=%s", codeString, timeoutString, waitString) 316 timeout, err := time.ParseDuration(timeoutString) 317 if err != nil && timeoutString != "" { 318 fmt.Fprintf(w, "argument 'timeout' must be a valid golang duration or empty, got %q\n", timeoutString) 319 return 320 } 321 wait, err := time.ParseDuration(waitString) 322 if err != nil && waitString != "" { 323 fmt.Fprintf(w, "argument 'wait' must be a valid golang duration or empty, got %q\n", waitString) 324 return 325 } 326 code, err := strconv.Atoi(codeString) 327 if err != nil && codeString != "" { 328 fmt.Fprintf(w, "argument 'code' must be an integer [0-127] or empty, got %q\n", codeString) 329 return 330 } 331 log.Printf("Will begin shutdown in %s, allowing %s for connections to close, then will exit with %d", wait, timeout, code) 332 time.Sleep(wait) 333 if timeout == 0 { 334 os.Exit(code) 335 } 336 exitCh <- shutdownRequest{code: code, timeout: timeout} 337 } 338 339 func hostnameHandler(w http.ResponseWriter, r *http.Request) { 340 log.Printf("GET /hostname") 341 fmt.Fprint(w, getHostName()) 342 } 343 344 // healthHandler response with a 200 if the UDP server is ready. It also serves 345 // as a health check of the HTTP server by virtue of being a HTTP handler. 346 func healthzHandler(w http.ResponseWriter, r *http.Request) { 347 log.Printf("GET /healthz") 348 if serverReady.get() { 349 w.WriteHeader(200) 350 return 351 } 352 w.WriteHeader(http.StatusPreconditionFailed) 353 } 354 355 // readyzHandler response with a 200 if the UDP server is ready. It serves as a readyz that will return a 503 356 // once a sig-term has been received. This allows for graceful removal from endpoints during a pod delete flow. 357 func readyzHandler(sigTermReceived chan struct{}) func(w http.ResponseWriter, r *http.Request) { 358 return func(w http.ResponseWriter, r *http.Request) { 359 log.Printf("GET /readyz") 360 361 select { 362 case <-sigTermReceived: 363 w.WriteHeader(http.StatusServiceUnavailable) 364 if _, err := w.Write([]byte("shutting down")); err != nil { 365 utilruntime.HandleError(err) 366 } 367 return 368 369 default: 370 if serverReady.get() { 371 if _, err := w.Write([]byte("ok")); err != nil { 372 utilruntime.HandleError(err) 373 } 374 return 375 } 376 w.WriteHeader(http.StatusPreconditionFailed) 377 } 378 } 379 } 380 381 func shutdownHandler(w http.ResponseWriter, r *http.Request) { 382 log.Printf("GET /shutdown") 383 os.Exit(0) 384 } 385 386 func dialHandler(w http.ResponseWriter, r *http.Request) { 387 values, err := url.Parse(r.URL.RequestURI()) 388 if err != nil { 389 http.Error(w, fmt.Sprintf("%v", err), http.StatusBadRequest) 390 return 391 } 392 393 host := values.Query().Get("host") 394 port := values.Query().Get("port") 395 request := values.Query().Get("request") // hostName 396 protocol := values.Query().Get("protocol") 397 tryParam := values.Query().Get("tries") 398 log.Printf("GET /dial?host=%s&protocol=%s&port=%s&request=%s&tries=%s", host, protocol, port, request, tryParam) 399 tries := 1 400 if len(tryParam) > 0 { 401 tries, err = strconv.Atoi(tryParam) 402 } 403 if err != nil { 404 http.Error(w, fmt.Sprintf("tries parameter is invalid. %v", err), http.StatusBadRequest) 405 return 406 } 407 if len(request) == 0 { 408 http.Error(w, fmt.Sprintf("request parameter not specified. %v", err), http.StatusBadRequest) 409 return 410 } 411 412 hostPort := net.JoinHostPort(host, port) 413 var addr net.Addr 414 var dialer func(string, net.Addr) (string, error) 415 switch strings.ToLower(protocol) { 416 case "", "http": 417 dialer = dialHTTP 418 addr, err = net.ResolveTCPAddr("tcp", hostPort) 419 case "udp": 420 dialer = dialUDP 421 addr, err = net.ResolveUDPAddr("udp", hostPort) 422 case "sctp": 423 dialer = dialSCTP 424 addr, err = sctp.ResolveSCTPAddr("sctp", hostPort) 425 default: 426 http.Error(w, fmt.Sprintf("unsupported protocol. %s", protocol), http.StatusBadRequest) 427 return 428 } 429 if err != nil { 430 http.Error(w, fmt.Sprintf("host and/or port param are invalid. %v", err), http.StatusBadRequest) 431 return 432 } 433 434 errors := make([]string, 0) 435 responses := make([]string, 0) 436 var response string 437 for i := 0; i < tries; i++ { 438 response, err = dialer(request, addr) 439 if err != nil { 440 errors = append(errors, fmt.Sprintf("%v", err)) 441 } else { 442 responses = append(responses, response) 443 } 444 } 445 output := map[string][]string{} 446 if len(response) > 0 { 447 output["responses"] = responses 448 } 449 if len(errors) > 0 { 450 output["errors"] = errors 451 } 452 bytes, err := json.Marshal(output) 453 if err == nil { 454 fmt.Fprint(w, string(bytes)) 455 } else { 456 http.Error(w, fmt.Sprintf("response could not be serialized. %v", err), http.StatusExpectationFailed) 457 } 458 } 459 460 func dialHTTP(request string, addr net.Addr) (string, error) { 461 transport := utilnet.SetTransportDefaults(&http.Transport{}) 462 httpClient := createHTTPClient(transport) 463 resp, err := httpClient.Get(fmt.Sprintf("http://%s/%s", addr.String(), request)) 464 defer transport.CloseIdleConnections() 465 if err == nil { 466 defer resp.Body.Close() 467 body, err := io.ReadAll(resp.Body) 468 if err == nil { 469 return string(body), nil 470 } 471 } 472 return "", err 473 } 474 475 func createHTTPClient(transport *http.Transport) *http.Client { 476 client := &http.Client{ 477 Transport: transport, 478 Timeout: 5 * time.Second, 479 } 480 return client 481 } 482 483 func dialUDP(request string, addr net.Addr) (string, error) { 484 Conn, err := net.DialUDP("udp", nil, addr.(*net.UDPAddr)) 485 if err != nil { 486 return "", fmt.Errorf("udp dial failed. err:%v", err) 487 } 488 489 defer Conn.Close() 490 buf := []byte(request) 491 _, err = Conn.Write(buf) 492 if err != nil { 493 return "", fmt.Errorf("udp connection write failed. err:%v", err) 494 } 495 udpResponse := make([]byte, 2048) 496 Conn.SetReadDeadline(time.Now().Add(5 * time.Second)) 497 count, err := Conn.Read(udpResponse) 498 if err != nil || count == 0 { 499 return "", fmt.Errorf("reading from udp connection failed. err:'%v'", err) 500 } 501 return string(udpResponse[0:count]), nil 502 } 503 504 func dialSCTP(request string, addr net.Addr) (string, error) { 505 Conn, err := sctp.DialSCTP("sctp", nil, addr.(*sctp.SCTPAddr)) 506 if err != nil { 507 return "", fmt.Errorf("sctp dial failed. err:%v", err) 508 } 509 510 defer Conn.Close() 511 buf := []byte(request) 512 _, err = Conn.Write(buf) 513 if err != nil { 514 return "", fmt.Errorf("sctp connection write failed. err:%v", err) 515 } 516 sctpResponse := make([]byte, 1024) 517 Conn.SetReadDeadline(time.Now().Add(5 * time.Second)) 518 count, err := Conn.Read(sctpResponse) 519 if err != nil || count == 0 { 520 return "", fmt.Errorf("reading from sctp connection failed. err:'%v'", err) 521 } 522 return string(sctpResponse[0:count]), nil 523 } 524 525 func shellHandler(w http.ResponseWriter, r *http.Request) { 526 cmd := r.FormValue("shellCommand") 527 if cmd == "" { 528 cmd = r.FormValue("cmd") 529 } 530 log.Printf("GET /shell?cmd=%s", cmd) 531 cmdOut, err := exec.Command(shellPath, "-c", cmd).CombinedOutput() 532 output := map[string]string{} 533 if len(cmdOut) > 0 { 534 output["output"] = string(cmdOut) 535 } 536 if err != nil { 537 output["error"] = fmt.Sprintf("%v", err) 538 } 539 log.Printf("Output: %s", output) 540 bytes, err := json.Marshal(output) 541 if err == nil { 542 fmt.Fprint(w, string(bytes)) 543 } else { 544 http.Error(w, fmt.Sprintf("response could not be serialized. %v", err), http.StatusExpectationFailed) 545 } 546 } 547 548 func uploadHandler(w http.ResponseWriter, r *http.Request) { 549 log.Printf("GET /upload") 550 result := map[string]string{} 551 file, _, err := r.FormFile("file") 552 if err != nil { 553 result["error"] = "Unable to upload file." 554 bytes, err := json.Marshal(result) 555 if err == nil { 556 fmt.Fprint(w, string(bytes)) 557 } else { 558 http.Error(w, fmt.Sprintf("%s. Also unable to serialize output. %v", result["error"], err), http.StatusInternalServerError) 559 } 560 log.Printf("Unable to upload file: %s", err) 561 return 562 } 563 defer file.Close() 564 565 f, err := os.CreateTemp("/uploads", "upload") 566 if err != nil { 567 result["error"] = "Unable to open file for write" 568 bytes, err := json.Marshal(result) 569 if err == nil { 570 fmt.Fprint(w, string(bytes)) 571 } else { 572 http.Error(w, fmt.Sprintf("%s. Also unable to serialize output. %v", result["error"], err), http.StatusInternalServerError) 573 } 574 log.Printf("Unable to open file for write: %s", err) 575 return 576 } 577 defer f.Close() 578 if _, err = io.Copy(f, file); err != nil { 579 result["error"] = "Unable to write file." 580 bytes, err := json.Marshal(result) 581 if err == nil { 582 fmt.Fprint(w, string(bytes)) 583 } else { 584 http.Error(w, fmt.Sprintf("%s. Also unable to serialize output. %v", result["error"], err), http.StatusInternalServerError) 585 } 586 log.Printf("Unable to write file: %s", err) 587 return 588 } 589 590 UploadFile := f.Name() 591 if err := os.Chmod(UploadFile, 0700); err != nil { 592 result["error"] = "Unable to chmod file." 593 bytes, err := json.Marshal(result) 594 if err == nil { 595 fmt.Fprint(w, string(bytes)) 596 } else { 597 http.Error(w, fmt.Sprintf("%s. Also unable to serialize output. %v", result["error"], err), http.StatusInternalServerError) 598 } 599 log.Printf("Unable to chmod file: %s", err) 600 return 601 } 602 log.Printf("Wrote upload to %s", UploadFile) 603 result["output"] = UploadFile 604 w.WriteHeader(http.StatusCreated) 605 bytes, err := json.Marshal(result) 606 if err != nil { 607 http.Error(w, fmt.Sprintf("%s. Also unable to serialize output. %v", result["error"], err), http.StatusInternalServerError) 608 return 609 } 610 fmt.Fprint(w, string(bytes)) 611 } 612 613 func hostNameHandler(w http.ResponseWriter, r *http.Request) { 614 log.Printf("GET /hostName") 615 fmt.Fprint(w, getHostName()) 616 } 617 618 func redirectHandler(w http.ResponseWriter, r *http.Request) { 619 location := r.FormValue("location") 620 codeString := r.FormValue("code") 621 log.Printf("%s /redirect?msg=%s&code=%s", r.Method, location, codeString) 622 code := http.StatusFound 623 if codeString != "" { 624 var err error 625 code, err = strconv.Atoi(codeString) 626 if err != nil && codeString != "" { 627 fmt.Fprintf(w, "argument 'code' must be an integer or empty, got %q\n", codeString) 628 return 629 } 630 } 631 http.Redirect(w, r, location, code) 632 } 633 634 // udp server supports the hostName, echo and clientIP commands. 635 func startUDPServer(address string, udpPort int) { 636 serverAddress, err := net.ResolveUDPAddr("udp", net.JoinHostPort(address, strconv.Itoa(udpPort))) 637 assertNoError(err, fmt.Sprintf("failed to resolve UDP address for port %d", udpPort)) 638 serverConn, err := net.ListenUDP("udp", serverAddress) 639 assertNoError(err, fmt.Sprintf("failed to create listener for UDP address %v", serverAddress)) 640 defer serverConn.Close() 641 buf := make([]byte, 2048) 642 643 log.Printf("Started UDP server on port %s %d", address, udpPort) 644 // Start responding to readiness probes. 645 serverReady.set(true) 646 defer func() { 647 log.Printf("UDP server exited") 648 serverReady.set(false) 649 }() 650 for { 651 n, clientAddress, err := serverConn.ReadFromUDP(buf) 652 assertNoError(err, fmt.Sprintf("failed accepting UDP connections")) 653 receivedText := strings.ToLower(strings.TrimSpace(string(buf[0:n]))) 654 if receivedText == "hostname" { 655 log.Println("Sending udp hostName response") 656 _, err = serverConn.WriteToUDP([]byte(getHostName()), clientAddress) 657 assertNoError(err, fmt.Sprintf("failed to write hostname to UDP client %s", clientAddress)) 658 } else if strings.HasPrefix(receivedText, "echo ") { 659 parts := strings.SplitN(receivedText, " ", 2) 660 resp := "" 661 if len(parts) == 2 { 662 resp = parts[1] 663 } 664 log.Printf("Echoing %v to UDP client %s\n", resp, clientAddress) 665 _, err = serverConn.WriteToUDP([]byte(resp), clientAddress) 666 assertNoError(err, fmt.Sprintf("failed to echo to UDP client %s", clientAddress)) 667 } else if receivedText == "clientip" { 668 log.Printf("Sending clientip back to UDP client %s\n", clientAddress) 669 _, err = serverConn.WriteToUDP([]byte(clientAddress.String()), clientAddress) 670 assertNoError(err, fmt.Sprintf("failed to write clientip to UDP client %s", clientAddress)) 671 } else if len(receivedText) > 0 { 672 log.Printf("Unknown UDP command received from %s: %v\n", clientAddress, receivedText) 673 } 674 } 675 } 676 677 // sctp server supports the hostName, echo and clientIP commands. 678 func startSCTPServer(sctpPort int) { 679 serverAddress, err := sctp.ResolveSCTPAddr("sctp", fmt.Sprintf(":%d", sctpPort)) 680 assertNoError(err, fmt.Sprintf("failed to resolve SCTP address for port %d", sctpPort)) 681 listener, err := sctp.ListenSCTP("sctp", serverAddress) 682 assertNoError(err, fmt.Sprintf("failed to create listener for SCTP address %v", serverAddress)) 683 defer listener.Close() 684 buf := make([]byte, 1024) 685 686 log.Printf("Started SCTP server") 687 // Start responding to readiness probes. 688 serverReady.set(true) 689 defer func() { 690 log.Printf("SCTP server exited") 691 serverReady.set(false) 692 }() 693 for { 694 conn, err := listener.AcceptSCTP() 695 assertNoError(err, fmt.Sprintf("failed accepting SCTP connections")) 696 clientAddress := conn.RemoteAddr().String() 697 n, err := conn.Read(buf) 698 assertNoError(err, fmt.Sprintf("failed to read from SCTP client %s", clientAddress)) 699 receivedText := strings.ToLower(strings.TrimSpace(string(buf[0:n]))) 700 if receivedText == "hostname" { 701 log.Println("Sending SCTP hostName response") 702 _, err = conn.Write([]byte(getHostName())) 703 assertNoError(err, fmt.Sprintf("failed to write hostname to SCTP client %s", clientAddress)) 704 } else if strings.HasPrefix(receivedText, "echo ") { 705 parts := strings.SplitN(receivedText, " ", 2) 706 resp := "" 707 if len(parts) == 2 { 708 resp = parts[1] 709 } 710 log.Printf("Echoing %v to SCTP client %s\n", resp, clientAddress) 711 _, err = conn.Write([]byte(resp)) 712 assertNoError(err, fmt.Sprintf("failed to echo to SCTP client %s", clientAddress)) 713 } else if receivedText == "clientip" { 714 log.Printf("Sending clientip back to SCTP client %s\n", clientAddress) 715 _, err = conn.Write([]byte(clientAddress)) 716 assertNoError(err, fmt.Sprintf("failed to write clientip to SCTP client %s", clientAddress)) 717 } else if len(receivedText) > 0 { 718 log.Printf("Unknown SCTP command received from %s: %v\n", clientAddress, receivedText) 719 } 720 conn.Close() 721 } 722 } 723 724 func getHostName() string { 725 hostName, err := os.Hostname() 726 assertNoError(err, "failed to get hostname") 727 return hostName 728 } 729 730 func assertNoError(err error, detail string) { 731 if err != nil { 732 log.Fatalf("Error occurred: %s:%v", detail, err) 733 } 734 } 735 736 func parseAddresses(addresses string) ([]string, error) { 737 if addresses == "" { 738 return []string{bindToAny}, nil 739 } 740 // Using a set to remove duplicates 741 res := make([]string, 0) 742 split := strings.Split(addresses, ",") 743 for _, address := range split { 744 netAddr := netutils.ParseIPSloppy(address) 745 if netAddr == nil { 746 return nil, fmt.Errorf("parseAddress: invalid address %s", address) 747 } 748 res = append(res, address) 749 } 750 set := sets.NewString(res...) 751 return set.List(), nil 752 }