forked from mushorg/glutton
-
Notifications
You must be signed in to change notification settings - Fork 0
/
smtp.go
129 lines (122 loc) · 2.79 KB
/
smtp.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
package glutton
import (
"bufio"
"math/rand"
"net"
"regexp"
"strings"
"time"
)
// maximum lines that can be read after the "DATA" command
const maxDataRead = 500
// Client is a connection container
type Client struct {
conn net.Conn
bufin *bufio.Reader
bufout *bufio.Writer
}
func (c *Client) w(s string) {
c.bufout.WriteString(s + "\r\n")
c.bufout.Flush()
}
func (c *Client) r(g *Glutton) (string, error) {
reply, err := c.bufin.ReadString('\n')
if err != nil {
g.logger.Errorf("[smpt ] %v", err)
return "", err
}
return reply, nil
}
func rwait() {
// makes the process sleep for random time
rand.Seed(time.Now().Unix())
// between 0.5 - 1.5 seconds
rtime := rand.Intn(1500) + 500
duration := time.Duration(rtime) * time.Millisecond
time.Sleep(duration)
}
func validateMail(query string) bool {
email := regexp.MustCompile("^MAIL FROM:<.+@.+>$") // naive regex
if email.MatchString(query) {
return true
}
return false
}
func validateRCPT(query string) bool {
rcpt := regexp.MustCompile("^RCPT TO:<.+@.+>$")
if rcpt.MatchString(query) {
return true
}
return false
}
func rwait() {
// makes the process sleep for random time
rand.Seed(time.Now().Unix())
// between 0.5 - 1.5 seconds
rtime := rand.Intn(1500) + 500
duration := time.Duration(rtime) * time.Millisecond
time.Sleep(duration)
}
func validateMail(query string) bool {
email := regexp.MustCompile("^MAIL FROM:<.+@.+>$") // naive regex
if email.MatchString(query) {
return true
}
return false
}
func validateRCPT(query string) bool {
rcpt := regexp.MustCompile("^RCPT TO:<.+@.+>$")
if rcpt.MatchString(query) {
return true
}
return false
}
// HandleSMTP takes a net.Conn and does basic SMTP communication
func (g *Glutton) HandleSMTP(conn net.Conn) {
defer conn.Close()
client := &Client{
conn: conn,
bufin: bufio.NewReader(conn),
bufout: bufio.NewWriter(conn),
}
rwait()
client.w("220 Welcome!")
for {
msg, err := client.r(g)
if err != nil {
break
}
query := strings.Trim(msg, "\r\n")
g.logger.Infof("[smtp ] Payload : %q", query)
if strings.HasPrefix(query, "HELO ") {
rwait()
client.w("250 Hello! Pleased to meet you.")
} else if validateMail(query) {
rwait()
client.w("250 OK")
} else if validateRCPT(query) {
rwait()
client.w("250 OK")
} else if strings.Compare(query, "DATA") == 0 {
client.w("354 End data with <CRLF>.<CRLF>")
for readctr := maxDataRead; readctr >= 0; readctr-- {
data, err := client.r(g)
if err != nil {
break
}
g.logger.Infof("[smtp ] Data : %q", data)
// exit condition
if strings.Compare(data, ".\r\n") == 0 {
break
}
}
rwait()
client.w("250 OK")
} else if strings.Compare(query, "QUIT") == 0 {
client.w("Bye")
break
} else {
client.w("Recheck the command you entered.")
}
}
}