From 1aeeeb7e45e346cf8942a0364b85c1c7194f09e9 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 23 Feb 2021 14:46:17 -0800 Subject: [PATCH] net/netcheck: do PCP discovery without side effects Manually cherry-picked subset of c64bd587aee9709bc6a6203de58361e2bafc2dbf back into the 1.4 branch. Signed-off-by: Brad Fitzpatrick --- net/netcheck/netcheck.go | 50 ++++++++++------------------------------ 1 file changed, 12 insertions(+), 38 deletions(-) diff --git a/net/netcheck/netcheck.go b/net/netcheck/netcheck.go index 1b0194830..04fda240d 100644 --- a/net/netcheck/netcheck.go +++ b/net/netcheck/netcheck.go @@ -8,9 +8,7 @@ package netcheck import ( "bufio" "context" - "crypto/rand" "crypto/tls" - "encoding/binary" "errors" "fmt" "io" @@ -701,16 +699,14 @@ func (rs *reportState) probePortMapServices() { return } defer uc.Close() - tempPort := uc.LocalAddr().(*net.UDPAddr).Port uc.SetReadDeadline(time.Now().Add(portMapServiceProbeTimeout)) // Send request packets for all three protocols. uc.WriteTo(uPnPPacket, port1900) uc.WriteTo(pmpPacket, port5351) - uc.WriteTo(pcpPacket(myIP, tempPort, false), port5351) + uc.WriteTo(pcpAnnounceRequest(myIP), port5351) res := make([]byte, 1500) - sentPCPDelete := false for { n, addr, err := uc.ReadFrom(res) if err != nil { @@ -725,17 +721,8 @@ func (rs *reportState) probePortMapServices() { if n == 12 && res[0] == 0x00 { // right length and version 0 rs.setOptBool(&rs.report.PMP, true) } - if n == 60 && res[0] == 0x02 { // right length and version 2 + if n == 24 && res[0] == 0x02 { // right length and version 2 rs.setOptBool(&rs.report.PCP, true) - - if !sentPCPDelete { - sentPCPDelete = true - // And now delete the mapping. - // (PCP is the only protocol of the three that requires - // we cause a side effect to detect whether it's present, - // so we need to redo that side effect now.) - uc.WriteTo(pcpPacket(myIP, tempPort, true), port5351) - } } } } @@ -751,32 +738,19 @@ var uPnPPacket = []byte("M-SEARCH * HTTP/1.1\r\n" + var v4unspec, _ = netaddr.ParseIP("0.0.0.0") -// pcpPacket generates a PCP packet with a MAP opcode. -func pcpPacket(myIP netaddr.IP, mapToLocalPort int, delete bool) []byte { - const udpProtoNumber = 17 - lifetimeSeconds := uint32(1) - if delete { - lifetimeSeconds = 0 - } - const opMap = 1 - - // 24 byte header + 36 byte map opcode - pkt := make([]byte, (32+32+128)/8+(96+8+24+16+16+128)/8) +const ( + pcpVersion = 2 + pcpOpAnnounce = 0 +) - // The header (https://tools.ietf.org/html/rfc6887#section-7.1) - pkt[0] = 2 // version - pkt[1] = opMap - binary.BigEndian.PutUint32(pkt[4:8], lifetimeSeconds) +// pcpAnnounceRequest generates a PCP packet with an ANNOUNCE opcode. +func pcpAnnounceRequest(myIP netaddr.IP) []byte { + // See https://tools.ietf.org/html/rfc6887#section-7.1 + pkt := make([]byte, 24) + pkt[0] = pcpVersion // version + pkt[1] = pcpOpAnnounce myIP16 := myIP.As16() copy(pkt[8:], myIP16[:]) - - // The map opcode body (https://tools.ietf.org/html/rfc6887#section-11.1) - mapOp := pkt[24:] - rand.Read(mapOp[:12]) // 96 bit mappping nonce - mapOp[12] = udpProtoNumber - binary.BigEndian.PutUint16(mapOp[16:], uint16(mapToLocalPort)) - v4unspec16 := v4unspec.As16() - copy(mapOp[20:], v4unspec16[:]) return pkt }