尝试在go中实现端口扫描程序

时间:2015-12-25 12:08:47

标签: networking go goroutine

我最近开始学习go。唯一的共鸣就是goroutine的东西,似乎只存在于这种语言中(我有java背景,老实说,赢了,并没有完全转向去)。 我想实现一个简单的端口扫描程序,它可以在给定的网络范围内找到每个http服务器(已打开端口80的主机)。这是我如何做到这一点:

package main

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

// next two functions are shamelessly copied from somewhere 

func ip2long(ipstr string) (ip uint32) {
    r := `^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})`
    reg, err := regexp.Compile(r)
    if err != nil {
        return
    }
    ips := reg.FindStringSubmatch(ipstr)
    if ips == nil {
        return
    }
    ip1, _ := strconv.Atoi(ips[1])
    ip2, _ := strconv.Atoi(ips[2])
    ip3, _ := strconv.Atoi(ips[3])
    ip4, _ := strconv.Atoi(ips[4])
    if ip1 > 255 || ip2 > 255 || ip3 > 255 || ip4 > 255 {
        return
}
    ip += uint32(ip1 * 0x1000000)
    ip += uint32(ip2 * 0x10000)
    ip += uint32(ip3 * 0x100)
    ip += uint32(ip4)
    return
}
func long2ip(ip uint32) string {
    return fmt.Sprintf("%d.%d.%d.%d", ip>>24, ip<<8>>24, ip<<16>>24, ip<<24>>24)
}

// the actual code
func main() {
    seconds := 10 // timeout
    fmt.Println(seconds) // just to see it
    timeOut := time.Duration(seconds) * time.Second // time out to pass to the DialTimeout 
    can := make(chan int) // a chan
    req := func (ip string){ // parallelized function to do requests 
    c, err := net.DialTimeout("tcp", ip+":80",timeOut) // connect to ip with given timeout
    if err == nil { // if we're connected
        fmt.Println(ip) // output the successful ip
        c.Close() // close connection
    }
    can <- 0 // tell that we're done

}

    startIp := ip2long("50.97.99.0") // starting ip
    endIp := ip2long("50.97.102.0")
    curIp := startIp // current ip

    go func(){ // a demon function ran as goroutine which listens to the chan
        count := 0 // how many ips we processed
        looper: // label to break
        for{
            <- can // wait for some goroutine to finish
            curIp++ // next ip
            count++
            go req(long2ip(curIp)) // start new goroutine
            if (curIp > endIp) { // if we've walked through the range
                fmt.Println("final")
                break looper;
            }
        }
    }()
    numGoes := 100 // number of goroutines ran at one time
    for i := 0; i < numGoes; i++{
        can <- 0 // start 100 goroutines
    }
    // standard way to make the program hung
    var input string
    fmt.Scanln(&input)
}

我希望代码得到很好的评论,以便您可以看到我尝试做的事情。 ip范围是一些托管公司的范围,我知道IP 50.97.99.189运行http服务器,但问题是当我运行我的给定代码时,这个IP永远不会出现在控制台中,尽管主机已启动和ping时间大约是156毫秒,所以10秒就足够了。 所以问题 - 我做错了什么?

1 个答案:

答案 0 :(得分:0)

这是一个稍微改写的版本,更加惯用go。

有更短的方式来编写它,但这可能更清楚。

逻辑基本相同。我刚刚运行它并且工作正常,打印出几个连接的ips。此版本还会打印失败的原因,更多的是用于故障排除。

运行此版本时是否还有问题?如果是这样,你得到什么错误?

我的版本,on Play

package main

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

// next two functions are shamelessly copied from somewhere

func ip2long(ipstr string) (uint32, error) {
    r := `^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})`
    reg, err := regexp.Compile(r)
    if err != nil {
        return 0, err
    }
    ips := reg.FindStringSubmatch(ipstr)
    if ips == nil {
        return 0, fmt.Errorf("Invalid ip address")
    }
    var ip1, ip2, ip3, ip4 int
    if ip1, err = strconv.Atoi(ips[1]); err != nil {
        return 0, err
    }
    if ip2, err = strconv.Atoi(ips[2]); err != nil {
        return 0, err
    }
    if ip3, err = strconv.Atoi(ips[3]); err != nil {
        return 0, err
    }
    if ip4, err = strconv.Atoi(ips[4]); err != nil {
        return 0, err
    }
    if ip1 > 255 || ip2 > 255 || ip3 > 255 || ip4 > 255 {
        return 0, fmt.Errorf("Invalid ip address")
    }
    ip := uint32(ip1 * 0x1000000)
    ip += uint32(ip2 * 0x10000)
    ip += uint32(ip3 * 0x100)
    ip += uint32(ip4)
    return ip, nil
}
func long2ip(ip uint32) string {
    return fmt.Sprintf("%d.%d.%d.%d", ip>>24, ip<<8>>24, ip<<16>>24, ip<<24>>24)
}

// the actual code
func main() {
    timeOut := 10 * time.Second // time out to pass to the DialTimeout
    fmt.Println("Timeout is:", timeOut)
    req := func(ip string) { // parallelized function to do requests
        c, err := net.DialTimeout("tcp", ip+":80", timeOut) // connect to ip with given timeout
        if err == nil {                                     // if we're connected
            fmt.Println(ip) // output the successful ip
            c.Close()       // close connection
        } else {
            fmt.Println("Error is:", err)
        }
    }

    startIp, err := ip2long("50.97.99.0") // starting ip
    if err != nil {
        fmt.Println(err)
        return
    }
    endIp, err := ip2long("50.97.102.0")
    if err != nil {
        fmt.Println(err)
        return
    }
    var wg sync.WaitGroup    // synchronizer for main routine to wait for spawned workers
    ips := make(chan uint32) // channel to feed ip addrs

    //spawn 100 workers
    for idx := 0; idx < 100; idx++ {
        wg.Add(1)
        go func() {
            for ip := range ips {
                req(long2ip(ip)) // perform check of ip
            }
            wg.Done()
        }()
    }

    // send ip addrs to workers to process
    for curIp := startIp; curIp <= endIp; curIp++ {
        ips <- curIp
    }
    close(ips) // signal goroutines to end
    wg.Wait()  //wait for all goroutines to complete
}