// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package socktest provides utilities for socket testing.
package socktest import ( ) // A Switch represents a callpath point switch for socket system // calls. type Switch struct { once sync.Once fmu sync.RWMutex fltab map[FilterType]Filter smu sync.RWMutex sotab Sockets stats stats } func ( *Switch) () { .fltab = make(map[FilterType]Filter) .sotab = make(Sockets) .stats = make(stats) } // Stats returns a list of per-cookie socket statistics. func ( *Switch) () []Stat { var []Stat .smu.RLock() for , := range .stats { := * = append(, ) } .smu.RUnlock() return } // Sockets returns mappings of socket descriptor to socket status. func ( *Switch) () Sockets { .smu.RLock() := make(Sockets, len(.sotab)) for , := range .sotab { [] = } .smu.RUnlock() return } // A Cookie represents a 3-tuple of a socket; address family, socket // type and protocol number. type Cookie uint64 // Family returns an address family. func ( Cookie) () int { return int( >> 48) } // Type returns a socket type. func ( Cookie) () int { return int( << 16 >> 32) } // Protocol returns a protocol number. func ( Cookie) () int { return int( & 0xff) } func cookie(, , int) Cookie { return Cookie()<<48 | Cookie()&0xffffffff<<16 | Cookie()&0xff } // A Status represents the status of a socket. type Status struct { Cookie Cookie Err error // error status of socket system call SocketErr error // error status of socket by SO_ERROR } func ( Status) () string { return fmt.Sprintf("(%s, %s, %s): syscallerr=%v socketerr=%v", familyString(.Cookie.Family()), typeString(.Cookie.Type()), protocolString(.Cookie.Protocol()), .Err, .SocketErr) } // A Stat represents a per-cookie socket statistics. type Stat struct { Family int // address family Type int // socket type Protocol int // protocol number Opened uint64 // number of sockets opened Connected uint64 // number of sockets connected Listened uint64 // number of sockets listened Accepted uint64 // number of sockets accepted Closed uint64 // number of sockets closed OpenFailed uint64 // number of sockets open failed ConnectFailed uint64 // number of sockets connect failed ListenFailed uint64 // number of sockets listen failed AcceptFailed uint64 // number of sockets accept failed CloseFailed uint64 // number of sockets close failed } func ( Stat) () string { return fmt.Sprintf("(%s, %s, %s): opened=%d connected=%d listened=%d accepted=%d closed=%d openfailed=%d connectfailed=%d listenfailed=%d acceptfailed=%d closefailed=%d", familyString(.Family), typeString(.Type), protocolString(.Protocol), .Opened, .Connected, .Listened, .Accepted, .Closed, .OpenFailed, .ConnectFailed, .ListenFailed, .AcceptFailed, .CloseFailed) } type stats map[Cookie]*Stat func ( stats) ( Cookie) *Stat { , := [] if ! { = &Stat{Family: .Family(), Type: .Type(), Protocol: .Protocol()} [] = } return } // A FilterType represents a filter type. type FilterType int const ( FilterSocket FilterType = iota // for Socket FilterConnect // for Connect or ConnectEx FilterListen // for Listen FilterAccept // for Accept, Accept4 or AcceptEx FilterGetsockoptInt // for GetsockoptInt FilterClose // for Close or Closesocket ) // A Filter represents a socket system call filter. // // It will only be executed before a system call for a socket that has // an entry in internal table. // If the filter returns a non-nil error, the execution of system call // will be canceled and the system call function returns the non-nil // error. // It can return a non-nil [AfterFilter] for filtering after the // execution of the system call. type Filter func(*Status) (AfterFilter, error) func ( Filter) ( *Status) (AfterFilter, error) { if == nil { return nil, nil } return () } // An AfterFilter represents a socket system call filter after an // execution of a system call. // // It will only be executed after a system call for a socket that has // an entry in internal table. // If the filter returns a non-nil error, the system call function // returns the non-nil error. type AfterFilter func(*Status) error func ( AfterFilter) ( *Status) error { if == nil { return nil } return () } // Set deploys the socket system call filter f for the filter type t. func ( *Switch) ( FilterType, Filter) { .once.Do(.init) .fmu.Lock() .fltab[] = .fmu.Unlock() }