Skip to content

Instantly share code, notes, and snippets.

@ls0f
Created April 5, 2020 10:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ls0f/941912ca0cf6e756eeb4524e497a7095 to your computer and use it in GitHub Desktop.
Save ls0f/941912ca0cf6e756eeb4524e497a7095 to your computer and use it in GitHub Desktop.
package main
import (
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"log"
"net"
"os"
"strconv"
"time"
)
// get the local ip and port based on our destination ip
func localIPPort(dstip net.IP) (net.IP, int) {
serverAddr, err := net.ResolveUDPAddr("udp", dstip.String()+":12345")
if err != nil {
log.Fatal(err)
}
// We don't actually connect to anything, but we can determine
// based on our destination ip what source ip we should use.
if con, err := net.DialUDP("udp", nil, serverAddr); err == nil {
if udpaddr, ok := con.LocalAddr().(*net.UDPAddr); ok {
return udpaddr.IP, udpaddr.Port
}
}
log.Fatal("could not get local ip: " + err.Error())
return nil, -1
}
func main() {
if len(os.Args) != 3 {
log.Printf("Usage: %s <host/ip> <port>\n", os.Args[0])
os.Exit(-1)
}
log.Println("starting")
dstaddrs, err := net.LookupIP(os.Args[1])
if err != nil {
log.Fatal(err)
}
// parse the destination host and port from the command line os.Args
dstip := dstaddrs[0].To4()
var dstport layers.TCPPort
if d, err := strconv.ParseUint(os.Args[2], 10, 16); err != nil {
log.Fatal(err)
} else {
dstport = layers.TCPPort(d)
}
srcip, sport := localIPPort(dstip)
srcport := layers.TCPPort(sport)
log.Printf("using srcip: %v", srcip.String())
// Our IP header... not used, but necessary for TCP checksumming.
ip := &layers.IPv4{
SrcIP: srcip,
DstIP: dstip,
Protocol: layers.IPProtocolTCP,
}
// Our TCP header
tcp := &layers.TCP{
SrcPort: srcport,
DstPort: dstport,
Seq: 1105024978,
SYN: true,
Window: 14600,
}
tcp.SetNetworkLayerForChecksum(ip)
// Serialize. Note: we only serialize the TCP layer, because the
// socket we get with net.ListenPacket wraps our data in IPv4 packets
// already. We do still need the IP layer to compute checksums
// correctly, though.
buf := gopacket.NewSerializeBuffer()
opts := gopacket.SerializeOptions{
ComputeChecksums: true,
FixLengths: true,
}
if err := gopacket.SerializeLayers(buf, opts, tcp); err != nil {
log.Fatal(err)
}
conn, err := net.ListenPacket("ip4:tcp", "0.0.0.0")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
log.Println("writing request")
sendMsg(conn, dstip, opts, tcp)
if err := conn.SetDeadline(time.Now().Add(10 * time.Second)); err != nil {
log.Fatal(err)
}
payload1 := []byte("GET /404 HTTP/1.0\r\nHost: baidu.com")
payload2 := []byte("\r\n\r\n")
// payload := []byte(string(payload1) + string(payload2))
seq := tcp.Seq + 1
for {
b := make([]byte, 4096)
log.Println("reading from conn")
n, addr, err := conn.ReadFrom(b)
if err != nil {
log.Println("error reading packet: ", err)
return
} else if addr.String() == dstip.String() {
// Decode a packet
packet := gopacket.NewPacket(b[:n], layers.LayerTypeTCP, gopacket.Default)
// Get the TCP layer from this packet
if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
rtcp, _ := tcpLayer.(*layers.TCP)
if rtcp.DstPort == srcport {
if rtcp.SYN && rtcp.ACK {
log.Printf("Port %d is OPEN\n", dstport)
// 数据包设置ACK位
tcp.ACK = true
tcp.Ack = rtcp.Seq + 1
tcp.PSH = true
tcp.SYN = false
// 模拟第一个数据包丢失,先发最后四个字节,这个包发出去也会握手成功
// 先发payload2
tcp.Seq = seq + uint32(len(payload1))
sendMsg(conn, dstip, opts, tcp, gopacket.Payload(payload2))
} else if rtcp.ACK && rtcp.Ack == seq {
// 客户端重发相同的握手ack
// 发送payload1
tcp.Seq = rtcp.Ack
sendMsg(conn, dstip, opts, tcp, gopacket.Payload(payload1))
} else if rtcp.ACK && rtcp.Ack == seq + uint32(len(payload1)) {
// payload1 ack了
// 再发payload2
tcp.Seq = rtcp.Ack
sendMsg(conn, dstip, opts, tcp, gopacket.Payload(payload2))
return
}
}
}
} else {
log.Printf("Got packet not matching addr")
}
}
}
func sendMsg(conn net.PacketConn, dstip net.IP, opts gopacket.SerializeOptions, layer ...gopacket.SerializableLayer) {
buf := gopacket.NewSerializeBuffer()
if err := gopacket.SerializeLayers(buf, opts, layer...); err != nil {
log.Fatal(err)
}
if _, err := conn.WriteTo(buf.Bytes(), &net.IPAddr{IP: dstip}); err != nil {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment