forked from Deuxfleurs/bottin
145 lines
3.4 KiB
Go
145 lines
3.4 KiB
Go
package ldapserver
|
|
|
|
import (
|
|
"bufio"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Server is an LDAP server.
|
|
type Server struct {
|
|
Listener net.Listener
|
|
ReadTimeout time.Duration // optional read timeout
|
|
WriteTimeout time.Duration // optional write timeout
|
|
wg sync.WaitGroup // group of goroutines (1 by client)
|
|
chDone chan bool // Channel Done, value => shutdown
|
|
|
|
// OnNewConnection, if non-nil, is called on new connections.
|
|
// If it returns non-nil, the connection is closed.
|
|
OnNewConnection func(c net.Conn) error
|
|
|
|
// Handler handles ldap message received from client
|
|
// it SHOULD "implement" RequestHandler interface
|
|
Handler Handler
|
|
NewUserState func() UserState
|
|
}
|
|
|
|
//NewServer return a LDAP Server
|
|
func NewServer() *Server {
|
|
return &Server{
|
|
chDone: make(chan bool),
|
|
}
|
|
}
|
|
|
|
// Handle registers the handler for the server.
|
|
// If a handler already exists for pattern, Handle panics
|
|
func (s *Server) Handle(h Handler) {
|
|
if s.Handler != nil {
|
|
Logger.Fatal("LDAP: multiple Handler registrations")
|
|
}
|
|
s.Handler = h
|
|
}
|
|
|
|
// ListenAndServe listens on the TCP network address s.Addr and then
|
|
// calls Serve to handle requests on incoming connections. If
|
|
// s.Addr is blank, ":389" is used.
|
|
func (s *Server) ListenAndServe(addr string, options ...func(*Server)) error {
|
|
|
|
if addr == "" {
|
|
addr = ":389"
|
|
}
|
|
|
|
var e error
|
|
s.Listener, e = net.Listen("tcp", addr)
|
|
if e != nil {
|
|
return e
|
|
}
|
|
Logger.Printf("Listening on %s\n", addr)
|
|
|
|
for _, option := range options {
|
|
option(s)
|
|
}
|
|
|
|
return s.serve()
|
|
}
|
|
|
|
// Handle requests messages on the ln listener
|
|
func (s *Server) serve() error {
|
|
defer s.Listener.Close()
|
|
|
|
if s.Handler == nil {
|
|
Logger.Panicln("No LDAP Request Handler defined")
|
|
}
|
|
|
|
i := 0
|
|
|
|
for {
|
|
select {
|
|
case <-s.chDone:
|
|
Logger.Print("Stopping server")
|
|
s.Listener.Close()
|
|
return nil
|
|
default:
|
|
}
|
|
|
|
rw, err := s.Listener.Accept()
|
|
|
|
if s.ReadTimeout != 0 {
|
|
rw.SetReadDeadline(time.Now().Add(s.ReadTimeout))
|
|
}
|
|
if s.WriteTimeout != 0 {
|
|
rw.SetWriteDeadline(time.Now().Add(s.WriteTimeout))
|
|
}
|
|
if nil != err {
|
|
if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
|
|
continue
|
|
}
|
|
Logger.Println(err)
|
|
}
|
|
|
|
cli, err := s.newClient(rw)
|
|
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
i = i + 1
|
|
cli.Numero = i
|
|
Logger.Printf("Connection client [%d] from %s accepted", cli.Numero, cli.rwc.RemoteAddr().String())
|
|
s.wg.Add(1)
|
|
go cli.serve()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Return a new session with the connection
|
|
// client has a writer and reader buffer
|
|
func (s *Server) newClient(rwc net.Conn) (c *client, err error) {
|
|
c = &client{
|
|
srv: s,
|
|
rwc: rwc,
|
|
br: bufio.NewReader(rwc),
|
|
bw: bufio.NewWriter(rwc),
|
|
userState: s.NewUserState(),
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
// Termination of the LDAP session is initiated by the server sending a
|
|
// Notice of Disconnection. In this case, each
|
|
// protocol peer gracefully terminates the LDAP session by ceasing
|
|
// exchanges at the LDAP message layer, tearing down any SASL layer,
|
|
// tearing down any TLS layer, and closing the transport connection.
|
|
// A protocol peer may determine that the continuation of any
|
|
// communication would be pernicious, and in this case, it may abruptly
|
|
// terminate the session by ceasing communication and closing the
|
|
// transport connection.
|
|
// In either case, when the LDAP session is terminated.
|
|
func (s *Server) Stop() {
|
|
close(s.chDone)
|
|
Logger.Print("gracefully closing client connections...")
|
|
s.wg.Wait()
|
|
Logger.Print("all clients connection closed")
|
|
}
|