基于Go语言的端口扫描工具设计与实现(完整代码)

目的是在指定的子网范围内对多个 IP 地址和端口进行扫描,并返回扫描到的开放端口信息。下面是具体操作的细节:

main 函数:

解析端口信息并转换为 PortInfo 结构体的切片。
调用 DiscoverAssets 函数进行扫描。
输出扫描结果。

incrementIP 函数:

将 IP 地址递增,以便遍历子网中的所有 IP 地址。

parsePortInfo 函数:

将简化的端口信息字符串(例如 "21,22,80,U:137,U:161,443,445,U:1900,3306,3389,U:5353,8080")解析为 PortInfo 结构体的切片。
支持 TCP 和 UDP 协议。

ScanPort 函数:

根据协议(TCP 或 UDP)扫描指定 IP 地址上的端口。
对于 UDP 端口,尝试读取数据以确定端口是否开放。
使用 net.DialTimeout 函数进行连接,并设置超时时间。

netErrorTimeout 函数:

检查错误是否为超时错误。

DiscoverAssets 函数:

扫描子网中的所有 IP 地址和端口。
使用并发扫描,通过 semaphore 控制并发数量。
将扫描结果发送到 results 通道,并通过 assets 切片收集结果。

具体操作

并发控制:

使用 semaphore 控制并发的扫描任务数。maxConcurrency 控制同时进行的扫描数量。

结果收集:

使用 results 通道收集扫描结果。
启动一个 goroutine 来从 results 通道中读取结果并将其添加到 assets 切片中。

扫描流程:

遍历子网中的每个 IP 地址。
对于每个 IP 地址,遍历所有的端口信息。
对每个端口,启动一个 goroutine 进行扫描。
扫描完成后,将结果发送到 results 通道。

等待和关闭:

使用 sync.WaitGroup 等待所有扫描任务完成。
关闭 results 通道以指示结果收集完成。
package main

import (
	"fmt"
	"github.com/labstack/gommon/log"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"
)
// 扫描端口的结构体
type PortInfo struct {
	Port     int
	Protocol string
}
type ScanningAsset struct {
	IP      string          `gorm:"type:varchar(200)" json:"ip"`
	Port    int             `json:"port"`
	Status  string          `gorm:"type:varchar(500)" json:"status"`
}
func main() {
	subnet := "192.168.2.0/24"
	simplifyPort := "21,22,80,U:137,U:161,443,445,U:1900,3306,3389,U:5353,8080"
	maxConcurrency := 200
	portInfoList := parsePortInfo(simplifyPort)
	scannedAssets := DiscoverAssets(subnet, portInfoList, maxConcurrency)
	for _, asset := range scannedAssets {
		fmt.Println(asset)
	}
}

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

func parsePortInfo(portStr string) []PortInfo {
	var portInfoList []PortInfo
	segments := strings.Split(portStr, ",")
	for _, segment := range segments {
		if segment == "" {
			continue
		}
		parts := strings.Split(segment, ":")
		protocol := "tcp" // 默认协议
		port := parts[0]
		if len(parts) > 1 {
			protocol = "udp"
			port = parts[1]
		}
		portNumber, err := strconv.Atoi(port)
		if err != nil {
			log.Debug(err.Error())
			continue
		}
		portInfoList = append(portInfoList, PortInfo{Port: portNumber, Protocol: protocol})
	}
	return portInfoList
}

func ScanPort(ip string, portInfo PortInfo) (string, error) {
	address := fmt.Sprintf("%s:%d", ip, portInfo.Port)
	log.Debug(address)
	conn, err := net.DialTimeout(portInfo.Protocol, address, 1*time.Second)
	if err != nil {
		return "closed", err
	}
	if portInfo.Protocol == "udp" {
		conn.SetReadDeadline(time.Now().Add(1 * time.Second))
		_, err := conn.Read(make([]byte, 1))
		if err != nil && !netErrorTimeout(err) {
			return "closed", err
		}
	}
	conn.Close()
	return "open", nil
}

func netErrorTimeout(err error) bool {
	if netErr, ok := err.(net.Error); ok {
		return netErr.Timeout()
	}
	return false
}

func DiscoverAssets(subnet string, portInfoList []PortInfo, maxConcurrency int) []ScanningAsset {
	var assets []ScanningAsset
	var wg sync.WaitGroup
	var mu sync.Mutex
	semaphore := make(chan struct{}, maxConcurrency)
	results := make(chan ScanningAsset)

	go func() {
		for result := range results {
			mu.Lock()
			assets = append(assets, result)
			mu.Unlock()
		}
	}()

	ip, ipNet, err := net.ParseCIDR(subnet)
	if err != nil {
		fmt.Println("Invalid subnet:", err)
		return assets
	}

	for ip := ip.Mask(ipNet.Mask); ipNet.Contains(ip); incrementIP(ip) {
		for _, portInfo := range portInfoList {
			wg.Add(1)
			semaphore <- struct{}{}
			go func(ip string, portinfo PortInfo) {
				defer wg.Done()
				status, _ := ScanPort(ip, portinfo)
				if status == "open" {
					results <- ScanningAsset{
						IP:      ip,
						Port:    portinfo.Port,
						Status:  status,
					}
				}
				<-semaphore
			}(ip.String(), portInfo)
		}
	}

	wg.Wait()
	close(results)
	return assets
}
  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于C语言的端口扫描工具设计实现涉及到网络编程和系统编程的知识。以下是一个简单的基于C语言的端口扫描工具设计实现过程。 首先,我们需要引入相关的头文件,包括netinet/in.h、sys/socket.h和arpa/inet.h。这些头文件提供了一些网络编程所需的函数和数据结构。 接下来,我们需要创建一个Socket,并设置为非阻塞模式。使用socket()函数创建一个套接字,并使用fcntl()函数设置为非阻塞模式。 然后,我们需要定义一个循环,用于遍历要扫描的端口范围。在循环中,使用connect()函数尝试连接每个端口,并通过检查返回值来确定端口的状态。如果返回值是0,表示端口开放,可以成功连接。如果返回值是ECONNREFUSED,表示端口关闭。如果返回值是其他错误码,表示其他情况,需要根据具体情况进行处理。 在实现过程中,为了提高扫描速度,可以使用多线程或者多进程进行并发扫描。通过创建多个线程或者进程来同时扫描不同的端口,可以减少扫描的时间。 最后,我们需要将扫描结果输出到日志文件或者控制台。可以使用printf()函数将结果打印到控制台,或者使用file I/O函数将结果写入到日志文件中。 总结起来,基于C语言的端口扫描工具设计实现需要了解网络编程和系统编程的知识。通过创建Socket、设置为非阻塞模式、使用循环遍历端口范围、使用connect()函数进行连接尝试、使用多线程或多进程进行并发扫描,并将结果输出到日志文件或控制台,可以实现一个简单的端口扫描工具。这只是一个简单示例,实际的实现过程中还需要考虑异常处理、性能优化等方面的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值