aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/google/gopacket/examples/synscan/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/google/gopacket/examples/synscan/main.go')
-rw-r--r--vendor/github.com/google/gopacket/examples/synscan/main.go259
1 files changed, 259 insertions, 0 deletions
diff --git a/vendor/github.com/google/gopacket/examples/synscan/main.go b/vendor/github.com/google/gopacket/examples/synscan/main.go
new file mode 100644
index 0000000..7a2345f
--- /dev/null
+++ b/vendor/github.com/google/gopacket/examples/synscan/main.go
@@ -0,0 +1,259 @@
+// Copyright 2012 Google, Inc. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style license
+// that can be found in the LICENSE file in the root of the source
+// tree.
+
+// synscan implements a TCP syn scanner on top of pcap.
+// It's more complicated than arpscan, since it has to handle sending packets
+// outside the local network, requiring some routing and ARP work.
+//
+// Since this is just an example program, it aims for simplicity over
+// performance. It doesn't handle sending packets very quickly, it scans IPs
+// serially instead of in parallel, and uses gopacket.Packet instead of
+// gopacket.DecodingLayerParser for packet processing. We also make use of very
+// simple timeout logic with time.Since.
+//
+// Making it blazingly fast is left as an exercise to the reader.
+package main
+
+import (
+ "errors"
+ "flag"
+ "log"
+ "net"
+ "time"
+
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/examples/util"
+ "github.com/google/gopacket/layers"
+ "github.com/google/gopacket/pcap"
+ "github.com/google/gopacket/routing"
+)
+
+// scanner handles scanning a single IP address.
+type scanner struct {
+ // iface is the interface to send packets on.
+ iface *net.Interface
+ // destination, gateway (if applicable), and source IP addresses to use.
+ dst, gw, src net.IP
+
+ handle *pcap.Handle
+
+ // opts and buf allow us to easily serialize packets in the send()
+ // method.
+ opts gopacket.SerializeOptions
+ buf gopacket.SerializeBuffer
+}
+
+// newScanner creates a new scanner for a given destination IP address, using
+// router to determine how to route packets to that IP.
+func newScanner(ip net.IP, router routing.Router) (*scanner, error) {
+ s := &scanner{
+ dst: ip,
+ opts: gopacket.SerializeOptions{
+ FixLengths: true,
+ ComputeChecksums: true,
+ },
+ buf: gopacket.NewSerializeBuffer(),
+ }
+ // Figure out the route to the IP.
+ iface, gw, src, err := router.Route(ip)
+ if err != nil {
+ return nil, err
+ }
+ log.Printf("scanning ip %v with interface %v, gateway %v, src %v", ip, iface.Name, gw, src)
+ s.gw, s.src, s.iface = gw, src, iface
+
+ // Open the handle for reading/writing.
+ // Note we could very easily add some BPF filtering here to greatly
+ // decrease the number of packets we have to look at when getting back
+ // scan results.
+ handle, err := pcap.OpenLive(iface.Name, 65536, true, pcap.BlockForever)
+ if err != nil {
+ return nil, err
+ }
+ s.handle = handle
+ return s, nil
+}
+
+// close cleans up the handle.
+func (s *scanner) close() {
+ s.handle.Close()
+}
+
+// getHwAddr is a hacky but effective way to get the destination hardware
+// address for our packets. It does an ARP request for our gateway (if there is
+// one) or destination IP (if no gateway is necessary), then waits for an ARP
+// reply. This is pretty slow right now, since it blocks on the ARP
+// request/reply.
+func (s *scanner) getHwAddr() (net.HardwareAddr, error) {
+ start := time.Now()
+ arpDst := s.dst
+ if s.gw != nil {
+ arpDst = s.gw
+ }
+ // Prepare the layers to send for an ARP request.
+ eth := layers.Ethernet{
+ SrcMAC: s.iface.HardwareAddr,
+ DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ EthernetType: layers.EthernetTypeARP,
+ }
+ arp := layers.ARP{
+ AddrType: layers.LinkTypeEthernet,
+ Protocol: layers.EthernetTypeIPv4,
+ HwAddressSize: 6,
+ ProtAddressSize: 4,
+ Operation: layers.ARPRequest,
+ SourceHwAddress: []byte(s.iface.HardwareAddr),
+ SourceProtAddress: []byte(s.src),
+ DstHwAddress: []byte{0, 0, 0, 0, 0, 0},
+ DstProtAddress: []byte(arpDst),
+ }
+ // Send a single ARP request packet (we never retry a send, since this
+ // is just an example ;)
+ if err := s.send(&eth, &arp); err != nil {
+ return nil, err
+ }
+ // Wait 3 seconds for an ARP reply.
+ for {
+ if time.Since(start) > time.Second*3 {
+ return nil, errors.New("timeout getting ARP reply")
+ }
+ data, _, err := s.handle.ReadPacketData()
+ if err == pcap.NextErrorTimeoutExpired {
+ continue
+ } else if err != nil {
+ return nil, err
+ }
+ packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy)
+ if arpLayer := packet.Layer(layers.LayerTypeARP); arpLayer != nil {
+ arp := arpLayer.(*layers.ARP)
+ if net.IP(arp.SourceProtAddress).Equal(net.IP(arpDst)) {
+ return net.HardwareAddr(arp.SourceHwAddress), nil
+ }
+ }
+ }
+}
+
+// scan scans the dst IP address of this scanner.
+func (s *scanner) scan() error {
+ // First off, get the MAC address we should be sending packets to.
+ hwaddr, err := s.getHwAddr()
+ if err != nil {
+ return err
+ }
+ // Construct all the network layers we need.
+ eth := layers.Ethernet{
+ SrcMAC: s.iface.HardwareAddr,
+ DstMAC: hwaddr,
+ EthernetType: layers.EthernetTypeIPv4,
+ }
+ ip4 := layers.IPv4{
+ SrcIP: s.src,
+ DstIP: s.dst,
+ Version: 4,
+ TTL: 64,
+ Protocol: layers.IPProtocolTCP,
+ }
+ tcp := layers.TCP{
+ SrcPort: 54321,
+ DstPort: 0, // will be incremented during the scan
+ SYN: true,
+ }
+ tcp.SetNetworkLayerForChecksum(&ip4)
+
+ // Create the flow we expect returning packets to have, so we can check
+ // against it and discard useless packets.
+ ipFlow := gopacket.NewFlow(layers.EndpointIPv4, s.dst, s.src)
+ start := time.Now()
+ for {
+ // Send one packet per loop iteration until we've sent packets
+ // to all of ports [1, 65535].
+ if tcp.DstPort < 65535 {
+ start = time.Now()
+ tcp.DstPort++
+ if err := s.send(&eth, &ip4, &tcp); err != nil {
+ log.Printf("error sending to port %v: %v", tcp.DstPort, err)
+ }
+ }
+ // Time out 5 seconds after the last packet we sent.
+ if time.Since(start) > time.Second*5 {
+ log.Printf("timed out for %v, assuming we've seen all we can", s.dst)
+ return nil
+ }
+
+ // Read in the next packet.
+ data, _, err := s.handle.ReadPacketData()
+ if err == pcap.NextErrorTimeoutExpired {
+ continue
+ } else if err != nil {
+ log.Printf("error reading packet: %v", err)
+ continue
+ }
+
+ // Parse the packet. We'd use DecodingLayerParser here if we
+ // wanted to be really fast.
+ packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy)
+
+ // Find the packets we care about, and print out logging
+ // information about them. All others are ignored.
+ if net := packet.NetworkLayer(); net == nil {
+ // log.Printf("packet has no network layer")
+ } else if net.NetworkFlow() != ipFlow {
+ // log.Printf("packet does not match our ip src/dst")
+ } else if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer == nil {
+ // log.Printf("packet has not tcp layer")
+ } else if tcp, ok := tcpLayer.(*layers.TCP); !ok {
+ // We panic here because this is guaranteed to never
+ // happen.
+ panic("tcp layer is not tcp layer :-/")
+ } else if tcp.DstPort != 54321 {
+ // log.Printf("dst port %v does not match", tcp.DstPort)
+ } else if tcp.RST {
+ log.Printf(" port %v closed", tcp.SrcPort)
+ } else if tcp.SYN && tcp.ACK {
+ log.Printf(" port %v open", tcp.SrcPort)
+ } else {
+ // log.Printf("ignoring useless packet")
+ }
+ }
+}
+
+// send sends the given layers as a single packet on the network.
+func (s *scanner) send(l ...gopacket.SerializableLayer) error {
+ if err := gopacket.SerializeLayers(s.buf, s.opts, l...); err != nil {
+ return err
+ }
+ return s.handle.WritePacketData(s.buf.Bytes())
+}
+
+func main() {
+ defer util.Run()()
+ router, err := routing.New()
+ if err != nil {
+ log.Fatal("routing error:", err)
+ }
+ for _, arg := range flag.Args() {
+ var ip net.IP
+ if ip = net.ParseIP(arg); ip == nil {
+ log.Printf("non-ip target: %q", arg)
+ continue
+ } else if ip = ip.To4(); ip == nil {
+ log.Printf("non-ipv4 target: %q", arg)
+ continue
+ }
+ // Note: newScanner creates and closes a pcap Handle once for
+ // every scan target. We could do much better, were this not an
+ // example ;)
+ s, err := newScanner(ip, router)
+ if err != nil {
+ log.Printf("unable to create scanner for %v: %v", ip, err)
+ continue
+ }
+ if err := s.scan(); err != nil {
+ log.Printf("unable to scan %v: %v", ip, err)
+ }
+ s.close()
+ }
+}