forked from Videxio/ldapserver
-
Notifications
You must be signed in to change notification settings - Fork 1
/
server.go
148 lines (125 loc) · 3.63 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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
MaxRequestSize int // optional max request size (analogous to the maxBERSize of the Sun Java System Directory Server)
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
}
//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 {
panic("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.Printf("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),
maxRequestSize: s.MaxRequestSize,
}
if c.maxRequestSize <= 0 {
c.maxRequestSize = 5242880 // Default 5MB
}
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.Printf("gracefully closing client connections...")
s.wg.Wait()
Logger.Printf("all clients connection closed")
}