astaxie / NPWG_zh

Network programming with Go 中文翻译版本
583 stars 145 forks source link

在charpter 3 中 `Ping` 代码有问题,具体问题,把代码弄来跑一下就知道了,但是我想咨询各位大佬另外一个问题 #42

Open alpha-baby opened 3 years ago

alpha-baby commented 3 years ago

下列代码是我copy的 Ping 那个demo然后做了一些修改而成

这代码我在本地的mac上跑是有问题的, 但是在linux上跑就是正确的:

mac 系统版本为 1.15.5 go 版本为 1.13.8 和 1.15.5 都试过

原文在: https://github.com/astaxie/NPWG_zh/blob/master/zh/Text/chapter-socket.html 的最底部

package basic

import (
    "fmt"
    "net"
    "os"
    "time"
    "encoding/hex"
)

func Ping() {
    if len(os.Args) != 2 {
        fmt.Println("Usage: ", os.Args[0], "host")
        os.Exit(1)
    }

    addr, err := net.ResolveIPAddr("ip", os.Args[1])
    if err != nil {
        fmt.Println("Resolution error", err.Error())
        os.Exit(1)
    }

    var localAddr *net.IPAddr
    if localAddr == nil {
        conn, err := net.Dial("tcp", "www.baidu.com:80")
        checkError(err)

        localAddr, err = net.ResolveIPAddr("ip4", conn.LocalAddr().(*net.TCPAddr).IP.String())
        checkError(err)
    }

    conn, err := net.DialIP("ip4:icmp", localAddr, addr)
    checkError(err)

    var msg [512]byte
    msg[0] = 8  // echo
    msg[1] = 0  // code 0
    msg[2] = 0  // checksum, fix later
    msg[3] = 0  // checksum, fix later
    msg[4] = 0  // identifier[0]
    msg[5] = 13 //identifier[1]
    msg[6] = 0  // sequence[0]
    msg[7] = 37 // sequence[1]
    length := 8

    check := CheckSum(msg[0:length])
    msg[2] = byte(check >> 8)
    msg[3] = byte(check & 255)
    startTime := time.Now()
    _, err = conn.Write(msg[0:length])
    checkError(err)

    n, err := conn.Read(msg[0:])
    checkError(err)
    fmt.Printf("raw ip msg: %v \n", hex.EncodeToString(msg[:n]))
    endTime := time.Now()
    result := make([]byte, n)
    if n > 20 {
        result = msg[20:]
    }

    fmt.Println("Got response")
    if result[5] == 13 {
        fmt.Println("identifier matches")
    }
    if result[7] == 37 {
        fmt.Println("Sequence matches")
    }
    fmt.Println("ip packet: ", msg[:n], "length: ", n)
    ipHeadLength := int(msg[0]) & 15 * 4
    fmt.Printf("ip header length: %v \n", ipHeadLength)
    fmt.Printf("ip len %x %x \n", msg[2], msg[3]) // 重点在这里,我发现在mac系统上,我用wirshark 抓包抓到的数据和这里读到的数据是不一样的
    totalLength := int(msg[2])<<8 + int(msg[3])
    fmt.Printf("ip total length: %v \n", totalLength)
    ipDataLength := totalLength - ipHeadLength
    fmt.Println("icmp packet: ", result[:ipDataLength])
    t := endTime.Sub(startTime)
    fmt.Printf("time: %.3f ms\n", float64(t.Microseconds())/1000)
}

// 原文中的计算校验和的代码是有问题的,这里我换成了另外的,这个函数是没有问题的
func CheckSum(data []byte) uint16 {
    var (
        sum    uint32
        length int = len(data)
        index  int
    )
    //以每16位为单位进行求和,直到所有的字节全部求完或者只剩下一个8位字节(如果剩余一个8位字节说明字节数为奇数个)
    for length > 1 {
        sum += uint32(data[index])<<8 + uint32(data[index+1])
        index += 2
        length -= 2
    }
    //如果字节数为奇数个,要加上最后剩下的那个8位字节
    if length > 0 {
        sum += uint32(data[index])
    }
    //加上高16位进位的部分
    sum += (sum >> 16)
    //别忘了返回的时候先求反
    return uint16(^sum)
}

这是代码获取到的IP 报文的结果:

=== RUN   TestPing
host:  [103.235.46.39]
raw ip msg: 4500080038c700002f01103e279c454f0a01cbf00000ffcd000d0025
Got response
identifier matches
Sequence matches
ip packet:  [69 0 8 0 56 199 0 0 47 1 16 62 39 156 69 79 10 1 203 240 0 0 255 205 0 13 0 37] length:  28
ip header length: 20
ip len 8 0
ip total length: 2048
--- FAIL: TestPing (0.36s)
panic: runtime error: slice bounds out of range [:2028] with capacity 492 [recovered]
    panic: runtime error: slice bounds out of range [:2028] with capacity 492

用wirshark 抓到的包: IP包 起始位置为第一行的: 45 00

0000   00 e0 4c 6b 10 e0 3c f5 cc 71 88 01 08 00 45 00
0010   00 1c 38 c7 00 00 2f 01 10 3e 27 9c 45 4f 0a 01
0020   cb f0 00 00 ff cd 00 0d 00 25 00 00 00 00 00 00
0030   00 00 00 00 00 00 00 00 00 00 00 00

image

这是不是go的一个bug??

alpha-baby commented 3 years ago

在mac 平台上 执行可能需要 sudo