go - Simple worker pool

503 views Asked by At

As a beginner in go and programming in general, I've been coding a port scanner in go with gopacket library and most of the code is done, but I've been running into a problem of spawning too many goroutines and getting 'read ip4 0.0.0.0: i/o timeout' I've done some research and it seems I need to implement a worker pool I've been trying to implement this example 'https://gobyexample.com/worker-pools' as I'm still learning goroutines and channels I've been at it a few days now and can't seem to figure out how to implement the above example correctly in my program can you guys give me some pointers or preferably a code fix example.

package main

import (
    "fmt"
    "log"
    "net"
    "time"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
)

// Get preferred outbound ip and port of this machine
func GetOutboundIPPort() (net.IP, int) {
    conn, err := net.Dial("udp", "1.1.1.1:80")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    localAddr := conn.LocalAddr().(*net.UDPAddr)
    return localAddr.IP, localAddr.Port
}

func ipv4_gen(out chan net.IP) {
    ip, ipnet, err := net.ParseCIDR("192.168.0.0/24")
    if err != nil {
        log.Fatal(err)
    }
    for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
        time.Sleep(300 * time.Millisecond)
        dstaddrs, err := net.LookupIP(ip.String())
        if err != nil {
            log.Fatal(err)
        }
        dstip := dstaddrs[0].To4()
        out <- dstip
    }
    close(out)
}

func inc(ip net.IP) {
    for j := len(ip) - 1; j >= 0; j-- {
        ip[j]++
        if ip[j] > 0 {
            break
        }
    }
}

func port_scanner(dstip net.IP) {
    dstport := layers.TCPPort(80)

    srcip, port := GetOutboundIPPort()
    srcport := layers.TCPPort(port)

    ip := &layers.IPv4{
        SrcIP:    srcip,
        DstIP:    dstip,
        Protocol: layers.IPProtocolTCP,
    }
    tcp := &layers.TCP{
        SrcPort: srcport,
        DstPort: dstport,
        Seq:     1105024978,
        SYN:     true,
        Window:  14600,
    }
    tcp.SetNetworkLayerForChecksum(ip)
    buf := gopacket.NewSerializeBuffer()
    opts := gopacket.SerializeOptions{
        ComputeChecksums: true,
        FixLengths:       true,
    }
    if err := gopacket.SerializeLayers(buf, opts, tcp); err != nil {
        log.Fatal(err)
    }

    conn, err := net.ListenPacket("ip4:tcp", "0.0.0.0")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()
    if _, err := conn.WriteTo(buf.Bytes(), &net.IPAddr{IP: dstip}); err != nil {
        log.Fatal(err)
    }

    // Set deadline so we don't wait forever.
    if err := conn.SetDeadline(time.Now().Add(10 * time.Second)); err != nil {
        log.Fatal(err)
    }

    for {
        b := make([]byte, 4096)
        n, addr, err := conn.ReadFrom(b)
        if err != nil {
            log.Println("error reading packet: ", err)
            return
        } else if addr.String() == dstip.String() {
            // Decode a packet
            packet := gopacket.NewPacket(b[:n], layers.LayerTypeTCP, gopacket.Default)
            // Get the TCP layer from this packet
            if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
                tcp, _ := tcpLayer.(*layers.TCP)

                if tcp.DstPort == srcport {
                    if tcp.SYN && tcp.ACK {
                        fmt.Printf("Discovered open port %d/tcp on %s\n", dstport, dstip)
                    }
                    return
                }
            }
        }
    }
}

func worker(id int, ips <-chan net.IP) {
    for ip := range ips {
        go port_scanner(ip)
    }
}

func main() {
    ips := make(chan net.IP)
    go ipv4_gen(ips)

    for w := 1; w <= 10; w++ {
        go worker(w, ips)
    }
}
1

There are 1 answers

9
Daniel On

You are starting a new goroutine for each piece of work inside the worker so it defeats its purpose.
You need to run the work instead of starting a goroutine inside the worker.