package moved
This commit is contained in:
95
internal/matcher/matcher.go
Normal file
95
internal/matcher/matcher.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package matcher
|
||||
|
||||
import (
|
||||
osm "git.loafle.net/overflow/service_matcher-go"
|
||||
"git.loafle.net/overflow/service_matcher-go/elasticsearch"
|
||||
"git.loafle.net/overflow/service_matcher-go/ftp"
|
||||
"git.loafle.net/overflow/service_matcher-go/http"
|
||||
"git.loafle.net/overflow/service_matcher-go/ldap"
|
||||
"git.loafle.net/overflow/service_matcher-go/lpd"
|
||||
"git.loafle.net/overflow/service_matcher-go/mongodb"
|
||||
"git.loafle.net/overflow/service_matcher-go/mysql"
|
||||
"git.loafle.net/overflow/service_matcher-go/nbss"
|
||||
"git.loafle.net/overflow/service_matcher-go/postgresql"
|
||||
"git.loafle.net/overflow/service_matcher-go/redis"
|
||||
"git.loafle.net/overflow/service_matcher-go/ssh"
|
||||
"git.loafle.net/overflow/service_matcher-go/telnet"
|
||||
)
|
||||
|
||||
var (
|
||||
AllMatchers []osm.Matcher
|
||||
|
||||
TCPMatchers []osm.Matcher
|
||||
UDPMatchers []osm.UDPMatcher
|
||||
|
||||
TCPPrePacketMatchers []osm.Matcher
|
||||
TCPNotPrePacketMatchers []osm.Matcher
|
||||
|
||||
HTTPSubMatchers []osm.Matcher
|
||||
)
|
||||
|
||||
func init() {
|
||||
//TCP
|
||||
addTCPMatcher(ssh.NewMatcher())
|
||||
addTCPMatcher(mongodb.NewMatcher())
|
||||
addTCPMatcher(mysql.NewMatcher())
|
||||
addTCPMatcher(redis.NewMatcher())
|
||||
addTCPMatcher(postgresql.NewMatcher())
|
||||
addTCPMatcher(ftp.NewMatcher())
|
||||
addTCPMatcher(http.NewMatcher())
|
||||
addTCPMatcher(lpd.NewMatcher())
|
||||
addTCPMatcher(telnet.NewMatcher())
|
||||
addTCPMatcher(nbss.NewMatcher())
|
||||
addTCPMatcher(ldap.NewMatcher())
|
||||
addTCPMatcher(mysql.NewMatcher())
|
||||
|
||||
// UDP
|
||||
// addUDPMatcher(dns.NewMatcher())
|
||||
|
||||
// HTTP-relative
|
||||
addHTTPSubMatcher(elasticsearch.NewMatcher())
|
||||
}
|
||||
|
||||
func addTCPMatcher(m osm.Matcher) {
|
||||
AllMatchers = append(AllMatchers, m)
|
||||
TCPMatchers = append(TCPMatchers, m)
|
||||
if m.IsPrePacket() {
|
||||
TCPPrePacketMatchers = append(TCPPrePacketMatchers, m)
|
||||
} else {
|
||||
TCPNotPrePacketMatchers = append(TCPNotPrePacketMatchers, m)
|
||||
}
|
||||
}
|
||||
|
||||
func addUDPMatcher(m osm.UDPMatcher) {
|
||||
AllMatchers = append(AllMatchers, m)
|
||||
UDPMatchers = append(UDPMatchers, m)
|
||||
}
|
||||
|
||||
func addHTTPSubMatcher(m osm.Matcher) {
|
||||
HTTPSubMatchers = append(HTTPSubMatchers, m)
|
||||
}
|
||||
|
||||
func GetTCPMatchers(isPrePacket bool) []osm.Matcher {
|
||||
if isPrePacket {
|
||||
return TCPPrePacketMatchers
|
||||
}
|
||||
|
||||
return TCPNotPrePacketMatchers
|
||||
}
|
||||
|
||||
func GetUDPMatchers() []osm.UDPMatcher {
|
||||
return UDPMatchers
|
||||
}
|
||||
|
||||
func GetHTTPSubMatchers() []osm.Matcher {
|
||||
return HTTPSubMatchers
|
||||
}
|
||||
|
||||
func GetMatcherByKey(key string) osm.Matcher {
|
||||
for _, m := range AllMatchers {
|
||||
if m.Key() == key {
|
||||
return m
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
106
internal/pcap/packet.go
Normal file
106
internal/pcap/packet.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package pcap
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
type PacketType int
|
||||
|
||||
const (
|
||||
PacketTypeUnknown PacketType = iota
|
||||
PacketTypeARP
|
||||
PacketTypeTCP
|
||||
PacketTypeUDP
|
||||
)
|
||||
|
||||
func getPacketType(packet gopacket.Packet) PacketType {
|
||||
if packet == nil {
|
||||
return PacketTypeUnknown
|
||||
}
|
||||
layer := packet.Layer(layers.LayerTypeARP)
|
||||
if layer != nil {
|
||||
return PacketTypeARP
|
||||
}
|
||||
|
||||
layer = packet.Layer(layers.LayerTypeTCP)
|
||||
if layer != nil {
|
||||
if _, ok := layer.(*layers.TCP); ok {
|
||||
return PacketTypeTCP
|
||||
}
|
||||
}
|
||||
|
||||
layer = packet.Layer(layers.LayerTypeUDP)
|
||||
if layer != nil {
|
||||
if _, ok := layer.(*layers.UDP); ok {
|
||||
return PacketTypeUDP
|
||||
}
|
||||
}
|
||||
return PacketTypeUnknown
|
||||
}
|
||||
|
||||
func handlePacket(ps *pCapScan, packet gopacket.Packet) {
|
||||
switch getPacketType(packet) {
|
||||
case PacketTypeARP:
|
||||
handlePacketARP(ps, packet)
|
||||
case PacketTypeTCP:
|
||||
handlePacketTCP(ps, packet)
|
||||
case PacketTypeUDP:
|
||||
handlePacketUDP(ps, packet)
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func handlePacketARP(ps *pCapScan, packet gopacket.Packet) {
|
||||
ps.arpListenerChanMtx.RLock()
|
||||
defer ps.arpListenerChanMtx.RUnlock()
|
||||
|
||||
arpLayer := packet.Layer(layers.LayerTypeARP)
|
||||
arp := arpLayer.(*layers.ARP)
|
||||
|
||||
for _, ch := range ps.arpListenerChans {
|
||||
ch <- arp
|
||||
}
|
||||
}
|
||||
|
||||
func handlePacketTCP(ps *pCapScan, packet gopacket.Packet) {
|
||||
ipLayer := packet.Layer(layers.LayerTypeIPv4)
|
||||
if nil == ipLayer {
|
||||
return
|
||||
}
|
||||
|
||||
ip := ipLayer.(*layers.IPv4).SrcIP.String()
|
||||
|
||||
ps.tcpListenerChanMtx.RLock()
|
||||
defer ps.tcpListenerChanMtx.RUnlock()
|
||||
|
||||
chs, ok := ps.tcpListenerChans[ip]
|
||||
if ok {
|
||||
|
||||
layer := packet.Layer(layers.LayerTypeTCP)
|
||||
tcp, _ := layer.(*layers.TCP)
|
||||
|
||||
for _, ch := range chs {
|
||||
ch <- tcp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func handlePacketUDP(ps *pCapScan, packet gopacket.Packet) {
|
||||
ipLayer := packet.Layer(layers.LayerTypeIPv4)
|
||||
if nil == ipLayer {
|
||||
return
|
||||
}
|
||||
|
||||
ip := ipLayer.(*layers.IPv4).SrcIP.String()
|
||||
|
||||
ps.udpListenerChanMtx.RLock()
|
||||
defer ps.udpListenerChanMtx.RUnlock()
|
||||
|
||||
chs, ok := ps.udpListenerChans[ip]
|
||||
if ok {
|
||||
for _, ch := range chs {
|
||||
ch <- packet
|
||||
}
|
||||
}
|
||||
}
|
||||
60
internal/pcap/pcap.go
Normal file
60
internal/pcap/pcap.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package pcap
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
omd "git.loafle.net/overflow/model/discovery"
|
||||
)
|
||||
|
||||
var mtx sync.Mutex
|
||||
var instances map[string]PCapScanner
|
||||
|
||||
func init() {
|
||||
instances = make(map[string]PCapScanner, 0)
|
||||
}
|
||||
|
||||
func RetainScanner(zone *omd.Zone) (PCapScanner, error) {
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
|
||||
var ps PCapScanner
|
||||
var ok bool
|
||||
if ps, ok = instances[zone.Network]; !ok {
|
||||
ps = newPCapScanner(zone)
|
||||
if err := ps.start(); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
instances[zone.Network] = ps
|
||||
}
|
||||
ps.retain()
|
||||
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func ReleaseScanner(zone *omd.Zone) {
|
||||
|
||||
go func() {
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
|
||||
if ps, ok := instances[zone.Network]; ok {
|
||||
if ps.release() {
|
||||
ps.stop()
|
||||
delete(instances, zone.Network)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func ReleaseScannerAll() {
|
||||
mtx.Lock()
|
||||
defer mtx.Unlock()
|
||||
|
||||
for k, ps := range instances {
|
||||
ps.stop()
|
||||
delete(instances, k)
|
||||
}
|
||||
}
|
||||
251
internal/pcap/pcap_scan.go
Normal file
251
internal/pcap/pcap_scan.go
Normal file
@@ -0,0 +1,251 @@
|
||||
package pcap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
omd "git.loafle.net/overflow/model/discovery"
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/google/gopacket/pcap"
|
||||
)
|
||||
|
||||
func newPCapScanner(zone *omd.Zone) PCapScanner {
|
||||
ps := &pCapScan{
|
||||
zone: zone,
|
||||
}
|
||||
|
||||
return ps
|
||||
}
|
||||
|
||||
type PCapScanner interface {
|
||||
OpenARP() chan *layers.ARP
|
||||
CloseARP(ch chan *layers.ARP)
|
||||
|
||||
OpenTCP(ip string) chan *layers.TCP
|
||||
CloseTCP(ip string, ch chan *layers.TCP)
|
||||
|
||||
OpenUDP(ip string) chan gopacket.Packet
|
||||
CloseUDP(ip string, ch chan gopacket.Packet)
|
||||
|
||||
WritePacketData(data []byte) (err error)
|
||||
|
||||
start() error
|
||||
stop()
|
||||
|
||||
retain()
|
||||
release() bool
|
||||
}
|
||||
|
||||
type pCapScan struct {
|
||||
pCapHandle *pcap.Handle
|
||||
zone *omd.Zone
|
||||
|
||||
arpListenerChanMtx sync.RWMutex
|
||||
arpListenerChans []chan *layers.ARP
|
||||
|
||||
tcpListenerChanMtx sync.RWMutex
|
||||
tcpListenerChans map[string][]chan *layers.TCP
|
||||
|
||||
udpListenerChanMtx sync.RWMutex
|
||||
udpListenerChans map[string][]chan gopacket.Packet
|
||||
|
||||
refCount int
|
||||
stopChan chan struct{}
|
||||
stopWg sync.WaitGroup
|
||||
}
|
||||
|
||||
func (ps *pCapScan) start() error {
|
||||
if ps.stopChan != nil {
|
||||
return fmt.Errorf("PCapScanner: already running. Stop it before starting it again")
|
||||
}
|
||||
|
||||
// new pcap handle
|
||||
var h *pcap.Handle
|
||||
var err error
|
||||
if h, err = pcap.OpenLive(ps.zone.Iface, 65536, true, pcap.BlockForever); nil != err {
|
||||
return err
|
||||
}
|
||||
// set filter
|
||||
// todo add tcp, udp filter
|
||||
//if err = h.SetBPFFilter("arp and src net " + ps.zone.Network + " or (((tcp[tcpflags] & (tcp-syn|tcp-ack) != 0) or (tcp[tcpflags] & (tcp-rst) != 0)) and port 60000) or udp "); nil != err {
|
||||
if err = h.SetBPFFilter("arp or tcp or udp"); nil != err {
|
||||
h.Close()
|
||||
return err
|
||||
}
|
||||
ps.pCapHandle = h
|
||||
|
||||
ps.arpListenerChans = make([]chan *layers.ARP, 0)
|
||||
ps.tcpListenerChans = make(map[string][]chan *layers.TCP, 0)
|
||||
ps.udpListenerChans = make(map[string][]chan gopacket.Packet, 0)
|
||||
|
||||
ps.stopChan = make(chan struct{})
|
||||
|
||||
ps.stopWg.Add(1)
|
||||
go handleReceive(ps)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *pCapScan) stop() {
|
||||
if ps.stopChan == nil {
|
||||
panic("PCapScanner: must be started before stopping it")
|
||||
}
|
||||
close(ps.stopChan)
|
||||
ps.stopWg.Wait()
|
||||
ps.stopChan = nil
|
||||
}
|
||||
|
||||
func (ps *pCapScan) OpenARP() chan *layers.ARP {
|
||||
ps.arpListenerChanMtx.Lock()
|
||||
defer ps.arpListenerChanMtx.Unlock()
|
||||
|
||||
c := make(chan *layers.ARP, 0)
|
||||
ps.arpListenerChans = append(ps.arpListenerChans, c)
|
||||
return c
|
||||
}
|
||||
func (ps *pCapScan) CloseARP(ch chan *layers.ARP) {
|
||||
ps.arpListenerChanMtx.Lock()
|
||||
defer ps.arpListenerChanMtx.Unlock()
|
||||
|
||||
i := sort.Search(len(ps.arpListenerChans), func(i int) bool {
|
||||
return ch == ps.arpListenerChans[i]
|
||||
})
|
||||
|
||||
if -1 != i {
|
||||
close(ch)
|
||||
ps.arpListenerChans = append(ps.arpListenerChans[:i], ps.arpListenerChans[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
func (ps *pCapScan) OpenTCP(ip string) chan *layers.TCP {
|
||||
ps.tcpListenerChanMtx.Lock()
|
||||
defer ps.tcpListenerChanMtx.Unlock()
|
||||
|
||||
if _, ok := ps.tcpListenerChans[ip]; !ok {
|
||||
ps.tcpListenerChans[ip] = make([]chan *layers.TCP, 0)
|
||||
}
|
||||
|
||||
ch := make(chan *layers.TCP, 0)
|
||||
ps.tcpListenerChans[ip] = append(ps.tcpListenerChans[ip], ch)
|
||||
|
||||
return ch
|
||||
}
|
||||
func (ps *pCapScan) CloseTCP(ip string, ch chan *layers.TCP) {
|
||||
ps.tcpListenerChanMtx.Lock()
|
||||
defer ps.tcpListenerChanMtx.Unlock()
|
||||
|
||||
if _, ok := ps.tcpListenerChans[ip]; !ok {
|
||||
return
|
||||
}
|
||||
|
||||
chs := ps.tcpListenerChans[ip]
|
||||
i := sort.Search(len(chs), func(i int) bool {
|
||||
return ch == chs[i]
|
||||
})
|
||||
|
||||
if -1 != i {
|
||||
close(ch)
|
||||
ps.tcpListenerChans[ip] = append(ps.tcpListenerChans[ip][:i], ps.tcpListenerChans[ip][i+1:]...)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (ps *pCapScan) OpenUDP(ip string) chan gopacket.Packet {
|
||||
ps.udpListenerChanMtx.Lock()
|
||||
defer ps.udpListenerChanMtx.Unlock()
|
||||
|
||||
if _, ok := ps.udpListenerChans[ip]; !ok {
|
||||
ps.udpListenerChans[ip] = make([]chan gopacket.Packet, 0)
|
||||
}
|
||||
|
||||
ch := make(chan gopacket.Packet, 0)
|
||||
ps.udpListenerChans[ip] = append(ps.udpListenerChans[ip], ch)
|
||||
|
||||
return ch
|
||||
|
||||
}
|
||||
func (ps *pCapScan) CloseUDP(ip string, ch chan gopacket.Packet) {
|
||||
ps.udpListenerChanMtx.Lock()
|
||||
defer ps.udpListenerChanMtx.Unlock()
|
||||
|
||||
if _, ok := ps.udpListenerChans[ip]; !ok {
|
||||
return
|
||||
}
|
||||
|
||||
chs := ps.udpListenerChans[ip]
|
||||
i := sort.Search(len(chs), func(i int) bool {
|
||||
return ch == chs[i]
|
||||
})
|
||||
|
||||
if -1 != i {
|
||||
close(ch)
|
||||
ps.udpListenerChans[ip] = append(ps.udpListenerChans[ip][:i], ps.udpListenerChans[ip][i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
func (ps *pCapScan) WritePacketData(data []byte) (err error) {
|
||||
return ps.pCapHandle.WritePacketData(data)
|
||||
}
|
||||
|
||||
func (ps *pCapScan) retain() {
|
||||
ps.refCount++
|
||||
}
|
||||
func (ps *pCapScan) release() bool {
|
||||
ps.refCount--
|
||||
if 0 > ps.refCount {
|
||||
ps.refCount = 0
|
||||
}
|
||||
|
||||
return 0 == ps.refCount
|
||||
}
|
||||
|
||||
func (ps *pCapScan) destroy() {
|
||||
ps.tcpListenerChanMtx.Lock()
|
||||
for k, v := range ps.tcpListenerChans {
|
||||
for _, ch := range v {
|
||||
close(ch)
|
||||
}
|
||||
v = v[:0]
|
||||
delete(ps.tcpListenerChans, k)
|
||||
}
|
||||
ps.tcpListenerChanMtx.Unlock()
|
||||
|
||||
ps.udpListenerChanMtx.Lock()
|
||||
for k, v := range ps.udpListenerChans {
|
||||
for _, ch := range v {
|
||||
close(ch)
|
||||
}
|
||||
v = v[:0]
|
||||
delete(ps.udpListenerChans, k)
|
||||
}
|
||||
ps.udpListenerChanMtx.Unlock()
|
||||
|
||||
ps.arpListenerChanMtx.Lock()
|
||||
for _, v := range ps.arpListenerChans {
|
||||
close(v)
|
||||
}
|
||||
ps.arpListenerChans = ps.arpListenerChans[:0]
|
||||
ps.arpListenerChanMtx.Unlock()
|
||||
|
||||
ps.pCapHandle.Close()
|
||||
}
|
||||
|
||||
func handleReceive(ps *pCapScan) {
|
||||
defer ps.stopWg.Done()
|
||||
|
||||
pSrc := gopacket.NewPacketSource(ps.pCapHandle, layers.LayerTypeEthernet)
|
||||
inPacket := pSrc.Packets()
|
||||
|
||||
for {
|
||||
select {
|
||||
case packet := <-inPacket:
|
||||
handlePacket(ps, packet)
|
||||
case <-ps.stopChan:
|
||||
ps.destroy()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
27
internal/pcap/pcap_test.go
Normal file
27
internal/pcap/pcap_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package pcap
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/google/gopacket/pcap"
|
||||
)
|
||||
|
||||
func TestFindIfaces(t *testing.T) {
|
||||
devices, err := pcap.FindAllDevs()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log("Devices found:")
|
||||
for _, d := range devices {
|
||||
t.Log("\nName: ", d.Name)
|
||||
t.Log("Description: ", d.Description)
|
||||
|
||||
for _, address := range d.Addresses {
|
||||
t.Log("- IP address: ", address.IP)
|
||||
t.Log("- Subnet mask: ", address.Netmask)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
177
internal/protocol/dhcp/dhcp.go
Normal file
177
internal/protocol/dhcp/dhcp.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package dhcp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
MAGIC_COOKIE uint32 = 0x63825363
|
||||
OPT_CODE_SERVER_IDENTIFIER uint8 = 54
|
||||
OPT_CODE_SUBNET_MASK uint8 = 1
|
||||
OPT_CODE_ROUTER uint8 = 3
|
||||
OPT_CODE_DNS uint8 = 6
|
||||
)
|
||||
|
||||
type dhcpDiscover struct {
|
||||
MsgType byte
|
||||
HwType byte
|
||||
HwAddrLen byte
|
||||
Hops byte
|
||||
Xid uint32
|
||||
Secs uint16
|
||||
BootpFlags uint16
|
||||
ClientIp uint32
|
||||
YourIp uint32
|
||||
NextServerIp uint32
|
||||
RelayAgentIp uint32
|
||||
ClientMacAddr [6]byte
|
||||
ClientHwAddrPadding [10]byte
|
||||
ServerHostName [64]byte
|
||||
BootFileName [128]byte
|
||||
MagicCookie uint32
|
||||
Mtype byte
|
||||
MtypeLen byte
|
||||
MtypeVal byte
|
||||
Opts [200]byte
|
||||
End byte
|
||||
Padding [16]byte
|
||||
}
|
||||
|
||||
func doDiscover(address string) (map[string]string, error) {
|
||||
|
||||
localAddr, err := net.ResolveUDPAddr("udp", address+":68")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := net.ListenUDP("udp", localAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
sendDiscover(conn) // broadcast 255.255.255.255:67
|
||||
|
||||
return readOffer(conn) // unicast
|
||||
|
||||
}
|
||||
|
||||
func sendDiscover(conn *net.UDPConn) error {
|
||||
|
||||
server := fmt.Sprintf("%s:%d", "255.255.255.255", 67)
|
||||
serverAddr, err := net.ResolveUDPAddr("udp", server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dhcp := dhcpDiscover{
|
||||
MsgType: 0x01,
|
||||
HwType: 0x01,
|
||||
HwAddrLen: 0x06,
|
||||
Hops: 0x00,
|
||||
Xid: 0x00000000,
|
||||
Secs: 0x0000,
|
||||
ClientIp: 0x00000000,
|
||||
YourIp: 0x00000000,
|
||||
NextServerIp: 0x00000000,
|
||||
RelayAgentIp: 0x00000000,
|
||||
MagicCookie: MAGIC_COOKIE,
|
||||
Mtype: 0x35,
|
||||
MtypeLen: 0x01,
|
||||
MtypeVal: 0x01,
|
||||
End: 0xff,
|
||||
}
|
||||
|
||||
var flag uint16 = 0
|
||||
dhcp.BootpFlags = ^flag // flag = unicast , ^flag = broadcast
|
||||
|
||||
writer := new(bytes.Buffer)
|
||||
if err := binary.Write(writer, binary.BigEndian, dhcp); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = conn.WriteToUDP(writer.Bytes(), serverAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readOffer(conn *net.UDPConn) (map[string]string, error) {
|
||||
offerOptions := make(map[string]string)
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
_, _, err := conn.ReadFromUDP(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
offer := dhcpDiscover{}
|
||||
|
||||
reader := new(bytes.Buffer)
|
||||
reader.Write(buf)
|
||||
if err := binary.Read(reader, binary.BigEndian, &offer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if offer.MagicCookie != MAGIC_COOKIE {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//option searching
|
||||
r := new(bytes.Buffer)
|
||||
r.Write(offer.Opts[:])
|
||||
|
||||
for i := 0; i < r.Len(); i++ {
|
||||
v := r.Next(1)[0]
|
||||
|
||||
if v == OPT_CODE_SUBNET_MASK && r.Next(1)[0] == 4 {
|
||||
ipStr := byteToIPString(r.Next(4))
|
||||
offerOptions["SUBNETMASK"] = ipStr
|
||||
}
|
||||
|
||||
if v == OPT_CODE_ROUTER && r.Next(1)[0] == 4 {
|
||||
ipStr := byteToIPString(r.Next(4))
|
||||
offerOptions["ROUTER"] = ipStr
|
||||
}
|
||||
|
||||
if v == OPT_CODE_DNS {
|
||||
len := r.Next(1)[0]
|
||||
var dns []string
|
||||
var ipStr string
|
||||
ipStr = byteToIPString(r.Next(4))
|
||||
dns = append(dns, ipStr)
|
||||
if len == 8 {
|
||||
ipStr = byteToIPString(r.Next(4))
|
||||
dns = append(dns, ipStr)
|
||||
}
|
||||
offerOptions["DNS"] = strings.Join(dns, ",")
|
||||
}
|
||||
|
||||
if v == OPT_CODE_SERVER_IDENTIFIER && r.Next(1)[0] == 4 {
|
||||
ipStr := byteToIPString(r.Next(4))
|
||||
offerOptions["DHCP_SERVER"] = ipStr
|
||||
}
|
||||
}
|
||||
|
||||
return offerOptions, nil
|
||||
}
|
||||
|
||||
func byteToIPString(b []byte) string {
|
||||
var ipStr string
|
||||
len := len(b)
|
||||
for i := 0; i < len; i++ {
|
||||
v := b[i]
|
||||
ipStr += fmt.Sprintf("%d", v)
|
||||
if i < len-1 {
|
||||
ipStr += "."
|
||||
}
|
||||
}
|
||||
|
||||
return ipStr
|
||||
}
|
||||
13
internal/protocol/dhcp/dhcp_test.go
Normal file
13
internal/protocol/dhcp/dhcp_test.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package dhcp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDHCP(t *testing.T) {
|
||||
offer, err := doDiscover("192.168.1.201")
|
||||
if err != nil {
|
||||
t.Errorf("ERROR : %s", err.Error())
|
||||
}
|
||||
t.Log(offer)
|
||||
}
|
||||
215
internal/protocol/mdns/mdns.go
Normal file
215
internal/protocol/mdns/mdns.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package mdns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
omd "git.loafle.net/overflow/model/discovery"
|
||||
omm "git.loafle.net/overflow/model/meta"
|
||||
omu "git.loafle.net/overflow/model/util"
|
||||
"git.loafle.net/overflow_scanner/probe/model"
|
||||
"github.com/grandcat/zeroconf"
|
||||
)
|
||||
|
||||
func Scan(discovered model.Discovered) {
|
||||
serviceEntries, err := browse("_services._dns-sd._udp", "local")
|
||||
if nil != err {
|
||||
log.Print("Cannot find service ", err)
|
||||
}
|
||||
|
||||
metaIPTypeEnum := omm.ToMetaIPTypeEnum(discovered.Zone().MetaIPType)
|
||||
|
||||
for _, serviceEntry := range serviceEntries {
|
||||
name := removeDomainName(serviceEntry.Instance, serviceEntry.Domain)
|
||||
entries, _err := browse(name, serviceEntry.Domain)
|
||||
if nil != _err {
|
||||
log.Print("Cannot find entry ", _err)
|
||||
return
|
||||
}
|
||||
|
||||
LOOP:
|
||||
for _, entry := range entries {
|
||||
log.Print("serviceEntry ", entry)
|
||||
|
||||
name := entry.Instance // HP\ LaserJet\ P1505n
|
||||
service := entry.Service // _pdl-datastream._tcp
|
||||
port := entry.Port // 9100
|
||||
meta := toMeta(entry.Text)
|
||||
hostName := removeDomainName(entry.HostName, entry.Domain) // NPIFACA9B
|
||||
serviceName, portType, cryptoType := parseService(service)
|
||||
|
||||
var metaPortType *omm.MetaPortType
|
||||
switch portType {
|
||||
case "tcp":
|
||||
metaPortType = omm.ToMetaPortType(omm.MetaPortTypeEnumTCP)
|
||||
case "udp":
|
||||
metaPortType = omm.ToMetaPortType(omm.MetaPortTypeEnumUDP)
|
||||
}
|
||||
|
||||
metaCryptoType := omm.ToMetaCryptoType(omm.MetaCryptoTypeEnumUNKNOWN)
|
||||
switch cryptoType {
|
||||
case "NONE":
|
||||
metaCryptoType = omm.ToMetaCryptoType(omm.MetaCryptoTypeEnumNONE)
|
||||
case "TLS":
|
||||
metaCryptoType = omm.ToMetaCryptoType(omm.MetaCryptoTypeEnumTLS)
|
||||
}
|
||||
|
||||
switch metaIPTypeEnum {
|
||||
case omm.MetaIPTypeEnumV4:
|
||||
for _, ipv4 := range entry.AddrIPv4 {
|
||||
h := discovered.AddHost(&omd.Host{
|
||||
MetaIPType: omm.ToMetaIPType(metaIPTypeEnum),
|
||||
Name: hostName,
|
||||
Address: ipv4.String(),
|
||||
Meta: meta,
|
||||
Zone: discovered.Zone(),
|
||||
DiscoveredDate: omu.NowPtr(),
|
||||
})
|
||||
|
||||
if 1 > port {
|
||||
continue LOOP
|
||||
}
|
||||
|
||||
p := discovered.AddPort(&omd.Port{
|
||||
MetaPortType: metaPortType,
|
||||
PortNumber: json.Number(strconv.Itoa(port)),
|
||||
Meta: meta,
|
||||
Host: h,
|
||||
})
|
||||
|
||||
discovered.AddService(&omd.Service{
|
||||
MetaCryptoType: metaCryptoType,
|
||||
Key: serviceName,
|
||||
Name: name,
|
||||
Port: p,
|
||||
})
|
||||
}
|
||||
|
||||
case omm.MetaIPTypeEnumV6:
|
||||
for _, ipv6 := range entry.AddrIPv6 {
|
||||
h := discovered.AddHost(&omd.Host{
|
||||
MetaIPType: omm.ToMetaIPType(metaIPTypeEnum),
|
||||
Name: hostName,
|
||||
Address: ipv6.String(),
|
||||
Meta: meta,
|
||||
Zone: discovered.Zone(),
|
||||
DiscoveredDate: omu.NowPtr(),
|
||||
})
|
||||
|
||||
if 1 > port {
|
||||
continue LOOP
|
||||
}
|
||||
|
||||
p := discovered.AddPort(&omd.Port{
|
||||
MetaPortType: metaPortType,
|
||||
PortNumber: json.Number(strconv.Itoa(port)),
|
||||
Meta: meta,
|
||||
Host: h,
|
||||
})
|
||||
|
||||
discovered.AddService(&omd.Service{
|
||||
MetaCryptoType: metaCryptoType,
|
||||
Key: serviceName,
|
||||
Name: name,
|
||||
Port: p,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeDomainName(instance string, domain string) (service string) {
|
||||
_instance := strings.TrimRight(instance, ".")
|
||||
return strings.TrimRight(_instance, fmt.Sprintf(".%s", domain))
|
||||
}
|
||||
|
||||
func toMeta(text []string) map[string]string {
|
||||
if nil == text || 0 == len(text) {
|
||||
return nil
|
||||
}
|
||||
|
||||
meta := make(map[string]string)
|
||||
for _, v := range text {
|
||||
ss := strings.SplitN(v, "=", 2)
|
||||
if 2 != len(ss) {
|
||||
continue
|
||||
}
|
||||
if nil != ss {
|
||||
meta[ss[0]] = ss[1]
|
||||
}
|
||||
}
|
||||
return meta
|
||||
}
|
||||
|
||||
func parseService(service string) (name string, portType string, cryptoType string) {
|
||||
ss := strings.SplitN(service, ".", 2)
|
||||
if nil == ss {
|
||||
return "UNKNOWN", "UNKNOWN", "UNKNOWN"
|
||||
}
|
||||
|
||||
_name := strings.TrimLeft(ss[0], "_")
|
||||
_portType := strings.TrimLeft(ss[1], "_")
|
||||
|
||||
name = _name
|
||||
portType = _portType
|
||||
cryptoType = "NONE"
|
||||
return
|
||||
}
|
||||
|
||||
func browse(service string, domain string) ([]*zeroconf.ServiceEntry, error) {
|
||||
resolver, err := zeroconf.NewResolver(nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to initialize resolver %v", err)
|
||||
}
|
||||
|
||||
entryChan := make(chan *zeroconf.ServiceEntry)
|
||||
timerStopped := make(chan struct{})
|
||||
|
||||
serviceEntries := make([]*zeroconf.ServiceEntry, 0)
|
||||
|
||||
go func(_entryChan <-chan *zeroconf.ServiceEntry) {
|
||||
var delay atomic.Value
|
||||
delay.Store(false)
|
||||
ticker := time.NewTicker(time.Second * 1)
|
||||
|
||||
for {
|
||||
select {
|
||||
case entry, ok := <-_entryChan:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
delay.Store(true)
|
||||
serviceEntries = append(serviceEntries, entry)
|
||||
case <-ticker.C:
|
||||
if false == delay.Load().(bool) {
|
||||
ticker.Stop()
|
||||
timerStopped <- struct{}{}
|
||||
return
|
||||
}
|
||||
delay.Store(false)
|
||||
}
|
||||
}
|
||||
}(entryChan)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(30))
|
||||
defer cancel()
|
||||
err = resolver.Browse(ctx, service, domain, entryChan)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to browse %v", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-timerStopped:
|
||||
}
|
||||
|
||||
return serviceEntries, nil
|
||||
}
|
||||
139
internal/protocol/mdns/mdns_test.go
Normal file
139
internal/protocol/mdns/mdns_test.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package mdns
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
omd "git.loafle.net/overflow/model/discovery"
|
||||
omm "git.loafle.net/overflow/model/meta"
|
||||
"git.loafle.net/overflow_scanner/probe/model"
|
||||
"github.com/grandcat/zeroconf"
|
||||
)
|
||||
|
||||
func TestScan(t *testing.T) {
|
||||
type args struct {
|
||||
discovered model.Discovered
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
{
|
||||
name: "1",
|
||||
args: args{
|
||||
discovered: model.New(
|
||||
&omd.Zone{
|
||||
Network: "192.168.1.0/24",
|
||||
Iface: "enp3s0",
|
||||
MetaIPType: omm.ToMetaIPType(omm.MetaIPTypeEnumV4),
|
||||
Address: "192.168.1.101",
|
||||
Mac: "44:8a:5b:f1:f1:f3",
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
Scan(tt.args.discovered)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_removeDomainName(t *testing.T) {
|
||||
type args struct {
|
||||
instance string
|
||||
domain string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantService string
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotService := removeDomainName(tt.args.instance, tt.args.domain); gotService != tt.wantService {
|
||||
t.Errorf("removeDomainName() = %v, want %v", gotService, tt.wantService)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toMeta(t *testing.T) {
|
||||
type args struct {
|
||||
text []string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want map[string]string
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := toMeta(tt.args.text); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("toMeta() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_parseService(t *testing.T) {
|
||||
type args struct {
|
||||
service string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantName string
|
||||
wantPortType string
|
||||
wantCryptoType string
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotName, gotPortType, gotCryptoType := parseService(tt.args.service)
|
||||
if gotName != tt.wantName {
|
||||
t.Errorf("parseService() gotName = %v, want %v", gotName, tt.wantName)
|
||||
}
|
||||
if gotPortType != tt.wantPortType {
|
||||
t.Errorf("parseService() gotPortType = %v, want %v", gotPortType, tt.wantPortType)
|
||||
}
|
||||
if gotCryptoType != tt.wantCryptoType {
|
||||
t.Errorf("parseService() gotCryptoType = %v, want %v", gotCryptoType, tt.wantCryptoType)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_browse(t *testing.T) {
|
||||
type args struct {
|
||||
service string
|
||||
domain string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []*zeroconf.ServiceEntry
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := browse(tt.args.service, tt.args.domain)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("browse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("browse() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
69
internal/protocol/snmp/snmp.go
Normal file
69
internal/protocol/snmp/snmp.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package snmp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
omd "git.loafle.net/overflow/model/discovery"
|
||||
"github.com/k-sone/snmpgo"
|
||||
)
|
||||
|
||||
var defaultPort = 161
|
||||
var defaultOIDs = []string{
|
||||
"1.3.6.1.2.1.1.5.0", //sysName
|
||||
}
|
||||
|
||||
type SNMPResponse struct {
|
||||
Host *omd.Host
|
||||
Value map[string]string
|
||||
Error error
|
||||
}
|
||||
|
||||
func ScanSNMP(host *omd.Host, community string, ch chan *SNMPResponse) {
|
||||
go func() {
|
||||
getV2(host, defaultPort, community, defaultOIDs, ch)
|
||||
}()
|
||||
}
|
||||
|
||||
func getV2(host *omd.Host, port int, community string, _oids []string, ch chan *SNMPResponse) {
|
||||
address := fmt.Sprintf("%s:%d", host.Address, port)
|
||||
snmp, err := snmpgo.NewSNMP(snmpgo.SNMPArguments{
|
||||
Version: snmpgo.V2c,
|
||||
Address: address,
|
||||
Retries: 1,
|
||||
Community: community,
|
||||
})
|
||||
if err != nil {
|
||||
ch <- &SNMPResponse{host, nil, err}
|
||||
return
|
||||
}
|
||||
defer snmp.Close()
|
||||
|
||||
oids, err := snmpgo.NewOids(_oids)
|
||||
if err != nil {
|
||||
ch <- &SNMPResponse{host, nil, err}
|
||||
return
|
||||
}
|
||||
|
||||
pdu, err := snmp.GetRequest(oids)
|
||||
if err != nil {
|
||||
ch <- &SNMPResponse{host, nil, err}
|
||||
return
|
||||
}
|
||||
|
||||
if pdu.ErrorStatus() != snmpgo.NoError {
|
||||
ch <- &SNMPResponse{host, nil, fmt.Errorf("%s", pdu.ErrorStatus().String())}
|
||||
return
|
||||
}
|
||||
|
||||
if pdu == nil {
|
||||
ch <- &SNMPResponse{host, nil, fmt.Errorf("%s", "Empty PDU")}
|
||||
return
|
||||
}
|
||||
|
||||
resMap := make(map[string]string)
|
||||
for _, val := range pdu.VarBinds() {
|
||||
resMap[val.Oid.String()] = val.Variable.String()
|
||||
}
|
||||
ch <- &SNMPResponse{host, resMap, nil}
|
||||
return
|
||||
}
|
||||
23
internal/protocol/snmp/snmp_test.go
Normal file
23
internal/protocol/snmp/snmp_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package snmp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
omd "git.loafle.net/overflow/model/discovery"
|
||||
omm "git.loafle.net/overflow/model/meta"
|
||||
)
|
||||
|
||||
func TestSNMPScan(t *testing.T) {
|
||||
host := &omd.Host{
|
||||
MetaIPType: omm.ToMetaIPType(omm.MetaIPTypeEnumV4),
|
||||
Address: "192.168.1.229",
|
||||
}
|
||||
|
||||
ch := make(chan *SNMPResponse)
|
||||
defer close(ch)
|
||||
ScanSNMP(host, "test1252serc", ch)
|
||||
|
||||
msg := <-ch
|
||||
t.Log(msg)
|
||||
|
||||
}
|
||||
49
internal/protocol/upnp/upnp.go
Normal file
49
internal/protocol/upnp/upnp.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package upnp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
omd "git.loafle.net/overflow/model/discovery"
|
||||
omu "git.loafle.net/overflow/model/util"
|
||||
"git.loafle.net/overflow_scanner/probe/model"
|
||||
"github.com/huin/goupnp"
|
||||
)
|
||||
|
||||
const (
|
||||
TargetRootDevice = "upnp:rootdevice"
|
||||
TargetSSDPAll = "ssdp:all"
|
||||
)
|
||||
|
||||
func Scan(discovered model.Discovered) {
|
||||
devs, err := goupnp.DiscoverDevices(TargetRootDevice)
|
||||
if nil != err {
|
||||
fmt.Println("DeletePortMapping: ", err)
|
||||
}
|
||||
|
||||
LOOP:
|
||||
for _, dev := range devs {
|
||||
rd := dev.Root.Device
|
||||
if !rd.PresentationURL.Ok {
|
||||
continue LOOP
|
||||
}
|
||||
|
||||
discovered.AddHost(&omd.Host{
|
||||
MetaIPType: discovered.Zone().MetaIPType,
|
||||
Name: rd.FriendlyName,
|
||||
Address: rd.PresentationURL.URL.Host,
|
||||
Meta: map[string]string{
|
||||
"DeviceType": rd.DeviceType,
|
||||
"Manufacturer": rd.Manufacturer,
|
||||
"ManufacturerURL": rd.ManufacturerURL.Str,
|
||||
"ModelName": rd.ModelName,
|
||||
"ModelDescription": rd.ModelDescription,
|
||||
"ModelNumber": rd.ModelNumber,
|
||||
"SerialNumber": rd.SerialNumber,
|
||||
"UDN": rd.UDN,
|
||||
"UPC": rd.UPC,
|
||||
},
|
||||
Zone: discovered.Zone(),
|
||||
DiscoveredDate: omu.NowPtr(),
|
||||
})
|
||||
}
|
||||
}
|
||||
40
internal/protocol/upnp/upnp_test.go
Normal file
40
internal/protocol/upnp/upnp_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package upnp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
omd "git.loafle.net/overflow/model/discovery"
|
||||
omm "git.loafle.net/overflow/model/meta"
|
||||
"git.loafle.net/overflow_scanner/probe/model"
|
||||
)
|
||||
|
||||
func TestScan(t *testing.T) {
|
||||
type args struct {
|
||||
discovered model.Discovered
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
{
|
||||
name: "1",
|
||||
args: args{
|
||||
discovered: model.New(
|
||||
&omd.Zone{
|
||||
Network: "192.168.1.0/24",
|
||||
Iface: "enp3s0",
|
||||
MetaIPType: omm.ToMetaIPType(omm.MetaIPTypeEnumV4),
|
||||
Address: "192.168.1.101",
|
||||
Mac: "44:8a:5b:f1:f1:f3",
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
Scan(tt.args.discovered)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user