youtubebeat/vendor/github.com/elastic/beats/metricbeat/module/system/socket/listeners.go

145 lines
3.7 KiB
Go

// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package socket
import (
"net"
)
// Direction indicates how a socket was initiated.
type Direction uint8
const (
_ Direction = iota
// Incoming indicates a connection was established from the outside to
// listening socket on this host.
Incoming
// Outgoing indicates a connection was established from this socket to an
// external listening socket.
Outgoing
// Listening indicates a socket that is listening.
Listening
)
var directionNames = map[Direction]string{
Incoming: "incoming",
Outgoing: "outgoing",
Listening: "listening",
}
func (d Direction) String() string {
if name, exists := directionNames[d]; exists {
return name
}
return "unknown"
}
// ipList is a list of IP addresses.
type ipList struct {
ips []net.IP
}
func (l *ipList) put(ip net.IP) { l.ips = append(l.ips, ip) }
// portTable is a mapping of port number to listening IP addresses.
type portTable map[int]*ipList
// protocolTable is a mapping of protocol numbers to listening ports.
type protocolTable map[uint8]portTable
// ListenerTable tracks sockets that are listening. It can then be used to
// identify if a socket is listening, incoming, or outgoing.
type ListenerTable struct {
data protocolTable
}
// NewListenerTable returns a new ListenerTable.
func NewListenerTable() *ListenerTable {
return &ListenerTable{
data: protocolTable{},
}
}
// Reset resets all data in the table.
func (t *ListenerTable) Reset() {
for _, ports := range t.data {
for port := range ports {
delete(ports, port)
}
}
}
// Put puts a new listening address into the table.
func (t *ListenerTable) Put(proto uint8, ip net.IP, port int) {
ports, exists := t.data[proto]
if !exists {
ports = portTable{}
t.data[proto] = ports
}
// Add port + addr to table.
interfaces, exists := ports[port]
if !exists {
interfaces = &ipList{}
ports[port] = interfaces
}
interfaces.put(ip)
}
// Direction returns whether the connection was incoming or outgoing based on
// the protocol and local address. It compares the given local address to the
// listeners in the table for the protocol and returns Incoming if there is a
// match. If remotePort is 0 then Listening is returned.
func (t *ListenerTable) Direction(
proto uint8,
localIP net.IP, localPort int,
remoteIP net.IP, remotePort int,
) Direction {
if remotePort == 0 {
return Listening
}
// Are there any listeners on the given protocol?
ports, exists := t.data[proto]
if !exists {
return Outgoing
}
// Is there any listener on the port?
interfaces, exists := ports[localPort]
if !exists {
return Outgoing
}
// Is there a listener that specific interface? OR
// Is there a listener on the "any" address (0.0.0.0 or ::)?
isIPv4 := localIP.To4() != nil
for _, ip := range interfaces.ips {
switch {
case ip.Equal(localIP):
return Incoming
case ip.Equal(net.IPv4zero) && isIPv4:
return Incoming
case ip.Equal(net.IPv6zero) && !isIPv4:
return Incoming
}
}
return Outgoing
}