Files
troubleshoot/pkg/collect/host_network.go
Andrew Lavery f18b5d754e update k8s imports to v0.30.0 and address changed function signature (#1528)
* update k8s imports to v0.30.0 and address changed function signature

* update schemas
2024-04-24 23:05:37 +09:00

212 lines
5.0 KiB
Go

package collect
import (
"bytes"
"net"
"regexp"
"strconv"
"strings"
"time"
"github.com/pkg/errors"
"github.com/replicatedhq/troubleshoot/pkg/debug"
"github.com/segmentio/ksuid"
validation "k8s.io/apimachinery/pkg/util/validation"
)
type NetworkStatus string
const (
NetworkStatusAddressInUse = "address-in-use"
NetworkStatusConnectionRefused = "connection-refused"
NetworkStatusConnectionTimeout = "connection-timeout"
NetworkStatusConnected = "connected"
NetworkStatusErrorOther = "error"
NetworkStatusBindPermissionDenied = "bind-permission-denied"
NetworkStatusInvalidAddress = "invalid-address"
)
type NetworkStatusResult struct {
Status NetworkStatus `json:"status"`
Message string `json:"message"`
}
var ipRegexp = regexp.MustCompile(`^[0-9.]+$`)
func isValidLoadBalancerAddress(address string) bool {
splitString := strings.Split(address, ":")
if len(splitString) != 2 { // should be hostAddress:port
return false
}
hostAddress := splitString[0]
port, err := strconv.Atoi(splitString[1])
if err != nil {
return false
}
portErrors := validation.IsValidPortNum(port)
if len(portErrors) > 0 {
return false
}
// Checking for uppercase letters
if strings.ToLower(hostAddress) != hostAddress {
return false
}
// Checking if it's all numbers and .
if ipRegexp.MatchString(hostAddress) {
// Check for isValidIP
test := validation.IsValidIP(nil, hostAddress)
return len(test) == 0
}
errs := validation.IsQualifiedName(hostAddress)
return len(errs) == 0
}
func checkTCPConnection(progressChan chan<- interface{}, listenAddress string, dialAddress string, timeout time.Duration) (NetworkStatus, error) {
if !isValidLoadBalancerAddress(dialAddress) {
return NetworkStatusInvalidAddress, errors.Errorf("Invalid Load Balancer Address: %v", dialAddress)
}
lstn, err := net.Listen("tcp", listenAddress)
if err != nil {
if strings.Contains(err.Error(), "address already in use") {
return NetworkStatusAddressInUse, nil
}
return NetworkStatusErrorOther, errors.Wrap(err, "failed to create listener")
}
defer lstn.Close()
// The server may receive requests from other clients and the client request may be forwarded to
// other servers. The client must continue to initiate new connections and send its request
// token until the server responds with its token.
requestToken := ksuid.New().Bytes()
responseToken := ksuid.New().Bytes()
go func() {
for {
conn, err := lstn.Accept()
if err != nil {
continue
}
if handleTestConnection(conn, requestToken, responseToken) {
return
}
}
}()
stopAfter := time.Now().Add(timeout)
for {
if time.Now().After(stopAfter) {
debug.Printf("Timeout")
return NetworkStatusConnectionTimeout, nil
}
conn, err := net.DialTimeout("tcp", dialAddress, 50*time.Millisecond)
if err != nil {
debug.Printf("Error: %s", err)
if strings.Contains(err.Error(), "i/o timeout") {
progressChan <- err
time.Sleep(time.Second)
continue
}
if strings.Contains(err.Error(), "connection refused") {
return NetworkStatusConnectionRefused, nil
}
return NetworkStatusErrorOther, errors.Wrap(err, "failed to dial")
}
if verifyConnectionToServer(conn, requestToken, responseToken) {
return NetworkStatusConnected, nil
}
progressChan <- errors.New("failed to verify connection to server")
time.Sleep(time.Second)
}
}
func handleTestConnection(conn net.Conn, requestToken []byte, responseToken []byte) bool {
defer func() {
if err := conn.Close(); err != nil {
debug.Println(err.Error())
}
}()
if err := conn.SetReadDeadline(time.Now().Add(50 * time.Millisecond)); err != nil {
debug.Printf("Server failed to set read deadline: %v\n", err)
return false
}
buf := make([]byte, 1024)
_, err := conn.Read(buf)
if err != nil {
debug.Printf("Server failed to read: %v\n", err)
return false
}
if !bytes.Contains(buf, requestToken) {
return false
}
if err := conn.SetWriteDeadline(time.Now().Add(50 * time.Millisecond)); err != nil {
debug.Printf("Server failed to set write deadline: %v\n", err)
return false
}
if _, err := conn.Write(responseToken); err != nil {
debug.Printf("Server failed to write: %v\n", err)
return false
}
return true
}
func verifyConnectionToServer(conn net.Conn, requestToken []byte, responseToken []byte) bool {
defer func() {
if err := conn.Close(); err != nil {
debug.Println(err.Error())
}
}()
if err := conn.SetWriteDeadline(time.Now().Add(50 * time.Millisecond)); err != nil {
debug.Printf("Client failed to set write deadline: %v\n", err)
return false
}
_, err := conn.Write(requestToken)
if err != nil {
return false
}
if err := conn.SetReadDeadline(time.Now().Add(50 * time.Millisecond)); err != nil {
debug.Printf("Client failed to set read deadline: %v\n", err)
return false
}
buf := make([]byte, 1024)
_, err = conn.Read(buf)
if err != nil {
return false
}
if !bytes.Contains(buf, responseToken) {
return false
}
return true
}