# kyoketsu: Go Coverage Report

not tracked not covered covered

    /*
                    GNU GENERAL PUBLIC LICENSE
                    Version 3, 29 June 2007

    kyoketsu, a Client-To-Client Network Enumeration System
    Copyright (C) 2024 Russell Hrubesky, ChiralWorks Software LLC

     Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
     Everyone is permitted to copy and distribute verbatim copies
     of this license document, but changing it is not allowed.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License,
    or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program. If not, see <http://www.gnu.org/licenses/>.

    */

    package main

    import (
            "flag"
            "fmt"
            "log"
            "net"
            "os"
            "strings"
            "sync"

            kyoketsu "git.aetherial.dev/aeth/kyoketsu/pkg"
    )

    var licenseMsg = "\n        http-wokou  Copyright (C) 2024  Russell Hrubesky, ChiralWorks Software LLC\n        This program comes with ABSOLUTELY NO WARRANTY; for details type `http-wokou --license`\n        This is free software, and you are welcome to redistribute it\n        under certain conditions; type `http-wokou --redist` for details.\n\n"

    var redistMsg = "\n        This program is free software: you can redistribute it and/or modify\n        it under the terms of the GNU General Public License as published by\n        the Free Software Foundation, either version 3 of the License, or\n        (at your option) any later version.\n\n"

    var licenseMsgLong = "\n                GNU GENERAL PUBLIC LICENSE\n                Version 3, 29 June 2007\n        Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n        Everyone is permitted to copy and distribute verbatim copies\n        of this license document, but changing it is not allowed.\n\n        http-wokou, An HTTP Proxying framework for bypassing DNS Security\n        Copyright (C) 2024 Russell Hrubesky, ChiralWorks Software LLC\n\n        This program is free software: you can redistribute it and/or modify\n        it under the terms of the GNU General Public License as published by\n        the Free Software Foundation, either version 3 of the License, or\n        (at your option) any later version.\n\n        This program is distributed in the hope that it will be useful,\n        but WITHOUT ANY WARRANTY; without even the implied warranty of\n        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n        GNU General Public License for more details.\n\n        You should have received a copy of the GNU General Public License\n        along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\n"

    func main() {

            ip := flag.String("ips", "", "single ip address with CIDR notation to gather info about")
            licenseInfo := flag.Bool("license", false, "Pass this flag to display license and warantee information.")
            redistInfo := flag.Bool("redist", false, "Pass this flag to display redistribution information.")
            flag.Parse()

            if *licenseInfo {
                    fmt.Println(licenseMsgLong)
                    os.Exit(0)
            }
            if *redistInfo {
                    fmt.Println(redistMsg)
                    os.Exit(0)
            }
            fmt.Println(licenseMsg)
            var err error
            var addr *kyoketsu.IpSubnetMapper
            addr, err = kyoketsu.GetNetworkAddresses(*ip)
            if err != nil {
                    log.Fatal(err)
            }
            var wg sync.WaitGroup
            for i := range addr.Ipv4s {
                    wg.Add(1)
                    go func(target string, wg *sync.WaitGroup) {
                            defer wg.Done()
                            out := kyoketsu.PortWalk(target, kyoketsu.RetrieveScanDirectives().Pairs)
                            if len(out.ListeningPorts) > 0 {
                                    dns, _ := net.LookupAddr(out.IpAddress)
                                    out.Fqdn = strings.Join(dns, ", ")

                                    fmt.Printf("%+v\n", out)

                            }

                    }(addr.Ipv4s[i].String(), &wg)

            }
            wg.Wait()
    }

    /*
                    GNU GENERAL PUBLIC LICENSE
                    Version 3, 29 June 2007

    kyoketsu, a Client-To-Client Network Enumeration System
    Copyright (C) 2024 Russell Hrubesky, ChiralWorks Software LLC

     Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
     Everyone is permitted to copy and distribute verbatim copies
     of this license document, but changing it is not allowed.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License,
    or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program. If not, see <http://www.gnu.org/licenses/>.

    */

    package kyoketsu

    import (
            "fmt"
            "log"
            "net"
            "net/netip"
            "strconv"
            "strings"
    )

    type NetworkInterfaceNotFound struct{ Passed string }

    // Implementing error interface
    func (n *NetworkInterfaceNotFound) Error() string {
            return fmt.Sprintf("Interface: '%s' not found.", n.Passed)
    }

    type IpSubnetMapper struct {
            Ipv4s       []net.IP `json:"addresses"`
            NetworkAddr net.IP
            Current     net.IP
            Mask        int
    }

    /*
    Get the next IPv4 address of the address specified in the 'addr' argument,

            :param addr: the address to get the next address of
    */
    func getNextAddr(addr string) string {
            parsed, err := netip.ParseAddr(addr)
            if err != nil {
                    log.Fatal("failed while parsing address in getNextAddr() ", err, "\n")
            }
            return parsed.Next().String()

    }

    /*
    get the network address of the ip address in 'addr' with the subnet mask from 'cidr'

                :param addr: the ipv4 address to get the network address of
                    :param cidr: the CIDR notation of the subbet
    */
    func getNetwork(addr string, cidr int) string {
            addr = fmt.Sprintf("%s/%v", addr, cidr)
            ip, net, err := net.ParseCIDR(addr)
            if err != nil {
                    log.Fatal("failed whilst attempting to parse cidr in getNetwork() ", err, "\n")
            }
            return ip.Mask(net.Mask).String()

    }

    /*
    Recursive function to get all of the IPv4 addresses for each IPv4 network that the host is on

                 :param ipmap: a pointer to an IpSubnetMapper struct which contains domain details such as
                                   the subnet mask, the original network mask, and the current IP address used in the
                                               recursive function
                    :param max: This is safety feature to prevent stack overflows, so you can manually set the depth to
                                call the function
    */
    func addressRecurse(ipmap *IpSubnetMapper) {

            next := getNextAddr(ipmap.Current.String())

            nextNet := getNetwork(next, ipmap.Mask)
            currentNet := ipmap.NetworkAddr.String()

            if nextNet != currentNet {
                    return
            }
            ipmap.Current = net.ParseIP(next)

            ipmap.Ipv4s = append(ipmap.Ipv4s, net.ParseIP(next))
            addressRecurse(ipmap)
    }

    /*
    Get all of the IPv4 addresses in the network that 'addr' belongs to. YOU MUST PASS THE ADDRESS WITH CIDR NOTATION
    i.e. '192.168.50.1/24'

            :param addr: the ipv4 address to use for subnet discovery
    */
    func GetNetworkAddresses(addr string) (*IpSubnetMapper, error) {
            ipmap := &IpSubnetMapper{Ipv4s: []net.IP{}}

            ip, net, err := net.ParseCIDR(addr)
            if err != nil {
                    return nil, err
            }
            mask, err := strconv.Atoi(strings.Split(addr, "/")[1])
            if err != nil {
                    return nil, err
            }
            ipmap.NetworkAddr = ip.Mask(net.Mask)
            ipmap.Mask = mask
            ipmap.Current = ip.Mask(net.Mask)
            addressRecurse(ipmap)

            return ipmap, nil

    }

    /*
            GNU GENERAL PUBLIC LICENSE
            Version 3, 29 June 2007

    kyoketsu, a Client-To-Client Network Enumeration System
    Copyright (C) 2024 Russell Hrubesky, ChiralWorks Software LLC

            Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
            Everyone is permitted to copy and distribute verbatim copies
            of this license document, but changing it is not allowed.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License,
    or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program. If not, see <http://www.gnu.org/licenses/>.
    */
    package kyoketsu

    import (
            "fmt"
            "net"
            "sync"
            "time"
    )

    var PORT_MAP = map[int]string{
            22: "ssh", 23: "telnet", 53: "dns", 80: "http", 25: "smtp", 443: "https", 8080: "unknown", 8081: "unknown",
            //8082: "unknown", 8085: "unknown", 8090: "unknown", 8091: "unknown", 9010: "unknown", 9012: "unknown", 10000: "unknown", 1433: "microsoft_sql",
            3306: "mysql", 3050: "firebird", 5432: "postgres", 27017: "mongo", 6379: "redis", 8005: "tomcat", 6443: "kubernetes", 853: "dns-tls", 143: "imap",
            389: "ldap", 445: "smb", 543: "kerberos", 544: "kerberos", 749: "kerberos", 760: "kerberos",
    }

    /*
    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 := []*PortScanResult{}

            for p, s := range portmap {
                    wg.Add(1)
                    go func(target string, p int, s string) {
                            defer wg.Done()
                            out = append(out, singlePortScan(target, p, s))
                    }(addr, p, s)
            }
            wg.Wait()
            host := &Host{IpAddress: addr, ListeningPorts: map[int]string{}}
            for i := range out {
                    if out[i].Listening {
                            host.ListeningPorts[out[i].PortNumber] = out[i].Service
                    }

            }
            return host

    }

    type PortScanResult struct {
            // 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}
    }

    /*
    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 {
            address := fmt.Sprintf("%v:%d", addr, port)
            conn, err := net.DialTimeout("tcp", address, 5*time.Second)
            if err != nil {
                    return &PortScanResult{PortNumber: port, Protocol: "tcp", Service: svcs, Listening: false}
            }
            conn.Close()
            return &PortScanResult{PortNumber: port, Protocol: "tcp", Service: svcs, Listening: true}
    }

    /*
                    GNU GENERAL PUBLIC LICENSE
                    Version 3, 29 June 2007

    kyoketsu, a Client-To-Client Network Enumeration System
    Copyright (C) 2024 Russell Hrubesky, ChiralWorks Software LLC

     Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
     Everyone is permitted to copy and distribute verbatim copies
     of this license document, but changing it is not allowed.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License,
    or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program. If not, see <http://www.gnu.org/licenses/>.

    */

    package kyoketsu

    import (
            "context"
            "fmt"
            "time"

            "go.mongodb.org/mongo-driver/mongo"
            "go.mongodb.org/mongo-driver/mongo/options"
    )

    type TopologyDatabaseIO interface {
            /*
                            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 {
            conn *mongo.Client
    }

    func NewMongoClient(host string, port int) *MongoClient {

            ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
            defer cancel()
            client, err := mongo.Connect(ctx, options.Client().ApplyURI(fmt.Sprintf("mongodb://%s:%v", host, port)))
            defer func() {
                    if err = client.Disconnect(ctx); err != nil {
                            panic(err)
                    }
            }()
            return &MongoClient{conn: client}
    }

    func (m *MongoClient) addDocument(id string, data interface{}) error {
            return nil
    }

    func (m *MongoClient) AddHostToDb(host *Host) error {
            return nil
    }

    func (m *MongoClient) UpdateHostEntry(id string, host *Host) error {
            return nil
    }

    func (m *MongoClient) RemoveHostEntry(id string) error {
            return nil
    }