//  [go]
func parseV2(r *bufio.Reader) (*HeaderV2, error) {
	buf := make([]byte, 232)
	n, err := io.ReadFull(r, buf[:16])
	if err != nil {
		return nil, &InvalidHeaderErr{Read: buf[:n], error: err}
	}
	var rawHdr rawV2
	err = binary.Read(bytes.NewReader(buf), binary.BigEndian, &rawHdr)
	if err != nil {
		return nil, &InvalidHeaderErr{Read: buf[:16], error: err}
	}
	if !bytes.Equal(rawHdr.Sig[:], sigV2) {
		return nil, &InvalidHeaderErr{Read: buf[:16], error: errors.New("invalid signature")}
	}
	// highest 4 indicate version
	if (rawHdr.VerCmd >> 4) != 2 {
		return nil, &InvalidHeaderErr{Read: buf[:16], error: errors.New("invalid v2 version value")}
	}
	var h HeaderV2
	// lowest 4 = command (0xf == 0b00001111)
	h.Command = Cmd(rawHdr.VerCmd & 0xf)
	if h.Command > CmdProxy {
		return nil, &InvalidHeaderErr{Read: buf[:16], error: errors.New("invalid v2 command")}
	}

	// highest 4 indicate address family
	switch rawHdr.FamProto >> 4 {
	case 0: // local
		if rawHdr.Len != 0 {
			return nil, &InvalidHeaderErr{Read: buf[:16], error: errors.New("invalid length")}
		}
	case 1: // ipv4
		if rawHdr.Len != 12 {
			return nil, &InvalidHeaderErr{Read: buf[:16], error: errors.New("invalid length")}
		}
	case 2: // ipv6
		if rawHdr.Len != 36 {
			return nil, &InvalidHeaderErr{Read: buf[:16], error: errors.New("invalid length")}
		}
	case 3: // unix
		if rawHdr.Len != 216 {
			return nil, &InvalidHeaderErr{Read: buf[:16], error: errors.New("invalid length")}
		}
	default:
		return nil, &InvalidHeaderErr{Read: buf[:16], error: errors.New("invalid v2 address family")}
	}

	// lowest 4 = transport protocol (0xf == 0b00001111)
	if (rawHdr.FamProto & 0xf) > 2 {
		return nil, &InvalidHeaderErr{Read: buf[:16], error: errors.New("invalid v2 transport protocol")}
	}

	buf = buf[:16+int(rawHdr.Len)]

	n, err = io.ReadFull(r, buf[16:])
	if err != nil {
		return nil, &InvalidHeaderErr{Read: buf[:16+n], error: err}
	}

	if h.Command == CmdLocal {
		// ignore address information for local
		return &h, nil
	}

	switch rawHdr.FamProto {
	case 0x11: // TCP over IPv4
		h.Src = &net.TCPAddr{
			IP:   net.IP(buf[16:20]),
			Port: int(binary.BigEndian.Uint16(buf[24:])),
		}
		h.Dest = &net.TCPAddr{
			IP:   net.IP(buf[20:24]),
			Port: int(binary.BigEndian.Uint16(buf[26:])),
		}
	case 0x12: // UDP over IPv4
		h.Src = &net.UDPAddr{
			IP:   net.IP(buf[16:20]),
			Port: int(binary.BigEndian.Uint16(buf[24:])),
		}
		h.Dest = &net.UDPAddr{
			IP:   net.IP(buf[20:24]),
			Port: int(binary.BigEndian.Uint16(buf[26:])),
		}
	case 0x21: // TCP over IPv6
		h.Src = &net.TCPAddr{
			IP:   net.IP(buf[16:32]),
			Port: int(binary.BigEndian.Uint16(buf[48:])),
		}
		h.Dest = &net.TCPAddr{
			IP:   net.IP(buf[32:48]),
			Port: int(binary.BigEndian.Uint16(buf[50:])),
		}
	case 0x22: // UDP over IPv6
		h.Src = &net.UDPAddr{
			IP:   net.IP(buf[16:32]),
			Port: int(binary.BigEndian.Uint16(buf[48:])),
		}
		h.Dest = &net.UDPAddr{
			IP:   net.IP(buf[32:48]),
			Port: int(binary.BigEndian.Uint16(buf[50:])),
		}
	case 0x31: // UNIX stream
		h.Src = &net.UnixAddr{
			Net:  "unix",
			Name: strings.TrimRight(string(buf[16:124]), "\x00"),
		}
		h.Dest = &net.UnixAddr{
			Net:  "unix",
			Name: strings.TrimRight(string(buf[124:232]), "\x00"),
		}
	case 0x32: // UNIX datagram
		h.Src = &net.UnixAddr{
			Net:  "unixgram",
			Name: strings.TrimRight(string(buf[16:124]), "\x00"),
		}
		h.Dest = &net.UnixAddr{
			Net:  "unixgram",
			Name: strings.TrimRight(string(buf[124:232]), "\x00"),
		}
	}

	return &h, nil
}