Files
wehub-resource-sync 36b3af2e3d
PR Check / Code Quality: Format (push) Failing after 1s
PR Check / Code Quality: Lint (darwin) (push) Failing after 0s
PR Check / Code Quality: Lint (freebsd) (push) Failing after 1s
PR Check / Code Quality: Lint (windows) (push) Failing after 1s
PR Check / Code Quality: Lint (linux) (push) Failing after 1s
PR Check / Security: Vulnerability Scan (push) Failing after 0s
Update Documentation / update-docs (push) Failing after 2s
PR Check / Code Quality: Vendor (push) Failing after 1s
PR Check / Code Quality: Coverage (push) Failing after 0s
PR Check / Tests: Unit (macos-latest) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04) (push) Has been cancelled
PR Check / Tests: Unit (ubuntu-24.04-arm) (push) Has been cancelled
PR Check / Tests: Unit (windows-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:22:06 +08:00

157 lines
3.4 KiB
Go

//go:build linux
package proc
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"github.com/pranshuparmar/witr/pkg/model"
)
// GetSocketStateForPort returns the socket state for a port
// Linux implementation using /proc/net/tcp and /proc/net/tcp6
func GetSocketStateForPort(port int) *model.SocketInfo {
// Check both IPv4 and IPv6
files := []string{"/proc/net/tcp", "/proc/net/tcp6"}
var states []model.SocketInfo
for _, file := range files {
isIPv6 := strings.HasSuffix(file, "tcp6")
func() {
f, err := os.Open(file)
if err != nil {
return
}
defer f.Close()
scanner := bufio.NewScanner(f)
scanner.Scan()
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
if len(fields) < 10 {
continue
}
localAddrHex := fields[1]
localIP, localPort := parseAddr(localAddrHex, isIPv6)
if localPort != port {
continue
}
remoteAddrHex := fields[2]
remoteIP, _ := parseAddr(remoteAddrHex, isIPv6)
stateHex := fields[3]
stateVal, _ := strconv.ParseInt(stateHex, 16, 0)
stateStr := mapTCPState(int(stateVal))
info := model.SocketInfo{
Port: port,
State: stateStr,
LocalAddr: localIP,
RemoteAddr: remoteIP,
}
addStateExplanation(&info)
states = append(states, info)
}
}()
}
if len(states) == 0 {
return nil
}
// Prioritize problematic states just like the Darwin implementation
for _, s := range states {
if isProblematicState(s.State) {
return &s
}
}
// Then prioritize LISTEN
for _, s := range states {
if s.State == "LISTEN" {
return &s
}
}
// Default to first found
return &states[0]
}
// mapTCPState maps Linux kernel TCP states (from include/net/tcp_states.h) to strings
func mapTCPState(state int) string {
switch state {
case 1:
return "ESTABLISHED"
case 2:
return "SYN_SENT"
case 3:
return "SYN_RECV"
case 4:
return "FIN_WAIT_1"
case 5:
return "FIN_WAIT_2"
case 6:
return "TIME_WAIT"
case 7:
return "CLOSE"
case 8:
return "CLOSE_WAIT"
case 9:
return "LAST_ACK"
case 10:
return "LISTEN"
case 11:
return "CLOSING"
default:
return fmt.Sprintf("UNKNOWN (%02X)", state)
}
}
func isProblematicState(state string) bool {
switch state {
case "TIME_WAIT", "CLOSE_WAIT", "FIN_WAIT_1", "FIN_WAIT_2":
return true
}
return false
}
func addStateExplanation(info *model.SocketInfo) {
switch info.State {
case "LISTEN":
info.Explanation = "Actively listening for connections"
case "TIME_WAIT":
info.Explanation = "Connection closed, waiting for delayed packets"
info.Workaround = "Wait for timeout (usually 60s) or use SO_REUSEADDR"
case "CLOSE_WAIT":
info.Explanation = "Remote side closed connection, local side has not closed yet"
info.Workaround = "The application should call close() on the socket"
case "FIN_WAIT_1":
info.Explanation = "Local side initiated close, waiting for acknowledgment"
case "FIN_WAIT_2":
info.Explanation = "Local close acknowledged, waiting for remote close"
case "ESTABLISHED":
info.Explanation = "Active connection"
case "SYN_SENT":
info.Explanation = "Connection request sent, waiting for response"
case "SYN_RECEIVED":
info.Explanation = "Connection request received, sending acknowledgment"
case "CLOSING":
info.Explanation = "Both sides initiated close simultaneously"
case "LAST_ACK":
info.Explanation = "Waiting for final acknowledgment of close"
default:
info.Explanation = "Socket in " + info.State + " state"
}
}