187 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			187 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package ipv4
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	"git.loafle.net/commons_go/logging"
 | |
| 	"git.loafle.net/overflow/overflow_discovery/api/module/discovery/model"
 | |
| 	"git.loafle.net/overflow/overflow_discovery/commons/pcap"
 | |
| 	"github.com/google/gopacket"
 | |
| 	"github.com/google/gopacket/layers"
 | |
| )
 | |
| 
 | |
| func scanPortTCP(host *model.Host, dp *model.DiscoveryPort, resultChan chan interface{}, errChan chan error, stopChan chan struct{}, wg *sync.WaitGroup) {
 | |
| 	defer func() {
 | |
| 		wg.Done()
 | |
| 	}()
 | |
| 
 | |
| 	ps, err := pcap.RetainScanner(host.Zone)
 | |
| 	if nil != err {
 | |
| 		errChan <- fmt.Errorf("Discovery: Cannot retain pcap instance %v", err)
 | |
| 		return
 | |
| 	}
 | |
| 	defer func() {
 | |
| 		pcap.ReleaseScanner(host.Zone)
 | |
| 	}()
 | |
| 
 | |
| 	tcpChan := ps.OpenTCP(host.IP)
 | |
| 	defer func() {
 | |
| 		ps.CloseTCP(host.IP, tcpChan)
 | |
| 	}()
 | |
| 
 | |
| 	go func() {
 | |
| 		ports := make(map[int]*model.Port)
 | |
| 
 | |
| 		for {
 | |
| 			select {
 | |
| 			case packet, ok := <-tcpChan:
 | |
| 				if !ok {
 | |
| 					logging.Logger().Debug(fmt.Sprintf("Discovery: tcp channel is closed"))
 | |
| 					return
 | |
| 				}
 | |
| 				if p := handlePacketTCP(host, ports, packet); nil != p {
 | |
| 					resultChan <- p
 | |
| 				}
 | |
| 			case <-stopChan:
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	if err := sendTCP(host, dp, stopChan); nil != err {
 | |
| 		errChan <- err
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	timer := time.NewTimer(20 * time.Second)
 | |
| 
 | |
| 	select {
 | |
| 	case <-stopChan:
 | |
| 		return
 | |
| 	case <-timer.C:
 | |
| 		return
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func sendTCP(host *model.Host, dp *model.DiscoveryPort, stopChan chan struct{}) error {
 | |
| 	tcpPacket, err := makePacketPortTCP(host)
 | |
| 	if nil != err {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer func() {
 | |
| 		tcpPacket.PacketConn.Close()
 | |
| 	}()
 | |
| 
 | |
| 	buf := gopacket.NewSerializeBuffer()
 | |
| 
 | |
| Loop:
 | |
| 	for portNumber := dp.FirstScanRange; portNumber < dp.LastScanRange; portNumber++ {
 | |
| 		if nil != dp.ExcludePorts {
 | |
| 			for _, exPortNumber := range dp.ExcludePorts {
 | |
| 				if portNumber == exPortNumber {
 | |
| 					continue Loop
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		tcpPacket.TCP.DstPort = layers.TCPPort(portNumber)
 | |
| 		tcpPacket.TCP.SetNetworkLayerForChecksum(tcpPacket.IP)
 | |
| 
 | |
| 		if err := gopacket.SerializeLayers(buf, tcpPacket.Opts, tcpPacket.TCP); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if _, err := tcpPacket.PacketConn.WriteTo(buf.Bytes(), &net.IPAddr{IP: tcpPacket.IP.DstIP}); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		timer := time.NewTimer(time.Microsecond * 100)
 | |
| 
 | |
| 		select {
 | |
| 		case <-stopChan:
 | |
| 			return nil
 | |
| 		case <-timer.C:
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func handlePacketTCP(host *model.Host, ports map[int]*model.Port, packet *layers.TCP) *model.Port {
 | |
| 	if nil == packet || packet.DstPort != 60000 {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if packet.RST {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	portNumber := int(packet.SrcPort)
 | |
| 
 | |
| 	if _, ok := ports[portNumber]; ok || !cr.Contains(ip) {
 | |
| 		return nil
 | |
| 	}
 | |
| 	h := &model.Host{}
 | |
| 	h.IP = ip.String()
 | |
| 	h.Mac = net.HardwareAddr(packet.SourceHwAddress).String()
 | |
| 	h.Zone = zone
 | |
| 
 | |
| 	hosts[ip.String()] = h
 | |
| 
 | |
| 	p := &model.Port{
 | |
| 		PortType:   model.PortTypeTCP,
 | |
| 		PortNumber: portNumber,
 | |
| 	}
 | |
| 	p.Host = host
 | |
| 
 | |
| 	return p
 | |
| }
 | |
| 
 | |
| type PortPacketTCP struct {
 | |
| 	IP         *layers.IPv4
 | |
| 	TCP        *layers.TCP
 | |
| 	Opts       gopacket.SerializeOptions
 | |
| 	PacketConn net.PacketConn
 | |
| }
 | |
| 
 | |
| func makePacketPortTCP(host *model.Host) (*PortPacketTCP, error) {
 | |
| 	packetTCP := &PortPacketTCP{}
 | |
| 
 | |
| 	srcIP := net.ParseIP(host.Zone.IP)
 | |
| 	if nil == srcIP {
 | |
| 		return nil, fmt.Errorf("Discovery: IP(%s) of zone is not valid", host.Zone.IP)
 | |
| 	}
 | |
| 	dstIP := net.ParseIP(host.IP)
 | |
| 	if nil == dstIP {
 | |
| 		return nil, fmt.Errorf("Discovery: IP(%s) of host is not valid", host.IP)
 | |
| 	}
 | |
| 
 | |
| 	packetTCP.IP = &layers.IPv4{
 | |
| 		SrcIP:    srcIP.To4(),
 | |
| 		DstIP:    dstIP.To4(),
 | |
| 		Version:  4,
 | |
| 		TTL:      64,
 | |
| 		Protocol: layers.IPProtocolTCP,
 | |
| 	}
 | |
| 	packetTCP.TCP = &layers.TCP{
 | |
| 		SrcPort: 60000,
 | |
| 		DstPort: 0, // will be incremented during the scan
 | |
| 		SYN:     true,
 | |
| 		Seq:     0,
 | |
| 	}
 | |
| 	packetTCP.Opts = gopacket.SerializeOptions{
 | |
| 		ComputeChecksums: true,
 | |
| 		FixLengths:       true,
 | |
| 	}
 | |
| 
 | |
| 	conn, err := net.ListenPacket("ip4:tcp", "0.0.0.0")
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("Discovery: SYN create socket error %v", err)
 | |
| 	}
 | |
| 
 | |
| 	packetTCP.PacketConn = conn
 | |
| 
 | |
| 	return packetTCP, nil
 | |
| }
 |