package signserver import ( "fmt" "io" "net" "sync" "erupe-ce/config" "erupe-ce/network" "github.com/jmoiron/sqlx" "go.uber.org/zap" ) // Config struct allows configuring the server. type Config struct { Logger *zap.Logger DB *sqlx.DB ErupeConfig *config.Config } // Server is a MHF sign server. type Server struct { sync.Mutex logger *zap.Logger erupeConfig *config.Config sid int sessions map[int]*Session db *sqlx.DB listener net.Listener isShuttingDown bool } // NewServer creates a new Server type. func NewServer(config *Config) *Server { s := &Server{ logger: config.Logger, erupeConfig: config.ErupeConfig, sid: 0, sessions: make(map[int]*Session), db: config.DB, } return s } // Start starts the server in a new goroutine. func (s *Server) Start() error { l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.erupeConfig.Sign.Port)) if err != nil { return err } s.listener = l go s.acceptClients() return nil } // Shutdown exits the server gracefully. func (s *Server) Shutdown() { s.logger.Debug("Shutting down") s.Lock() s.isShuttingDown = true s.Unlock() // This will cause the acceptor goroutine to error and exit gracefully. s.listener.Close() } func (s *Server) acceptClients() { for { conn, err := s.listener.Accept() if err != nil { // Check if we are shutting down and exit gracefully if so. s.Lock() shutdown := s.isShuttingDown s.Unlock() if shutdown { break } else { panic(err) } } go s.handleConnection(s.sid, conn) s.sid++ } } func (s *Server) handleConnection(sid int, conn net.Conn) { s.logger.Info("Got connection to sign server", zap.String("remoteaddr", conn.RemoteAddr().String())) // Client initalizes the connection with a one-time buffer of 8 NULL bytes. nullInit := make([]byte, 8) _, err := io.ReadFull(conn, nullInit) if err != nil { fmt.Println(err) conn.Close() return } // Create a new session. session := &Session{ logger: s.logger, server: s, rawConn: &conn, cryptConn: network.NewCryptConn(conn), } // Add the session to the server's sessions map. s.Lock() s.sessions[sid] = session s.Unlock() // Do the session's work. session.work() }