Browse Source

add stun-server to netmaker

Abhishek Kondur 2 years ago
parent
commit
fb60c528e3
1 changed files with 144 additions and 0 deletions
  1. 144 0
      stun-server/stun-server.go

+ 144 - 0
stun-server/stun-server.go

@@ -0,0 +1,144 @@
+package stunserver
+
+import (
+	"fmt"
+	"log"
+	"net"
+	"strings"
+
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/servercfg"
+	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
+	"gortc.io/stun"
+)
+
+// Server is RFC 5389 basic server implementation.
+//
+// Current implementation is UDP only and not utilizes FINGERPRINT mechanism,
+// nor ALTERNATE-SERVER, nor credentials mechanisms. It does not support
+// backwards compatibility with RFC 3489.
+type Server struct {
+	Addr         string
+	LogAllErrors bool
+	log          Logger
+}
+
+// Logger is used for logging formatted messages.
+type Logger interface {
+	// Printf must have the same semantics as log.Printf.
+	Printf(format string, args ...interface{})
+}
+
+var (
+	defaultLogger     = logrus.New()
+	software          = stun.NewSoftware("netmaker-stun")
+	errNotSTUNMessage = errors.New("not stun message")
+)
+
+func basicProcess(addr net.Addr, b []byte, req, res *stun.Message) error {
+	if !stun.IsMessage(b) {
+		return errNotSTUNMessage
+	}
+	if _, err := req.Write(b); err != nil {
+		return errors.Wrap(err, "failed to read message")
+	}
+	var (
+		ip   net.IP
+		port int
+	)
+	switch a := addr.(type) {
+	case *net.UDPAddr:
+		ip = a.IP
+		port = a.Port
+	default:
+		panic(fmt.Sprintf("unknown addr: %v", addr))
+	}
+	return res.Build(req,
+		stun.BindingSuccess,
+		software,
+		&stun.XORMappedAddress{
+			IP:   ip,
+			Port: port,
+		},
+		stun.Fingerprint,
+	)
+}
+
+func (s *Server) serveConn(c net.PacketConn, res, req *stun.Message) error {
+	if c == nil {
+		return nil
+	}
+	buf := make([]byte, 1024)
+	n, addr, err := c.ReadFrom(buf)
+	if err != nil {
+		s.log.Printf("ReadFrom: %v", err)
+		return nil
+	}
+	log.Printf("read %d bytes from %s\n", n, addr)
+	if _, err = req.Write(buf[:n]); err != nil {
+		s.log.Printf("Write: %v", err)
+		return err
+	}
+	if err = basicProcess(addr, buf[:n], req, res); err != nil {
+		if err == errNotSTUNMessage {
+			return nil
+		}
+		s.log.Printf("basicProcess: %v", err)
+		return nil
+	}
+	_, err = c.WriteTo(res.Raw, addr)
+	if err != nil {
+		s.log.Printf("WriteTo: %v", err)
+	}
+	return err
+}
+
+// Serve reads packets from connections and responds to BINDING requests.
+func (s *Server) Serve(c net.PacketConn) error {
+	var (
+		res = new(stun.Message)
+		req = new(stun.Message)
+	)
+	for {
+		if err := s.serveConn(c, res, req); err != nil {
+			s.log.Printf("serve: %v", err)
+			return err
+		}
+		res.Reset()
+		req.Reset()
+	}
+}
+
+// ListenUDPAndServe listens on laddr and process incoming packets.
+func ListenUDPAndServe(serverNet, laddr string) error {
+	c, err := net.ListenPacket(serverNet, laddr)
+	if err != nil {
+		return err
+	}
+	s := &Server{
+		log: defaultLogger,
+	}
+	return s.Serve(c)
+}
+
+func normalize(address string) string {
+	if len(address) == 0 {
+		address = "0.0.0.0"
+	}
+	if !strings.Contains(address, ":") {
+		address = fmt.Sprintf("%s:%d", address, stun.DefaultPort)
+	}
+	return address
+}
+
+func Start() {
+
+	normalized := normalize(fmt.Sprintf("0.0.0.0:%s", servercfg.GetStunPort()))
+	logger.Log(0, "netmaker-stun listening on", normalized, "via udp")
+	err := ListenUDPAndServe("udp", normalized)
+	if err != nil {
+		logger.Log(0, "failed to start stun server: ", err.Error())
+	}
+
+}