Iterating over a multiline variable to return all ips

Viewed 107

I'm trying to build a simple port scanner for a beginner golang project and most of the code works as intended, but I'm having a problem with ipv4_gen() function to return all ips that are generated line by line and pass them to another function to scan them currently ipv4_gen() Returns the first line only is there a way I can iterate over the ip variable to returns all the ips line by line?

package main

import (
    "fmt"
    "log"
    "net"
    "strconv"
    "time"
)

func ipv4_gen() string {
    ip, ipnet, err := net.ParseCIDR("192.168.1.1/24")
    if err != nil {
        log.Fatal(err)
    }
    for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
        return ip.String()
    }
    return ip.String()
}

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

func port_scanner(host string) {
    port := strconv.Itoa(80)
    conn, err := net.DialTimeout("tcp", host+":"+port, 1*time.Second)
    if err == nil {
        fmt.Println("Host:", conn.RemoteAddr().String(), "open")
        conn.Close()
    }
}

func main() {
    port_scanner(ipv4_gen())
}

Here's the link if you wanna run the code https://play.golang.org/p/YWvgnowZzhI

3 Answers

To return multiple results from a function (especially when generating potentially thousands of results) it's idiomatic in Go to use a channel.

package main

import (
    "fmt"
    "log"
    "net"
    "strconv"
    "time"
)

func ipv4_gen(out chan string) {
    ip, ipnet, err := net.ParseCIDR("192.168.1.1/24")
    if err != nil {
        log.Fatal(err)
    }
    for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
        out <- ip.String()
    }
    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(host string) {
    port := strconv.Itoa(80)
    conn, err := net.DialTimeout("tcp", host+":"+port, 1*time.Second)
    if err == nil {
        fmt.Println("Host:", conn.RemoteAddr().String(), "open")
        conn.Close()
    }
}

func main() {
    ips := make(chan string)
    go ipv4_gen(ips)
    for s := range ips {
        port_scanner(s)
    }
}

While channels are useful for communicating between goroutines, you do not benefit from using goroutines to separate address iteration from scanning; the channel slows things down. A simpler and quicker solution is to use an iterator object. An iterator object in any language is designed to do exactly what you requested, to "iterate over the ip variable to returns all the ips".

Here is the code to do so using the the IPAddress Go library. Disclaimer: I am the project manager.

package main

import (
    "fmt"
    "github.com/seancfoley/ipaddress-go/ipaddr"
    "net"
    "time"
)

func ipv4_gen() ipaddr.IPAddressIterator {
    block := ipaddr.NewIPAddressString("192.168.1.0/24").GetAddress()
    iterator := block.WithoutPrefixLen().Iterator()
    iterator.Next() // skip the first address 192.168.1.0
    return iterator
}

func port_scanner(iterator ipaddr.IPAddressIterator) {
    for iterator.HasNext() {
        conn, err := net.DialTimeout("tcp", 
            fmt.Sprint(iterator.Next(), ":", 80), time.Second)
        if err == nil {
            fmt.Println("Host:", conn.RemoteAddr().String(), "open")
            conn.Close()
        }
    }
}

func main() {
    port_scanner(ipv4_gen())
}

If I understood your question correctly then this should work.

package main

import (
    "fmt"
    "log"
    "net"
    "os"
    "strconv"
    "time"
)

func ipv4_gen(ch chan<- string) {
    ip, ipnet, err := net.ParseCIDR("192.168.1.1/24")
    if err != nil {
        log.Fatal(err)
    }
    for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
        ch <- ip.String()
    }
    close(ch)
}

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

func port_scanner(hosts <-chan string) {
    for host := range hosts {
        port := strconv.Itoa(80)
        conn, err := net.DialTimeout("tcp", host+":"+port, 1*time.Second)
        if err != nil {
            fmt.Fprintln(os.Stderr, err)
            continue
        }
        fmt.Println("Host:", conn.RemoteAddr().String(), "open")
        conn.Close()
    }
}

func main() {
    ip := make(chan string)
    go ipv4_gen(ip)
    port_scanner(ip)
}
Related