musique/server/scan/scanner.go

99 lines
2.1 KiB
Go
Raw Normal View History

package scan
import (
"fmt"
2022-12-16 16:34:52 +01:00
"log"
"musique/server/proto"
"net"
"sync"
"time"
)
type Network struct {
FirstAddress string
MaxHostsCount int
}
const timeoutTCPHosts = time.Duration(1) * time.Second
func nextIP(ip net.IP) (next net.IP) {
// FIXME Proper next IP address in network calculation
2022-12-17 17:50:34 +01:00
ip = ip.To4()
o := (uint(ip[0]) << 24) + (uint(ip[1]) << 16) + (uint(ip[2]) << 8) + uint(ip[3])
o++
o3 := byte(o & 0xFF)
o2 := byte((o >> 8) & 0xFF)
o1 := byte((o >> 16) & 0xFF)
o0 := byte((o >> 24) & 0xFF)
return net.IPv4(o0, o1, o2, o3)
}
// AvailableNetworks returns all IPv4 networks that are available to the host
func AvailableNetworks() ([]Network, error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return nil, fmt.Errorf("getting interfaces info: %v", err)
}
networks := []Network{}
for _, addr := range addrs {
_, ipNet, err := net.ParseCIDR(addr.String())
if err != nil {
continue
}
if ipNet.IP.IsGlobalUnicast() {
if ip := ipNet.IP.To4(); ip != nil {
// FIXME We assume mask /24. This is a reasonable assumption performance wise
// but may lead to inability to recognize some of the host in network
networks = append(networks, Network{nextIP(ipNet.IP).String(), 253})
}
}
}
return networks, nil
}
type Response struct {
proto.HandshakeResponse
Address string
}
// TCPHosts returns all TCP hosts that are in given networks on one of given ports
func TCPHosts(networks []Network, ports []uint16) <-chan Response {
2022-12-16 16:34:52 +01:00
ips := make(chan Response, 256)
2022-12-16 16:34:52 +01:00
log.Printf("tcphosts: %+v\n", networks)
2022-12-16 16:34:52 +01:00
wg := sync.WaitGroup{}
for _, network := range networks {
ip := net.ParseIP(network.FirstAddress)
for i := 0; i < network.MaxHostsCount; i++ {
for _, port := range ports {
wg.Add(1)
go func(ip net.IP, port uint16) {
defer wg.Done()
target := fmt.Sprintf("%s:%d", ip, port)
var hs proto.HandshakeResponse
err := proto.CommandTimeout(target, proto.Handshake(), &hs, timeoutTCPHosts)
if err == nil {
ips <- Response{hs, target}
}
}(ip, port)
}
ip = nextIP(ip)
}
}
go func() {
wg.Wait()
close(ips)
}()
return ips
}