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  }