├── README.md ├── pcapversion ├── README.md ├── adapter_windows.go └── main.go └── main.go /README.md: -------------------------------------------------------------------------------- 1 | gorawtcpsyn 2 | =========== 3 | 4 | Simple go program that will test if a port is open by sending a TCP SYN packet to it. Demonstrates how to use RAW IP sockets in Go 1.x+. 5 | 6 | ### Usage 7 | 8 | gorawtcpsyn **ip** **port** 9 | 10 | ##### Example 11 | 12 | gorawtcpsyn 192.168.0.2 5656 13 | 14 | ### Note 15 | 16 | Must run as root. This program is purposefully stupid/easy/dumb/simple. 17 | 18 | ### Other examples of raw socket usage in Go 19 | 20 | The authors of [gopacket](https://github.com/google/gopacket/) (which this example uses), also has a few other examples like an ARP scanner and a SYN scanner in his repository [here](https://github.com/google/gopacket/tree/master/examples). 21 | -------------------------------------------------------------------------------- /pcapversion/README.md: -------------------------------------------------------------------------------- 1 | gorawtcpsyn 2 | =========== 3 | 4 | This is a version that works with Windows. A little modification could make it work with any platform that has pcap. 5 | 6 | This version is slightly more complicated as it knows how to get its local MAC address, get a remote MAC address using ARP packets, and uses pcap to packet sniff. 7 | I haven't implemented it being able to send packets outside the network (getting gateway ip). 8 | 9 | It's not as fast as the main version, which can probably be fixed if I put more time into it. It also is dumb in that it doesn't check the local ARP cache to see 10 | if it knows the remote MAC already. 11 | 12 | This is just a proof of concept for other people to learn off of. 13 | 14 | ### Usage 15 | 16 | gorawtcpsyn **ip** **port** 17 | 18 | ##### Example 19 | 20 | gorawtcpsyn 192.168.0.2 5656 21 | 22 | ### Note 23 | 24 | Must run as root. -------------------------------------------------------------------------------- /pcapversion/adapter_windows.go: -------------------------------------------------------------------------------- 1 | // Taken and modified from golang/src/pkg/net 2 | 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "errors" 8 | "net" 9 | "os" 10 | "syscall" 11 | "unsafe" 12 | ) 13 | 14 | func localMac(dev string) (net.HardwareAddr, error) { 15 | adapterList, err := getAdapterList() 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | for adapter := adapterList; adapter != nil; adapter = adapter.Next { 21 | if bytes.Contains([]byte(dev), adapter.AdapterName[:bytes.IndexRune(adapter.AdapterName[:], 0)]) { 22 | return adapter.Address[:adapter.AddressLength], nil 23 | } 24 | } 25 | 26 | return nil, errors.New("Could not find adapter") 27 | } 28 | 29 | func getAdapterList() (*syscall.IpAdapterInfo, error) { 30 | b := make([]byte, 1000) 31 | l := uint32(len(b)) 32 | a := (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) 33 | // TODO(mikio): GetAdaptersInfo returns IP_ADAPTER_INFO that 34 | // contains IPv4 address list only. We should use another API 35 | // for fetching IPv6 stuff from the kernel. 36 | err := syscall.GetAdaptersInfo(a, &l) 37 | if err == syscall.ERROR_BUFFER_OVERFLOW { 38 | b = make([]byte, l) 39 | a = (*syscall.IpAdapterInfo)(unsafe.Pointer(&b[0])) 40 | err = syscall.GetAdaptersInfo(a, &l) 41 | } 42 | if err != nil { 43 | return nil, os.NewSyscallError("GetAdaptersInfo", err) 44 | } 45 | return a, nil 46 | } 47 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/google/gopacket" 5 | "github.com/google/gopacket/layers" 6 | "log" 7 | "net" 8 | "os" 9 | "strconv" 10 | "time" 11 | ) 12 | 13 | // get the local ip and port based on our destination ip 14 | func localIPPort(dstip net.IP) (net.IP, int) { 15 | serverAddr, err := net.ResolveUDPAddr("udp", dstip.String()+":12345") 16 | if err != nil { 17 | log.Fatal(err) 18 | } 19 | 20 | // We don't actually connect to anything, but we can determine 21 | // based on our destination ip what source ip we should use. 22 | if con, err := net.DialUDP("udp", nil, serverAddr); err == nil { 23 | if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok { 24 | return udpaddr.IP, udpaddr.Port 25 | } 26 | } 27 | log.Fatal("could not get local ip: " + err.Error()) 28 | return nil, -1 29 | } 30 | 31 | func main() { 32 | if len(os.Args) != 3 { 33 | log.Printf("Usage: %s \n", os.Args[0]) 34 | os.Exit(-1) 35 | } 36 | log.Println("starting") 37 | 38 | dstaddrs, err := net.LookupIP(os.Args[1]) 39 | if err != nil { 40 | log.Fatal(err) 41 | } 42 | 43 | // parse the destination host and port from the command line os.Args 44 | dstip := dstaddrs[0].To4() 45 | var dstport layers.TCPPort 46 | if d, err := strconv.ParseUint(os.Args[2], 10, 16); err != nil { 47 | log.Fatal(err) 48 | } else { 49 | dstport = layers.TCPPort(d) 50 | } 51 | 52 | srcip, sport := localIPPort(dstip) 53 | srcport := layers.TCPPort(sport) 54 | log.Printf("using srcip: %v", srcip.String()) 55 | 56 | // Our IP header... not used, but necessary for TCP checksumming. 57 | ip := &layers.IPv4{ 58 | SrcIP: srcip, 59 | DstIP: dstip, 60 | Protocol: layers.IPProtocolTCP, 61 | } 62 | // Our TCP header 63 | tcp := &layers.TCP{ 64 | SrcPort: srcport, 65 | DstPort: dstport, 66 | Seq: 1105024978, 67 | SYN: true, 68 | Window: 14600, 69 | } 70 | tcp.SetNetworkLayerForChecksum(ip) 71 | 72 | // Serialize. Note: we only serialize the TCP layer, because the 73 | // socket we get with net.ListenPacket wraps our data in IPv4 packets 74 | // already. We do still need the IP layer to compute checksums 75 | // correctly, though. 76 | buf := gopacket.NewSerializeBuffer() 77 | opts := gopacket.SerializeOptions{ 78 | ComputeChecksums: true, 79 | FixLengths: true, 80 | } 81 | if err := gopacket.SerializeLayers(buf, opts, tcp); err != nil { 82 | log.Fatal(err) 83 | } 84 | 85 | conn, err := net.ListenPacket("ip4:tcp", "0.0.0.0") 86 | if err != nil { 87 | log.Fatal(err) 88 | } 89 | defer conn.Close() 90 | log.Println("writing request") 91 | if _, err := conn.WriteTo(buf.Bytes(), &net.IPAddr{IP: dstip}); err != nil { 92 | log.Fatal(err) 93 | } 94 | 95 | // Set deadline so we don't wait forever. 96 | if err := conn.SetDeadline(time.Now().Add(10 * time.Second)); err != nil { 97 | log.Fatal(err) 98 | } 99 | 100 | for { 101 | b := make([]byte, 4096) 102 | log.Println("reading from conn") 103 | n, addr, err := conn.ReadFrom(b) 104 | if err != nil { 105 | log.Println("error reading packet: ", err) 106 | return 107 | } else if addr.String() == dstip.String() { 108 | // Decode a packet 109 | packet := gopacket.NewPacket(b[:n], layers.LayerTypeTCP, gopacket.Default) 110 | // Get the TCP layer from this packet 111 | if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { 112 | tcp, _ := tcpLayer.(*layers.TCP) 113 | 114 | if tcp.DstPort == srcport { 115 | if tcp.SYN && tcp.ACK { 116 | log.Printf("Port %d is OPEN\n", dstport) 117 | } else { 118 | log.Printf("Port %d is CLOSED\n", dstport) 119 | } 120 | return 121 | } 122 | } 123 | } else { 124 | log.Printf("Got packet not matching addr") 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /pcapversion/main.go: -------------------------------------------------------------------------------- 1 | // Windows doesn't allow raw TCP sockets. So I attempt to do a SYN 2 | // scan with pcap. 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "flag" 9 | "fmt" 10 | "github.com/google/gopacket" 11 | "github.com/google/gopacket/layers" 12 | "github.com/google/gopacket/pcap" 13 | "io" 14 | "log" 15 | "net" 16 | "os" 17 | "strconv" 18 | "sync" 19 | "time" 20 | ) 21 | 22 | // get the local ip based on our destination ip 23 | func localIP(dstip net.IP) (net.IP, error) { 24 | serverAddr, err := net.ResolveUDPAddr("udp", dstip.String()+":12345") 25 | if err != nil { 26 | return net.IP{}, err 27 | } 28 | 29 | // We don't actually connect to anything, but we can determine 30 | // based on our destination ip what source ip we should use. 31 | con, err := net.DialUDP("udp", nil, serverAddr) 32 | defer con.Close() 33 | if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok { 34 | return udpaddr.IP, nil 35 | } 36 | 37 | return net.IP{}, err 38 | } 39 | 40 | // Sends an ARP request packet to determine the MAC address of an IP 41 | func remoteMac(dev string, srcmac net.HardwareAddr, srcip, dstip net.IP) (net.HardwareAddr, error) { 42 | var dstmac net.HardwareAddr 43 | 44 | eth := &layers.Ethernet{ 45 | SrcMAC: srcmac, 46 | DstMAC: net.HardwareAddr{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 47 | EthernetType: layers.EthernetTypeARP, 48 | } 49 | 50 | arp := &layers.ARP{ 51 | AddrType: layers.LinkTypeEthernet, 52 | Protocol: layers.EthernetTypeIPv4, 53 | HwAddressSize: 6, 54 | ProtAddressSize: 4, 55 | Operation: 1, //arp request 56 | SourceHwAddress: srcmac, 57 | SourceProtAddress: srcip, 58 | DstHwAddress: net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 59 | DstProtAddress: dstip, 60 | } 61 | 62 | buf := gopacket.NewSerializeBuffer() 63 | err := gopacket.SerializeLayers( 64 | buf, 65 | gopacket.SerializeOptions{ 66 | ComputeChecksums: true, // automatically compute checksums 67 | FixLengths: true, 68 | }, 69 | eth, arp, 70 | ) 71 | if err != nil { 72 | return dstmac, err 73 | } 74 | 75 | handle, err := pcap.OpenLive(`rpcap://`+dev, 65535, true, time.Second*1) 76 | if err != nil { 77 | return dstmac, err 78 | } 79 | defer handle.Close() 80 | 81 | var wg sync.WaitGroup 82 | 83 | handle.SetBPFFilter(fmt.Sprintf("arp and ether host %s", srcmac.String())) 84 | packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) 85 | 86 | macchan := make(chan net.HardwareAddr, 1) 87 | 88 | wg.Add(1) 89 | go func() { 90 | stop := false 91 | 92 | go func() { 93 | <-time.After(time.Second * 2) 94 | stop = true 95 | }() 96 | 97 | for { 98 | if stop { 99 | break 100 | } 101 | 102 | packet, err := packetSource.NextPacket() 103 | if err == io.EOF { 104 | break 105 | } else if err != nil { 106 | //log.Println("Error:", err) 107 | continue 108 | } 109 | 110 | if arpLayer := packet.Layer(layers.LayerTypeARP); arpLayer != nil { 111 | arp_, _ := arpLayer.(*layers.ARP) 112 | 113 | if bytes.Equal(arp_.SourceProtAddress, dstip) && arp_.Operation == 2 { 114 | macchan <- arp_.SourceHwAddress 115 | break 116 | } 117 | } 118 | } 119 | 120 | wg.Done() 121 | }() 122 | 123 | err = handle.WritePacketData(buf.Bytes()) 124 | if err != nil { 125 | return dstmac, err 126 | } 127 | 128 | wg.Wait() 129 | 130 | dstmac = <-macchan 131 | return dstmac, nil 132 | } 133 | 134 | func main() { 135 | flag.Parse() 136 | args := flag.Args() 137 | if len(args) != 2 { 138 | log.Printf("Usage: %s \n", os.Args[0]) 139 | os.Exit(-1) 140 | } 141 | 142 | // parse the destination host and port from the command line args 143 | dstip := net.ParseIP(args[0]).To4() 144 | dport_, err := strconv.ParseInt(args[1], 10, 16) 145 | if err != nil { 146 | log.Fatal(err) 147 | } 148 | dport := layers.TCPPort(dport_) 149 | sport := layers.TCPPort(5000) 150 | 151 | // get our local ip. 152 | srcip, err := localIP(dstip) 153 | if err != nil { 154 | log.Fatal(err) 155 | } 156 | 157 | devs, err := pcap.FindAllDevs() 158 | if err != nil { 159 | log.Fatal(err) 160 | } 161 | 162 | dev := "" 163 | for _, d := range devs { 164 | for _, a := range d.Addresses { 165 | if bytes.Equal(a.IP, srcip) { 166 | dev = d.Name 167 | } 168 | } 169 | // if d.Description == "Realtek PCIe GBE Family Controller" { 170 | // fmt.Println(d.Name) 171 | // dev = d.Name 172 | // } 173 | 174 | // fmt.Println(dev.Description) 175 | } 176 | 177 | if dev == "" { 178 | log.Fatal("Could not find the appropriate adapter device") 179 | } 180 | 181 | srcmac, err := localMac(dev) 182 | if err != nil { 183 | log.Fatal(err) 184 | } 185 | 186 | dstmac, err := remoteMac(dev, srcmac, srcip, dstip) 187 | if err != nil { 188 | log.Fatal(err) 189 | } 190 | 191 | eth := &layers.Ethernet{ 192 | SrcMAC: srcmac, 193 | DstMAC: dstmac, 194 | EthernetType: layers.EthernetTypeIPv4, 195 | } 196 | 197 | // Our IPv4 header 198 | ip := &layers.IPv4{ 199 | Version: 4, 200 | IHL: 5, 201 | TOS: 0, 202 | Length: 20, // FIX 203 | Id: 2, 204 | Flags: layers.IPv4DontFragment, 205 | FragOffset: 0, //16384, 206 | TTL: 3, //64, 207 | Protocol: layers.IPProtocolTCP, 208 | Checksum: 0, 209 | SrcIP: srcip, 210 | DstIP: dstip, 211 | } 212 | 213 | // Our TCP header 214 | tcp := &layers.TCP{ 215 | SrcPort: sport, 216 | DstPort: dport, 217 | Seq: 0, 218 | Ack: 0, 219 | SYN: true, 220 | Window: 64240, 221 | Checksum: 0, 222 | Urgent: 0, 223 | } 224 | tcp.DataOffset = 5 // uint8(unsafe.Sizeof(tcp)) 225 | tcp.SetNetworkLayerForChecksum(ip) 226 | 227 | buf := gopacket.NewSerializeBuffer() 228 | err = gopacket.SerializeLayers( 229 | buf, 230 | gopacket.SerializeOptions{ 231 | ComputeChecksums: true, // automatically compute checksums 232 | FixLengths: true, 233 | }, 234 | eth, ip, tcp, 235 | ) 236 | if err != nil { 237 | log.Fatal(err) 238 | } 239 | 240 | handle, err := pcap.OpenLive(`rpcap://`+dev, 65535, true, time.Second*1) 241 | if err != nil { 242 | log.Fatal(err) 243 | } 244 | defer handle.Close() 245 | 246 | var wg sync.WaitGroup 247 | 248 | handle.SetBPFFilter(fmt.Sprintf("tcp and host %s and host %s and port %d and port %d", srcip.String(), dstip.String(), sport, dport)) 249 | packetSource := gopacket.NewPacketSource(handle, handle.LinkType()) 250 | 251 | wg.Add(1) 252 | go func() { 253 | stop := false 254 | 255 | go func() { 256 | <-time.After(time.Second * 2) 257 | stop = true 258 | }() 259 | 260 | for { 261 | if stop { 262 | break 263 | } 264 | 265 | packet, err := packetSource.NextPacket() 266 | if err == io.EOF { 267 | break 268 | } else if err != nil { 269 | //log.Println("Error:", err) 270 | continue 271 | } 272 | 273 | if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil { 274 | tcp, _ := tcpLayer.(*layers.TCP) 275 | if tcp.SrcPort == dport && tcp.DstPort == sport { 276 | if tcp.SYN && tcp.ACK { 277 | fmt.Printf("Port %d is OPEN\n", dport_) 278 | } else { 279 | fmt.Printf("Port %d is CLOSED\n", dport_) 280 | } 281 | break 282 | } 283 | //fmt.Printf("From src port %d to dst port %d\n", tcp.SrcPort, tcp.DstPort) 284 | } 285 | } 286 | 287 | wg.Done() 288 | }() 289 | 290 | err = handle.WritePacketData(buf.Bytes()) 291 | if err != nil { 292 | log.Fatal(err) 293 | } 294 | 295 | wg.Wait() 296 | } 297 | --------------------------------------------------------------------------------