|
3 | 3 | package netutil |
4 | 4 |
|
5 | 5 | import ( |
| 6 | + "bytes" |
| 7 | + "encoding/hex" |
6 | 8 | "fmt" |
| 9 | + "io/ioutil" |
| 10 | + "log" |
| 11 | + "net" |
| 12 | + "strconv" |
| 13 | + "strings" |
7 | 14 |
|
8 | 15 | "github.com/elastic/gosigar/sys/linux" |
| 16 | + |
| 17 | + gnet "github.com/shirou/gopsutil/net" |
9 | 18 | ) |
10 | 19 |
|
11 | 20 | // NetlinkConnections returns connection stats. |
@@ -48,3 +57,111 @@ func NetlinkLocalListeningPorts() ([]string, error) { |
48 | 57 | } |
49 | 58 | return ports, nil |
50 | 59 | } |
| 60 | + |
| 61 | +const ( |
| 62 | + tcpProcFilename = "/proc/net/tcp" |
| 63 | +) |
| 64 | + |
| 65 | +// Addr is <addr>:<port>. |
| 66 | +type Addr struct { |
| 67 | + IP string `json:"ip"` |
| 68 | + Port uint32 `json:"port"` |
| 69 | +} |
| 70 | + |
| 71 | +// ConnectionStat represents staticstics for a connection. |
| 72 | +type ConnectionStat struct { |
| 73 | + Laddr Addr |
| 74 | + Raddr Addr |
| 75 | + Status linux.TCPState |
| 76 | +} |
| 77 | + |
| 78 | +// ProcfsConnections returns connection stats. |
| 79 | +// ref. https://github.com/shirou/gopsutil/blob/c23bcca55e77b8389d84b09db8c5ac2b472070ef/net/net_linux.go#L656 |
| 80 | +func ProcfsConnections() ([]*ConnectionStat, error) { |
| 81 | + body, err := ioutil.ReadFile(tcpProcFilename) |
| 82 | + if err != nil { |
| 83 | + return nil, err |
| 84 | + } |
| 85 | + |
| 86 | + lines := bytes.Split(body, []byte("\n")) |
| 87 | + conns := make([]*ConnectionStat, 0, len(lines)-1) |
| 88 | + for _, line := range lines[1:] { |
| 89 | + l := strings.Fields(string(line)) |
| 90 | + if len(l) < 10 { |
| 91 | + continue |
| 92 | + } |
| 93 | + laddr := l[1] |
| 94 | + raddr := l[2] |
| 95 | + status, err := strconv.ParseUint(l[3], 16, 8) |
| 96 | + if err != nil { |
| 97 | + log.Printf("decode error: %v", err) |
| 98 | + } |
| 99 | + la, err := decodeAddress(laddr) |
| 100 | + if err != nil { |
| 101 | + continue |
| 102 | + } |
| 103 | + ra, err := decodeAddress(raddr) |
| 104 | + if err != nil { |
| 105 | + continue |
| 106 | + } |
| 107 | + |
| 108 | + conns = append(conns, &ConnectionStat{ |
| 109 | + Laddr: la, |
| 110 | + Raddr: ra, |
| 111 | + Status: linux.TCPState(status), |
| 112 | + }) |
| 113 | + } |
| 114 | + |
| 115 | + return conns, nil |
| 116 | +} |
| 117 | + |
| 118 | +// decodeAddress decode addresse represents addr in proc/net/* |
| 119 | +// ex: |
| 120 | +// "0500000A:0016" -> "10.0.0.5", 22 |
| 121 | +// "0085002452100113070057A13F025401:0035" -> "2400:8500:1301:1052:a157:7:154:23f", 53 |
| 122 | +// ref. https://github.com/shirou/gopsutil/blob/c23bcca55e77b8389d84b09db8c5ac2b472070ef/net/net_linux.go#L600 |
| 123 | +func decodeAddress(src string) (Addr, error) { |
| 124 | + t := strings.Split(src, ":") |
| 125 | + if len(t) != 2 { |
| 126 | + return Addr{}, fmt.Errorf("does not contain port, %s", src) |
| 127 | + } |
| 128 | + addr := t[0] |
| 129 | + port, err := strconv.ParseInt("0x"+t[1], 0, 64) |
| 130 | + if err != nil { |
| 131 | + return Addr{}, fmt.Errorf("invalid port, %s", src) |
| 132 | + } |
| 133 | + decoded, err := hex.DecodeString(addr) |
| 134 | + if err != nil { |
| 135 | + return Addr{}, fmt.Errorf("decode error, %s", err) |
| 136 | + } |
| 137 | + var ip net.IP |
| 138 | + // Assumes this is little_endian |
| 139 | + ip = net.IP(gnet.Reverse(decoded)) |
| 140 | + return Addr{ |
| 141 | + IP: ip.String(), |
| 142 | + Port: uint32(port), |
| 143 | + }, nil |
| 144 | +} |
| 145 | + |
| 146 | +// FilterByLocalListeningPorts filters ConnectionStat slice by the local listening ports. |
| 147 | +func FilterByLocalListeningPorts(conns []*ConnectionStat) ([]string, error) { |
| 148 | + ports := []string{} |
| 149 | + for _, conn := range conns { |
| 150 | + if conn.Status != linux.TCP_LISTEN { |
| 151 | + continue |
| 152 | + } |
| 153 | + if conn.Laddr.IP == "0.0.0.0" || conn.Laddr.IP == "127.0.0.1" || conn.Laddr.IP == "::" { |
| 154 | + ports = append(ports, fmt.Sprintf("%d", conn.Laddr.Port)) |
| 155 | + } |
| 156 | + } |
| 157 | + return ports, nil |
| 158 | +} |
| 159 | + |
| 160 | +// LocalListeningPorts returns the local listening ports. |
| 161 | +func LocalListeningPorts() ([]string, error) { |
| 162 | + conns, err := ProcfsConnections() |
| 163 | + if err != nil { |
| 164 | + return nil, err |
| 165 | + } |
| 166 | + return FilterByLocalListeningPorts(conns) |
| 167 | +} |
0 commit comments