This repository has been archived by the owner on Jan 28, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added a checker that will detect dead sockets before the timeout (Lin…
…ux only) Signed-off-by: Juanjo Alvarez <juanjo@sourced.tech>
- Loading branch information
Juanjo Alvarez
committed
Aug 16, 2019
1 parent
e742bea
commit e806bcd
Showing
13 changed files
with
768 additions
and
191 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package sockstate | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
) | ||
|
||
// OS independent part of the netstat_[OS].go modules | ||
// Taken (simplified, privatized and with utility functions added) from: | ||
// https://github.com/cakturk/go-netstat | ||
|
||
// skState type represents socket connection state | ||
type skState uint8 | ||
|
||
func (s skState) String() string { | ||
return skStates[s] | ||
} | ||
|
||
// Socket states | ||
const ( | ||
Established skState = 0x01 | ||
SynSent = 0x02 | ||
SynRecv = 0x03 | ||
FinWait1 = 0x04 | ||
FinWait2 = 0x05 | ||
TimeWait = 0x06 | ||
Close = 0x07 | ||
CloseWait = 0x08 | ||
LastAck = 0x09 | ||
Listen = 0x0a | ||
Closing = 0x0b | ||
) | ||
|
||
var skStates = [...]string{ | ||
"UNKNOWN", | ||
"ESTABLISHED", | ||
"SYN_SENT", | ||
"SYN_RECV", | ||
"FIN_WAIT1", | ||
"FIN_WAIT2", | ||
"TIME_WAIT", | ||
"", // CLOSE | ||
"CLOSE_WAIT", | ||
"LAST_ACK", | ||
"LISTEN", | ||
"CLOSING", | ||
} | ||
|
||
// sockAddr represents an ip:port pair | ||
type sockAddr struct { | ||
IP net.IP | ||
Port uint16 | ||
} | ||
|
||
func (s *sockAddr) String() string { | ||
return fmt.Sprintf("%v:%d", s.IP, s.Port) | ||
} | ||
|
||
// sockTabEntry type represents each line of the /proc/net/tcp | ||
type sockTabEntry struct { | ||
Ino string | ||
LocalAddr *sockAddr | ||
RemoteAddr *sockAddr | ||
State skState | ||
UID uint32 | ||
Process *process | ||
} | ||
|
||
// process holds the PID and process name to which each socket belongs | ||
type process struct { | ||
pid int | ||
name string | ||
} | ||
|
||
func (p *process) String() string { | ||
return fmt.Sprintf("%d/%s", p.pid, p.name) | ||
} | ||
|
||
// AcceptFn is used to filter socket entries. The value returned indicates | ||
// whether the element is to be appended to the socket list. | ||
type AcceptFn func(*sockTabEntry) bool | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package sockstate | ||
|
||
import "github.com/sirupsen/logrus" | ||
|
||
// tcpSocks returns a slice of active TCP sockets containing only those | ||
// elements that satisfy the accept function | ||
func tcpSocks(accept AcceptFn) ([]sockTabEntry, error) { | ||
// (juanjux) TODO: not implemented | ||
logrus.Info("Connection checking not implemented for Darwin") | ||
return []sockTabEntry{}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
package sockstate | ||
|
||
// Taken (simplified and with utility functions added) from https://github.com/cakturk/go-netstat | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"encoding/binary" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"log" | ||
"net" | ||
"os" | ||
"path" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
const ( | ||
pathTCPTab = "/proc/net/tcp" | ||
ipv4StrLen = 8 | ||
) | ||
|
||
type procFd struct { | ||
base string | ||
pid int | ||
sktab []sockTabEntry | ||
p *process | ||
} | ||
|
||
const sockPrefix = "socket:[" | ||
|
||
func getProcName(s []byte) string { | ||
i := bytes.Index(s, []byte("(")) | ||
if i < 0 { | ||
return "" | ||
} | ||
j := bytes.LastIndex(s, []byte(")")) | ||
if i < 0 { | ||
return "" | ||
} | ||
if i > j { | ||
return "" | ||
} | ||
return string(s[i+1 : j]) | ||
} | ||
|
||
func (p *procFd) iterFdDir() { | ||
// link name is of the form socket:[5860846] | ||
fddir := path.Join(p.base, "/fd") | ||
fi, err := ioutil.ReadDir(fddir) | ||
if err != nil { | ||
return | ||
} | ||
var buf [128]byte | ||
|
||
for _, file := range fi { | ||
fd := path.Join(fddir, file.Name()) | ||
lname, err := os.Readlink(fd) | ||
if err != nil { | ||
continue | ||
} | ||
|
||
for i := range p.sktab { | ||
sk := &p.sktab[i] | ||
ss := sockPrefix + sk.Ino + "]" | ||
if ss != lname { | ||
continue | ||
} | ||
if p.p == nil { | ||
stat, err := os.Open(path.Join(p.base, "stat")) | ||
if err != nil { | ||
return | ||
} | ||
n, err := stat.Read(buf[:]) | ||
_ = stat.Close() | ||
if err != nil { | ||
return | ||
} | ||
z := bytes.SplitN(buf[:n], []byte(" "), 3) | ||
name := getProcName(z[1]) | ||
p.p = &process{p.pid, name} | ||
} | ||
sk.Process = p.p | ||
} | ||
} | ||
} | ||
|
||
func extractProcInfo(sktab []sockTabEntry) { | ||
const basedir = "/proc" | ||
fi, err := ioutil.ReadDir(basedir) | ||
if err != nil { | ||
return | ||
} | ||
|
||
for _, file := range fi { | ||
if !file.IsDir() { | ||
continue | ||
} | ||
pid, err := strconv.Atoi(file.Name()) | ||
if err != nil { | ||
continue | ||
} | ||
base := path.Join(basedir, file.Name()) | ||
proc := procFd{base: base, pid: pid, sktab: sktab} | ||
proc.iterFdDir() | ||
} | ||
} | ||
|
||
func parseIPv4(s string) (net.IP, error) { | ||
v, err := strconv.ParseUint(s, 16, 32) | ||
if err != nil { | ||
return nil, err | ||
} | ||
ip := make(net.IP, net.IPv4len) | ||
binary.LittleEndian.PutUint32(ip, uint32(v)) | ||
return ip, nil | ||
} | ||
|
||
func parseAddr(s string) (*sockAddr, error) { | ||
fields := strings.Split(s, ":") | ||
if len(fields) < 2 { | ||
return nil, fmt.Errorf("sockstate: not enough fields: %v", s) | ||
} | ||
var ip net.IP | ||
var err error | ||
switch len(fields[0]) { | ||
case ipv4StrLen: | ||
ip, err = parseIPv4(fields[0]) | ||
default: | ||
log.Fatal("Bad formatted string") | ||
} | ||
if err != nil { | ||
return nil, err | ||
} | ||
v, err := strconv.ParseUint(fields[1], 16, 16) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &sockAddr{IP: ip, Port: uint16(v)}, nil | ||
} | ||
|
||
func parseSocktab(r io.Reader, accept AcceptFn) ([]sockTabEntry, error) { | ||
br := bufio.NewScanner(r) | ||
tab := make([]sockTabEntry, 0, 4) | ||
|
||
// Discard title | ||
br.Scan() | ||
|
||
for br.Scan() { | ||
var e sockTabEntry | ||
line := br.Text() | ||
// Skip comments | ||
if i := strings.Index(line, "#"); i >= 0 { | ||
line = line[:i] | ||
} | ||
fields := strings.Fields(line) | ||
if len(fields) < 12 { | ||
return nil, fmt.Errorf("sockstate: not enough fields: %v, %v", len(fields), fields) | ||
} | ||
addr, err := parseAddr(fields[1]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
e.LocalAddr = addr | ||
addr, err = parseAddr(fields[2]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
e.RemoteAddr = addr | ||
u, err := strconv.ParseUint(fields[3], 16, 8) | ||
if err != nil { | ||
return nil, err | ||
} | ||
e.State = skState(u) | ||
u, err = strconv.ParseUint(fields[7], 10, 32) | ||
if err != nil { | ||
return nil, err | ||
} | ||
e.UID = uint32(u) | ||
e.Ino = fields[9] | ||
if accept(&e) { | ||
tab = append(tab, e) | ||
} | ||
} | ||
return tab, br.Err() | ||
} | ||
|
||
// tcpSocks returns a slice of active TCP sockets containing only those | ||
// elements that satisfy the accept function | ||
func tcpSocks(accept AcceptFn) ([]sockTabEntry, error) { | ||
f, err := os.Open(pathTCPTab) | ||
defer f.Close() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
tabs, err := parseSocktab(f, accept) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
extractProcInfo(tabs) | ||
return tabs, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package sockstate | ||
|
||
import "github.com/sirupsen/logrus" | ||
|
||
// tcpSocks returns a slice of active TCP sockets containing only those | ||
// elements that satisfy the accept function | ||
func tcpSocks(accept AcceptFn) ([]sockTabEntry, error) { | ||
// (juanjux) TODO: not implemented | ||
logrus.Info("Connection checking not implemented for Windows") | ||
return []sockTabEntry{}, nil | ||
} |
Oops, something went wrong.