func (s *Server) handleChannelBindRequest(conn net.PacketConn, srcAddr net.Addr, m *stun.Message) error {
	s.log.Debugf("received ChannelBindRequest from %s", srcAddr.String())
	dstAddr := conn.LocalAddr()
	errorSend := func(err error, attrs ...stun.Setter) error {
		sendErr := s.sender(conn, srcAddr, s.makeAttrs(m.TransactionID, stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse), attrs...)...)
		if sendErr != nil {
			err = errors.Errorf(strings.Join([]string{sendErr.Error(), err.Error()}, "\n"))
		}
		return err
	}
	a := s.manager.GetAllocation(&allocation.FiveTuple{
		SrcAddr:  srcAddr,
		DstAddr:  dstAddr,
		Protocol: allocation.UDP,
	})
	if a == nil {
		return errors.Errorf("No allocation found for %v:%v", srcAddr, dstAddr)
	}
	messageIntegrity, _, err := authenticateRequest(func(class stun.MessageClass, method stun.Method, transactionID [stun.TransactionIDSize]byte, attrs ...stun.Setter) error {
		return s.sender(conn, srcAddr, s.makeAttrs(m.TransactionID, stun.NewType(method, class))...)
	}, m, stun.MethodChannelBind, s.realm, s.authHandler, srcAddr)
	if err != nil {
		return errorSend(err, stun.CodeBadRequest)
	}
	var channel proto.ChannelNumber
	if err = channel.GetFrom(m); err != nil {
		return errorSend(err, stun.CodeBadRequest)
	}
	peerAddr := proto.PeerAddress{}
	if err = peerAddr.GetFrom(m); err != nil {
		return errorSend(err, stun.CodeBadRequest)
	}
	s.log.Debugf("binding channel %d to %s",
		channel,
		fmt.Sprintf("%s:%d", peerAddr.IP.String(), peerAddr.Port))
	err = a.AddChannelBind(allocation.NewChannelBind(
		channel,
		&net.UDPAddr{IP: peerAddr.IP, Port: peerAddr.Port},
		s.log,
	), s.channelBindTimeout)
	if err != nil {
		return errorSend(err, stun.CodeBadRequest)
	}
	return s.sender(conn, srcAddr, s.makeAttrs(m.TransactionID, stun.NewType(stun.MethodChannelBind, stun.ClassSuccessResponse), messageIntegrity)...)
}
caller must hold the mutex
package turn
import (
	"crypto/md5"  
	"fmt"
	"net"
	"strings"
	"time"
	"github.com/pion/stun"
	"github.com/pion/turn/internal/allocation"
	"github.com/pion/turn/internal/ipnet"
	"github.com/pion/turn/internal/proto"
	"github.com/pkg/errors"
)
const (
	maximumLifetime = time.Hour  
)
type curriedSend func(class stun.MessageClass, method stun.Method, transactionID [stun.TransactionIDSize]byte, attrs ...stun.Setter) error
func authenticateRequest(curriedSend curriedSend, m *stun.Message, callingMethod stun.Method, realm string, authHandler AuthHandler, srcAddr net.Addr) (stun.MessageIntegrity, string, error) {
	handleErr := func(err error) (stun.MessageIntegrity, string, error) {
		if sendErr := curriedSend(stun.ClassErrorResponse, callingMethod, m.TransactionID,
			&stun.ErrorCodeAttribute{Code: stun.CodeBadRequest},
		); sendErr != nil {
			err = errors.Errorf(strings.Join([]string{sendErr.Error(), err.Error()}, "\n"))
		}
		return stun.MessageIntegrity{}, "", err
	}
	if !m.Contains(stun.AttrMessageIntegrity) {
		nonce, err := buildNonce()
		if err != nil {
			return stun.MessageIntegrity{}, "", err
		}
		return nil, "", curriedSend(stun.ClassErrorResponse, callingMethod, m.TransactionID,
			&stun.ErrorCodeAttribute{Code: stun.CodeUnauthorized},
			stun.NewNonce(nonce),
			stun.NewRealm(realm),
		)
	}
	var ourKey [16]byte
	nonceAttr := &stun.Nonce{}
	usernameAttr := &stun.Username{}
	realmAttr := &stun.Realm{}
	if err := realmAttr.GetFrom(m); err != nil {
		return handleErr(err)
	}
	if err := nonceAttr.GetFrom(m); err != nil {
		return handleErr(err)
	}
	if err := usernameAttr.GetFrom(m); err != nil {
		return handleErr(err)
	}
	password, ok := authHandler(usernameAttr.String(), srcAddr)
	if !ok {
		return handleErr(errors.Errorf("No user exists for %s", usernameAttr.String()))
	}
	ourKey = md5.Sum([]byte(usernameAttr.String() + ":" + realmAttr.String() + ":" + password))
	if err := assertMessageIntegrity(m, ourKey[:]); err != nil {
		return handleErr(err)
	}
	return stun.NewLongTermIntegrity(usernameAttr.String(), realmAttr.String(), password), usernameAttr.String(), nil
}
func assertDontFragment(curriedSend curriedSend, m *stun.Message, attr stun.Setter) error {
	if m.Contains(stun.AttrDontFragment) {
		err := errors.Errorf("no support for DONT-FRAGMENT")
		if sendErr := curriedSend(stun.ClassErrorResponse, stun.MethodAllocate, m.TransactionID,
			&stun.ErrorCodeAttribute{Code: stun.CodeUnknownAttribute},
			&stun.UnknownAttributes{stun.AttrDontFragment},
			attr,
		); sendErr != nil {
			err = errors.Errorf(strings.Join([]string{sendErr.Error(), err.Error()}, "\n"))
		}
		return err
	}
	return nil
}
func (s *Server) handleAllocateRequest(conn net.PacketConn, srcAddr net.Addr, m *stun.Message) error {
	s.log.Debugf("received AllocateRequest from %s", srcAddr.String())
	dstAddr := conn.LocalAddr()
	curriedSend := func(class stun.MessageClass, method stun.Method, transactionID [stun.TransactionIDSize]byte, attrs ...stun.Setter) error {
		return s.sender(conn, srcAddr, s.makeAttrs(transactionID, stun.NewType(method, class), attrs...)...)
	}
	respondWithError := func(err error, messageIntegrity stun.MessageIntegrity, errorCode stun.ErrorCode) error {
		if sendErr := curriedSend(stun.ClassErrorResponse, stun.MethodAllocate, m.TransactionID,
			&stun.ErrorCodeAttribute{Code: errorCode},
			messageIntegrity,
		); sendErr != nil {
			err = errors.Errorf(strings.Join([]string{sendErr.Error(), err.Error()}, "\n"))
		}
		return err
	}
	messageIntegrity, _, err := authenticateRequest(curriedSend, m, stun.MethodAllocate, s.realm, s.authHandler, srcAddr)
	if err != nil {
		return err
	}
	if messageIntegrity == nil {
		return nil
	}
	fiveTuple := &allocation.FiveTuple{
		SrcAddr:  srcAddr,
		DstAddr:  dstAddr,
		Protocol: allocation.UDP,
	}
	requestedPort := 0
	reservationToken := ""
	if alloc := s.manager.GetAllocation(fiveTuple); alloc != nil {
		return respondWithError(errors.Errorf("Relay already allocated for 5-TUPLE"), messageIntegrity, stun.CodeAllocMismatch)
	}
	var requestedTransport proto.RequestedTransport
	if err = requestedTransport.GetFrom(m); err != nil {
		return respondWithError(err, messageIntegrity, stun.CodeBadRequest)
	}
	if requestedTransport.Protocol != proto.ProtoUDP {
		return respondWithError(err, messageIntegrity, stun.CodeUnsupportedTransProto)
	}
	if err = assertDontFragment(curriedSend, m, messageIntegrity); err != nil {
		return err
	}
	var reservationTokenAttr proto.ReservationToken
	if err = reservationTokenAttr.GetFrom(m); err == nil {
		var evenPort proto.EvenPort
		if err = evenPort.GetFrom(m); err == nil {
			return respondWithError(errors.Errorf("Request must not contain RESERVATION-TOKEN and EVEN-PORT"), messageIntegrity, stun.CodeBadRequest)
		}
		allocationPort, reservationFound := s.reservationManager.GetReservation(string(reservationTokenAttr))
		if !reservationFound {
			return respondWithError(errors.Errorf("No reservation found with token %s", string(reservationTokenAttr)), messageIntegrity, stun.CodeBadRequest)
		}
		requestedPort = allocationPort + 1
	}
	var evenPort proto.EvenPort
	if err = evenPort.GetFrom(m); err == nil {
		randomPort := 0
		randomPort, err = allocation.GetRandomEvenPort()
		if err != nil {
			return respondWithError(err, messageIntegrity, stun.CodeInsufficientCapacity)
		}
		requestedPort = randomPort
		reservationToken = randSeq(8)
	}
	lifetimeDuration := allocationLifeTime(m)
	a, err := s.manager.CreateAllocation(
		fiveTuple,
		conn,
		s.relayIPs[0],  
		requestedPort,
		lifetimeDuration)
	if err != nil {
		return respondWithError(err, messageIntegrity, stun.CodeInsufficientCapacity)
	}
	srcIP, srcPort, err := ipnet.AddrIPPort(srcAddr)
	if err != nil {
		return respondWithError(err, messageIntegrity, stun.CodeBadRequest)
	}
	relayIP, relayPort, err := ipnet.AddrIPPort(a.RelayAddr)
	if err != nil {
		return respondWithError(err, messageIntegrity, stun.CodeBadRequest)
	}
	responseAttrs := []stun.Setter{
		&proto.RelayedAddress{
			IP:   relayIP,
			Port: relayPort,
		},
		&proto.Lifetime{
			Duration: lifetimeDuration,
		},
		&stun.XORMappedAddress{
			IP:   srcIP,
			Port: srcPort,
		},
	}
	if reservationToken != "" {
		s.reservationManager.CreateReservation(reservationToken, relayPort)
		responseAttrs = append(responseAttrs, proto.ReservationToken([]byte(reservationToken)))
	}
	return curriedSend(stun.ClassSuccessResponse, stun.MethodAllocate, m.TransactionID, append(responseAttrs, messageIntegrity)...)
}
func (s *Server) handleRefreshRequest(conn net.PacketConn, srcAddr net.Addr, m *stun.Message) error {
	s.log.Debugf("received RefreshRequest from %s", srcAddr.String())
	dstAddr := conn.LocalAddr()
	curriedSend := func(class stun.MessageClass, method stun.Method, transactionID [stun.TransactionIDSize]byte, attrs ...stun.Setter) error {
		return s.sender(conn, srcAddr, s.makeAttrs(transactionID, stun.NewType(method, class), attrs...)...)
	}
	messageIntegrity, _, err := authenticateRequest(curriedSend, m, stun.MethodCreatePermission, s.realm, s.authHandler, srcAddr)
	if err != nil {
		return err
	}
	a := s.manager.GetAllocation(&allocation.FiveTuple{
		SrcAddr:  srcAddr,
		DstAddr:  dstAddr,
		Protocol: allocation.UDP,
	})
	if a == nil {
		return errors.Errorf("No allocation found for %v:%v", srcAddr, dstAddr)
	}
	lifetimeDuration := allocationLifeTime(m)
	a.Refresh(lifetimeDuration)
	return curriedSend(stun.ClassSuccessResponse, stun.MethodRefresh, m.TransactionID,
		&proto.Lifetime{
			Duration: lifetimeDuration,
		},
		messageIntegrity,
	)
}
func (s *Server) handleCreatePermissionRequest(conn net.PacketConn, srcAddr net.Addr, m *stun.Message) error {
	s.log.Debugf("received CreatePermission from %s", srcAddr.String())
	dstAddr := conn.LocalAddr()
	curriedSend := func(class stun.MessageClass, method stun.Method, transactionID [stun.TransactionIDSize]byte, attrs ...stun.Setter) error {
		return s.sender(conn, srcAddr, s.makeAttrs(transactionID, stun.NewType(method, class), attrs...)...)
	}
	a := s.manager.GetAllocation(&allocation.FiveTuple{
		SrcAddr:  srcAddr,
		DstAddr:  dstAddr,
		Protocol: allocation.UDP,
	})
	if a == nil {
		return errors.Errorf("No allocation found for %v:%v", srcAddr, dstAddr)
	}
	messageIntegrity, _, err := authenticateRequest(curriedSend, m, stun.MethodCreatePermission, s.realm, s.authHandler, srcAddr)
	if err != nil {
		return err
	}
	addCount := 0
	if err := m.ForEach(stun.AttrXORPeerAddress, func(m *stun.Message) error {
		var peerAddress proto.PeerAddress
		if err := peerAddress.GetFrom(m); err != nil {
			return err
		}
		s.log.Debugf("adding permission for %s", fmt.Sprintf("%s:%d",
			peerAddress.IP.String(), peerAddress.Port))
		a.AddPermission(allocation.NewPermission(
			&net.UDPAddr{
				IP:   peerAddress.IP,
				Port: peerAddress.Port,
			},
			s.log,
		))
		addCount++
		return nil
	}); err != nil {
		addCount = 0
	}
	respClass := stun.ClassSuccessResponse
	if addCount == 0 {
		respClass = stun.ClassErrorResponse
	}
	return curriedSend(respClass, stun.MethodCreatePermission, m.TransactionID,
		messageIntegrity)
}
func (s *Server) handleSendIndication(conn net.PacketConn, srcAddr net.Addr, m *stun.Message) error {
	s.log.Debugf("received SendIndication from %s", srcAddr.String())
	dstAddr := conn.LocalAddr()
	a := s.manager.GetAllocation(&allocation.FiveTuple{
		SrcAddr:  srcAddr,
		DstAddr:  dstAddr,
		Protocol: allocation.UDP,
	})
	if a == nil {
		return errors.Errorf("No allocation found for %v:%v", srcAddr, dstAddr)
	}
	dataAttr := proto.Data{}
	if err := dataAttr.GetFrom(m); err != nil {
		return err
	}
	peerAddress := proto.PeerAddress{}
	if err := peerAddress.GetFrom(m); err != nil {
		return err
	}
	msgDst := &net.UDPAddr{IP: peerAddress.IP, Port: peerAddress.Port}
	if perm := a.GetPermission(msgDst); perm == nil {
		return errors.Errorf("Unable to handle send-indication, no permission added: %v", msgDst)
	}
	l, err := a.RelaySocket.WriteTo(dataAttr, msgDst)
	if l != len(dataAttr) {
		return errors.Errorf("packet write smaller than packet %d != %d (expected) err: %v", l, len(dataAttr), err)
	}
	return err
}