-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathserver.go
executable file
·174 lines (142 loc) · 3.55 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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Package smtpproxy is based heavily on https://github.com/emersion/go-smtp, with increased transparency of response codes and no sasl dependency.
package smtpproxy
import (
"crypto/tls"
"crypto/x509"
"errors"
"io"
"log"
"net"
"os"
"sync"
"time"
)
var errTCPAndLMTP = errors.New("smtp: cannot start LMTP server listening on a TCP socket")
// Logger interface is used by Server to report unexpected internal errors.
type Logger interface {
Printf(format string, v ...interface{})
Println(v ...interface{})
}
// Server is an SMTP server.
type Server struct {
// TCP or Unix address to listen on.
Addr string
// The server TLS configuration.
TLSConfig *tls.Config
Domain string
Debug io.Writer
ErrorLog Logger
ReadTimeout time.Duration
WriteTimeout time.Duration
// If set, the AUTH command will not be advertised and authentication
// attempts will be rejected. This setting overrides AllowInsecureAuth.
AuthDisabled bool
// The server backend.
Backend Backend
listener net.Listener
caps []string
//auths no longer using sasl library
locker sync.Mutex
conns map[*Conn]struct{}
}
// NewServer creates a new SMTP server, with a Backend interface, supporting many connections
func NewServer(be Backend) *Server {
return &Server{
Backend: be,
ErrorLog: log.New(os.Stderr, "smtp/server ", log.LstdFlags),
caps: []string{"PIPELINING", "8BITMIME", "ENHANCEDSTATUSCODES"},
conns: make(map[*Conn]struct{}),
}
}
// ServeTLS configures the server with TLS credentials from supplied cert/key
// and sets the EHLO server name
func (s *Server) ServeTLS(cert []byte, privkey []byte) error {
cer, err := tls.X509KeyPair(cert, privkey)
if err != nil {
return err
}
config := &tls.Config{Certificates: []tls.Certificate{cer}}
s.TLSConfig = config
leafCer, err := x509.ParseCertificate(cer.Certificate[0])
if err != nil {
return err
}
s.Domain = leafCer.Subject.CommonName
return nil
}
// Serve accepts incoming connections on the Listener l.
func (s *Server) Serve(l net.Listener) error {
s.listener = l
defer s.Close()
for {
c, err := l.Accept()
if err != nil {
return err
}
go s.handleConn(newConn(c, s))
}
}
// handleConn handles incoming SMTP connections
func (s *Server) handleConn(c *Conn) error {
s.locker.Lock()
s.conns[c] = struct{}{}
s.locker.Unlock()
defer func() {
c.Close()
s.locker.Lock()
delete(s.conns, c)
s.locker.Unlock()
}()
c.greet()
for {
line, err := c.ReadLine()
if err == nil {
cmd, arg, err := ParseCmd(line)
if err != nil {
c.nbrErrors++
c.WriteResponse(501, EnhancedCode{5, 5, 2}, "Bad command")
continue
}
c.handle(cmd, arg)
} else {
if err == io.EOF {
return nil
}
if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
c.WriteResponse(221, EnhancedCode{2, 4, 2}, "Idle timeout, bye bye")
return nil
}
c.WriteResponse(221, EnhancedCode{2, 4, 0}, "Connection error, sorry")
return err
}
}
}
// ListenAndServe listens on the network address s.Addr and then calls Serve
// to handle requests on incoming connections.
//
// If s.Addr is blank and LMTP is disabled, ":smtp" is used.
func (s *Server) ListenAndServe() error {
network := "tcp"
/* if s.LMTP {
network = "unix"
} */
addr := s.Addr
if addr == "" {
addr = ":smtp"
}
l, err := net.Listen(network, addr)
if err != nil {
return err
}
return s.Serve(l)
}
// Close function not needed
func (s *Server) Close() {
s.listener.Close()
s.locker.Lock()
defer s.locker.Unlock()
for conn := range s.conns {
conn.Close()
}
}
// ForEachConn not needed