Selaa lähdekoodia

cleaning up, adding docs, and trying to work on implementing ICMP dialing

AETH-erial 1 vuosi sitten
vanhempi
sitoutus
7c1f27d410
5 muutettua tiedostoa jossa 197 lisäystä ja 50 poistoa
  1. 22 1
      cmd/kyoketsu/kyoketsu.go
  2. 5 1
      go.mod
  3. 13 0
      go.sum
  4. 147 42
      pkg/scanner.go
  5. 10 6
      pkg/storage.go

+ 22 - 1
cmd/kyoketsu/kyoketsu.go

@@ -4,9 +4,15 @@ import (
 	"fmt"
 	"log"
 	"os"
+	"sync"
+
+	kyoketsu "git.aetherial.dev/aeth/kyoketsu/pkg"
 )
 
 func main() {
+	//	kyoketsu.PingWithDependency(os.Args[1])
+	//	os.Exit(1)
+
 	if len(os.Args) == 1 {
 		log.Fatal("Please pass in the name of an interface that belongs to the network to scan.")
 	}
@@ -14,6 +20,21 @@ func main() {
 	if err != nil {
 		log.Fatal(err)
 	}
-	fmt.Printf("%+v\n", addr)
+	var wg sync.WaitGroup
+	for i := range addr.Addr {
+		wg.Add(1)
+		go func(idx int, wg *sync.WaitGroup) {
+
+			out := kyoketsu.PortWalk(addr.Addr[idx].String(), kyoketsu.PORT_MAP)
+			if len(out.ListeningPorts) > 0 {
+				fmt.Printf("%+v\n", out)
+
+			}
+
+			wg.Done()
+		}(i, &wg)
+	}
+
+	wg.Wait()
 
 }

+ 5 - 1
go.mod

@@ -3,7 +3,9 @@ module git.aetherial.dev/aeth/kyoketsu
 go 1.21.1
 
 require (
+	github.com/go-ping/ping v1.1.0 // indirect
 	github.com/golang/snappy v0.0.1 // indirect
+	github.com/google/uuid v1.2.0 // indirect
 	github.com/klauspost/compress v1.13.6 // indirect
 	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
@@ -11,7 +13,9 @@ require (
 	github.com/xdg-go/stringprep v1.0.4 // indirect
 	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
 	go.mongodb.org/mongo-driver v1.14.0 // indirect
-	golang.org/x/crypto v0.17.0 // indirect
+	golang.org/x/crypto v0.22.0 // indirect
+	golang.org/x/net v0.24.0 // indirect
 	golang.org/x/sync v0.1.0 // indirect
+	golang.org/x/sys v0.19.0 // indirect
 	golang.org/x/text v0.14.0 // indirect
 )

+ 13 - 0
go.sum

@@ -1,5 +1,9 @@
+github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
+github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
 github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
+github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
 github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
@@ -19,19 +23,28 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
 golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
+golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
+golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
+golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

+ 147 - 42
pkg/scanner.go

@@ -2,10 +2,15 @@ package kyoketsu
 
 import (
 	"fmt"
+	"log"
 	"net"
-	"net/netip"
+	"os"
 	"sync"
 	"time"
+
+	"github.com/go-ping/ping"
+	"golang.org/x/net/icmp"
+	"golang.org/x/net/ipv4"
 )
 
 var PORT_MAP = map[int]string{
@@ -13,23 +18,77 @@ var PORT_MAP = map[int]string{
 	8082: "unknown", 8085: "unknown", 8090: "unknown", 8091: "unknown", 9010: "unknown", 9012: "unknown", 10000: "unknown",
 }
 
-type TcpScanHost struct {
-	Host         string           `json:"host"`
-	Ipv4Address  netip.Addr       `json:"ipv4_address"`
-	PortsScanned []PortScanResult `json:"ports_scanned"`
+/*
+Need to work with with a database schema in mind, and revolve functionality around that
+*/
+
+type Host struct {
+	Fqdn           string           // The FQDN of the address targeted as per the systems default resolver
+	IpAddress      string           // the IPv4 address (no ipv6 support yet)
+	PingResponse   bool             // boolean value representing if the host responded to ICMP
+	ListeningPorts []map[int]string // list of maps depicting a port number -> service name
+}
+
+/*
+Perform a concurrent TCP port dial on a host, either by domain name or IP.
+
+	:param addr: the address of fqdn to scan
+	:param portmap: a key/value pair of port numbers to service names to dial the host with
+*/
+func PortWalk(addr string, portmap map[int]string) *Host {
+	wg := &sync.WaitGroup{}
+	out := make(chan *PortScanResult)
+
+	wgOuter := &sync.WaitGroup{}
+	wgOuter.Add(1)
+	go func() {
+		defer wgOuter.Done()
+		ports := RetrieveScanDirectives()
+		for p, s := range ports.Pairs {
+			wg.Add(1)
+			port := p
+			svcs := s
+			go func() {
+				out <- singlePortScan(addr, port, svcs)
+				wg.Done()
+			}()
+		}
+		wg.Wait()
+		close(out)
+
+	}()
+
+	host := &Host{IpAddress: addr, ListeningPorts: []map[int]string{}}
+
+	for result := range out {
+		if result.Listening {
+			host.ListeningPorts = append(host.ListeningPorts, map[int]string{
+				result.PortNumber: result.Service,
+			})
+		}
+	}
+	wgOuter.Wait()
+	return host
+
 }
 
 type PortScanResult struct {
-	PortNumber int    `json:"port_number"`
-	Service    string `json:"service"`
-	Protocol   string `json:"protocol"`
-	Listening  bool   `json:"listening"`
+	// This is used to represent the results of a port scan against one host
+	PortNumber int    `json:"port_number"` // The port number that was scanned
+	Service    string `json:"service"`     // the name of the service that the port was identified/mapped to
+	Protocol   string `json:"protocol"`    // The IP protocol (TCP/UDP)
+	Listening  bool   `json:"listening"`   // A boolean value that depicts if the service is listening or not
 }
 
 type PortScanDirective struct {
+	// Struct for dependency injecting the dynamic port map used for scans
 	Pairs map[int]string
 }
 
+/*
+Wrapper function to dependency inject the resource for a port -> service name mapping.
+May move to a database, or something.
+*/
 func RetrieveScanDirectives() PortScanDirective {
 	return PortScanDirective{Pairs: PORT_MAP}
 }
@@ -39,9 +98,9 @@ Scans a single host on a single port
 
 	:param addr: the address to dial
 	:param port: the port number to dial
+	:param svcs: the name of the service that the port is associate with
 */
 func singlePortScan(addr string, port int, svcs string) *PortScanResult {
-	//defer wg.Done()
 	address := fmt.Sprintf("%v:%d", addr, port)
 	conn, err := net.DialTimeout("tcp", address, 1*time.Second)
 	if err != nil {
@@ -52,46 +111,92 @@ func singlePortScan(addr string, port int, svcs string) *PortScanResult {
 }
 
 /*
-Perform a TCP scan on the pointers host
+This function makes use of an external dependency, may or may not keep. It still needs to be implemented
 
-	:param posts: a list of ports to scan against a host
+	    :param addr: the ip address to send an ICMP request to
+		:param pinger: a pointer to a ping.Pinger struct to reuse
 */
-func (t *TcpScanHost) ScanPortRange(ports []int) []*PortScanResult {
-	wg := &sync.WaitGroup{}
-	out := make(chan *PortScanResult)
-	var res []*PortScanResult
-	wgOuter := &sync.WaitGroup{}
-	wgOuter.Add(1)
-	go func() {
-		defer wgOuter.Done()
-		ports := RetrieveScanDirectives()
-		for p, s := range ports.Pairs {
-			wg.Add(1)
-			port := p
-			svcs := s
-			go func() {
-				out <- singlePortScan(t.Ipv4Address.String(), port, svcs)
-				wg.Done()
-			}()
-		}
-		wg.Wait()
-		close(out)
+func PingTarget(addr string, pinger *ping.Pinger) bool {
+	err := pinger.SetAddr(addr)
+	if err != nil {
+		log.Println(err)
+		return false
+	}
+	err = pinger.Run()
+	if err != nil {
+		log.Println(err)
+		return false
+	}
+	stats := pinger.Statistics()
+	if stats.PacketsRecv > 0 {
+		return true
+	}
+	return false
 
-	}()
-	for result := range out {
-		if !result.Listening {
-			continue
-		}
-		res = append(res, result)
+}
+
+/*
+Listen for ICMP responses on a specific address
+
+	:param listening: the address of your server
+*/
+func IcmpListen(listening string) *icmp.PacketConn {
+	icmpSrv, err := icmp.ListenPacket("udp4", listening)
+	icmpSrv.SetDeadline(time.Now().Add(5 * time.Second))
+	if err != nil {
+		log.Println(err)
 	}
-	wgOuter.Wait()
-	return res
+	return icmpSrv
 
 }
 
 /*
-Evaluate whether the host has an entry in the ARP table
+Send an ICMP request to an address and listen for a response (UNFINISHED/NON-FUNCTIONAL)
+
+	    :param icmpSrv: a pointer to an ICMP PacketConn struct, for reading and sending ICMP requests.
+		:param addr: the address to send the request to
 */
-func (h *TcpScanHost) IsOnline() bool {
+func icmpAsk(icmpSrv *icmp.PacketConn, addr string) bool {
+	icmpReq := icmp.Message{
+		Type: ipv4.ICMPTypeEcho, Code: 0,
+		Body: &icmp.Echo{
+			ID: os.Getpid() & 0xffff, Seq: 1,
+			Data: []byte("DIALING FROM KYOKETSU"),
+		},
+	}
+	icmpB, err := icmpReq.Marshal(nil)
+	if err != nil {
+		log.Println(err)
+		return false
+	}
+	if _, err := icmpSrv.WriteTo(icmpB, &net.UDPAddr{IP: net.ParseIP(addr)}); err != nil {
+		log.Println(err)
+		return false
+	}
+	respB := make([]byte, 1500)
+	n, peer, err := icmpSrv.ReadFrom(respB)
+	if err != nil {
+		log.Println(err)
+		return false
+	}
+	rm, err := icmp.ParseMessage(ipv4.ICMPTypeEcho.Protocol(), respB[:n])
+	if err != nil {
+		log.Println(err)
+		return false
+	}
+	switch rm.Type {
+	case ipv4.ICMPTypeEchoReply:
+		echo, ok := rm.Body.(*icmp.Echo)
+		if !ok {
+			return false
+		}
+		if peer.(*net.UDPAddr).IP.String() == addr && echo.Seq == 1 {
+			return true
+		}
+	default:
+		return false
+
+	}
 	return false
+
 }

+ 10 - 6
pkg/storage.go

@@ -5,15 +5,19 @@ import (
 	"fmt"
 	"time"
 
-	"git.aetherial.dev/aeth/kyoketsu/pkg/scan"
 	"go.mongodb.org/mongo-driver/mongo"
 	"go.mongodb.org/mongo-driver/mongo/options"
 )
 
 type TopologyDatabaseIO interface {
-	AddHostToDb(*scan.TcpScanHost) error             // Add a host to the hosts table
-	UpdateHostEntry(string, *scan.TcpScanHost) error //Update a host entry, indexing by its ip address
-	RemoveHostEntry(string) error
+	/*
+			This interface defines the Input and output methods that will be necessary
+		    for an appropriate implementation of the data storage that the distributed system will use.
+		    When I get around to implementing the client-to-client format of this, it could be anything.
+	*/
+	AddHostToDb(*Host) error             // Add a host to the hosts table
+	UpdateHostEntry(string, *Host) error //Update a host entry, indexing by its ip address
+	RemoveHostEntry(string) error        // Remove a host from the database
 }
 
 type MongoClient struct {
@@ -37,11 +41,11 @@ func (m *MongoClient) addDocument(id string, data interface{}) error {
 	return nil
 }
 
-func (m *MongoClient) AddHostToDb(host *scan.TcpScanHost) error {
+func (m *MongoClient) AddHostToDb(host *Host) error {
 	return nil
 }
 
-func (m *MongoClient) UpdateHostEntry(id string, host *scan.TcpScanHost) error {
+func (m *MongoClient) UpdateHostEntry(id string, host *Host) error {
 	return nil
 }