forked from alash3al/go-smtpsrv
/
request.go
128 lines (100 loc) · 2.66 KB
/
request.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
package smtpsrv
import (
"crypto/tls"
"net"
"net/mail"
"net/textproto"
"strings"
"github.com/zaccone/spf"
)
// Request is the incoming connection meta-data
type Request struct {
// the currently running server
Server *Server
// the underlying socket for currently connected client
Conn net.Conn
// an instance of go stdlib for handling the above Conn as a text-porotocol
TextProto *textproto.Conn
// TLS related info
TLSState *tls.ConnectionState
// a shortcut for Conn.RemoteAddr()
RemoteAddr string
// contains the hostname of the currently connected client during the EHLO/HELO command
HelloHost string
// whether EHLO/HELO called or not
HelloRecieved bool
// the login username used for login, empty means that this is an anonymous attempt
AuthUser string
// the user that sends the mail
From string
// the rctps!
To []string
// the body of the mail "DATA command" but parsed
Message *mail.Message
// whether the client called QUIT or not
QuitSent bool
// whether to check the from domain
DisableFromChecks bool
// the spf checking result
SPFResult spf.Result
// whether the FROM mail is mailable or not
Mailable bool
// the currently processing line
Line []string
}
// NewRequest creates a new instance of the Request struct
func NewRequest(conn net.Conn, srv *Server) (req *Request, err error) {
req = new(Request)
req.Reset()
req.RemoteAddr = conn.RemoteAddr().String()
req.Server = srv
req.Conn = conn
req.TextProto = textproto.NewConn(conn)
req.TLSState = nil
req.Line = []string{}
req.DisableFromChecks = srv.DisableFromChecks
return req, nil
}
// Serve start accepting incoming connections
func (req *Request) Serve() {
defer func() {
req.TextProto.Close()
req.Conn.Close()
}()
err := req.TextProto.PrintfLine("%d %s %s", 220, req.Server.Name, "ESMTP")
if err != nil {
return
}
for !req.QuitSent && err == nil {
err = req.Process()
if err != nil {
return
}
}
}
// Reset resets to the defaults
func (req *Request) Reset() {
req.From = ""
req.To = make([]string, 0)
req.Message = nil
}
// Process start parsing and processing the current command-line
func (req *Request) Process() error {
s, err := req.TextProto.ReadLine()
if err != nil {
return err
}
req.Line = strings.Split(s, " ")
if len(req.Line) <= 0 {
return req.TextProto.PrintfLine("%d %s (%s)", 500, "Command not recognized", s)
}
if req.Server.Processors == nil {
req.Server.Processors = DefaultProcessors
}
req.Line[0] = strings.ToUpper(req.Line[0])
processor, found := req.Server.Processors[req.Line[0]]
if !found {
return req.TextProto.PrintfLine("%d %s (%s)", 500, "Command not recognized", req.Line[0])
}
return processor(req)
}