// receives packets from the receive loop and dispatches them // to their corresponding connections func processLoop(c net.PacketConn, l *listener, rld *receiveLoopData, pld *processLoopData) { var ( quit bool wg = new(sync.WaitGroup) tq = newTimerQueue() connections = struct { sync.RWMutex M map[uint32]*connection }{ M: make(map[uint32]*connection), } ) // keep a refcount to `c` for ourselves wg.Add(1) defer wg.Done() // wait for all pending connections, then close the socket go func() { wg.Wait() c.Close() tq.Close() }() // generate a new key for cookies cookieKey := make([]byte, sha1.Size) if _, err := crand.Read(cookieKey); err != nil { l.L.Lock() l.err = fmt.Errorf("Failed to generate cookie secret: %v", err) l.L.Unlock() l.Broadcast() return } sig := hmac.New(sha1.New, cookieKey) // generate addler32(localaddr) var localAddrSum [4]byte binary.LittleEndian.PutUint32(localAddrSum[:], adler32.Checksum([]byte(c.LocalAddr().String()))) shouldWait := func() bool { waitOnConnections := pld.rejectNewConnections && pld.remainingConnections == 0 return pld.err == nil && len(pld.Q) == 0 && !waitOnConnections } var packets []processLoopPacket for !quit { // wait for a packet to become available pld.L.Lock() for shouldWait() { pld.Wait() } rejectNewConnections := pld.rejectNewConnections pldErr := pld.err quit = pldErr != nil packets, pld.Q = pld.Q, packets[:0] remainingConnections := pld.remainingConnections pld.L.Unlock() if rejectNewConnections && remainingConnections == 0 { quit = true } // process packets for ii := range packets { buffer, addr := packets[ii].D, packets[ii].A switch PacketType(buffer[0]) { case PacketInit: // are we accepting new connections? if rejectNewConnections { continue } // verify length const MinInitPacketLength = 9 if len(buffer) < MinInitPacketLength { continue } // verify protocol magic if !bytes.Equal(buffer[1:5], protocolMagic) { continue } // verify version if buffer[5] != version1 { sendAbort(c, addr, buffer[6:9]) continue } var outgoing [32]byte now := time.Now() outgoing[0] = byte(PacketCookie) if _, err := crand.Read(outgoing[1:4]); err != nil { return } copy(outgoing[4:7], buffer[6:9]) outgoing[7] = version1 binary.LittleEndian.PutUint32(outgoing[8:12], uint32(now.Add(5*time.Second).Unix()+1)) sig.Reset() sig.Write(outgoing[1:12]) sig.Write(localAddrSum[:]) sig.Sum(outgoing[1:12]) c.WriteTo(outgoing[:], addr) case PacketCookieEcho: // are we accepting new connections if rejectNewConnections { continue } // verify length const CookieEchoPacketLength = 32 if len(buffer) != CookieEchoPacketLength { continue } // verify signature sig.Reset() sig.Write(buffer[1:12]) sig.Write(localAddrSum[:]) if !hmac.Equal(sig.Sum(nil), buffer[12:]) { continue } // verify version if buffer[7] != version1 { sendAbort(c, addr, buffer[1:4]) continue } // vetify timeout now := time.Now() if time.Unix(int64(binary.LittleEndian.Uint32(buffer[8:12])), 0).Before(now) { sendAbort(c, addr, buffer[1:4]) continue } // decode connection tag to uin32 tagId := binary.LittleEndian.Uint32(buffer[3:7]) >> 8 connections.RLock() _, ok := connections.M[tagId] connections.RUnlock() // create new connection if !ok { // create connection wg.Add(1) connections.Lock() conn := newConnection(c, addr, tq, buffer[1:4], func() { connections.Lock() delete(connections.M, tagId) connections.Unlock() wg.Done() pld.L.Lock() pld.remainingConnections-- remain := pld.remainingConnections pld.L.Unlock() if remain == 0 { pld.Signal() } }) connections.M[tagId] = conn connections.Unlock() pld.L.Lock() pld.remainingConnections++ pld.L.Unlock() l.L.Lock() l.pending = append(l.pending, conn) l.L.Unlock() l.Signal() } // send COOKIE-ACK var outgoing [4]byte outgoing[0] = byte(PacketCookieAck) copy(outgoing[1:4], buffer[1:4]) c.WriteTo(outgoing[:], addr) default: // parse connection tag tagId := binary.LittleEndian.Uint32(buffer[0:4]) >> 8 connections.RLock() conn, ok := connections.M[tagId] connections.RUnlock() // discard packets addressed to unknown connections if !ok { sendAbort(c, addr, buffer[1:4]) continue } // copy the packet data so we can reuse the buffer p := NewPacket(len(buffer)) copy(p.D, buffer) // queue the packet onto the connection conn.L.Lock() conn.Incoming = append(conn.Incoming, p) conn.L.Unlock() conn.Signal() } } // return the packet to the receive loop rld.L.Lock() for ii := range packets { rld.buffers = append(rld.buffers, packets[ii].D) } rld.L.Unlock() rld.Signal() // if we were signaled to quit, terminate existing connections if pldErr != nil { connections.Lock() m := connections.M connections.M = make(map[uint32]*connection) connections.Unlock() for _, c := range m { c.closeWithError(pldErr) } } } }