package server import ( "crypto/tls" "errors" "log" "net" "sync" "go.rctt.net/solec/core" ) type Server struct { cfg Config users map[string]User // TODO: Use full address instead of just name servers map[string]RemoteServer usersMu sync.RWMutex serversMu sync.RWMutex Storage Storage } type Config struct { ListenAddr string Name string Tls bool CertPem []byte KeyPem []byte } func NewServer(cfg Config, storage Storage) *Server { return &Server{ cfg: cfg, Storage: storage, users: make(map[string]User), servers: make(map[string]RemoteServer), } } func (s *Server) Start() error { if s.cfg.Tls { return s.listenTls() } return s.listenPlain() } func (s *Server) listenPlain() error { ln, err := net.Listen("tcp", s.cfg.ListenAddr) if err != nil { return err } s.listen(ln) return nil } func (s *Server) listenTls() error { cert, err := tls.X509KeyPair(s.cfg.CertPem, s.cfg.KeyPem) if err != nil { return err } cfg := &tls.Config{Certificates: []tls.Certificate{cert}} ln, err := tls.Listen("tcp", s.cfg.ListenAddr, cfg) if err != nil { return err } s.listen(ln) return nil } func (s *Server) listen(ln net.Listener) { for { conn, err := ln.Accept() if err != nil { log.Println("cannot listen:", err) continue } log.Println("client connected:", conn.RemoteAddr()) go s.handleConn(conn) } } func (s *Server) handleConn(conn net.Conn) { defer conn.Close() cType, err := s.performHandshake(conn) if err != nil { log.Println("handshake error:", err) return } switch cType { case core.ConnTypeUnknown: log.Println("invalid connection type") case core.ConnTypeUser: s.handleUserConn(conn) case core.ConnTypeServer: s.handleServerConn(conn) } } func (s *Server) performHandshake(conn net.Conn) (core.ConnType, error) { serverHs := core.Handshake{0, 2, core.ConnTypeServer} if err := core.Send(conn, serverHs); err != nil { return core.ConnTypeUnknown, err } clientPayload, err := core.Decode(conn) if err != nil { return core.ConnTypeUnknown, err } clientHs, ok := clientPayload.(core.Handshake) if !ok { return core.ConnTypeUnknown, core.ErrUnexpectedPayloadType } if serverHs.Major != clientHs.Major { return clientHs.ConnType, errors.New("server and client are using different protocol version") } return clientHs.ConnType, nil }