client.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package guerrilla
  2. import (
  3. "bufio"
  4. "net"
  5. "strings"
  6. "sync"
  7. "time"
  8. )
  9. // ClientState indicates which part of the SMTP transaction a given client is in.
  10. type ClientState int
  11. const (
  12. // The client has connected, and is awaiting our first response
  13. ClientGreeting = iota
  14. // We have responded to the client's connection and are awaiting a command
  15. ClientCmd
  16. // We have received the sender and recipient information
  17. ClientData
  18. // We have agreed with the client to secure the connection over TLS
  19. ClientStartTLS
  20. // Server will shutdown, client to shutdown on next command turn
  21. ClientShutdown
  22. )
  23. type client struct {
  24. *Envelope
  25. ID uint64
  26. ConnectedAt time.Time
  27. KilledAt time.Time
  28. // Number of errors encountered during session with this client
  29. errors int
  30. state ClientState
  31. messagesSent int
  32. // Response to be written to the client
  33. response string
  34. conn net.Conn
  35. bufin *smtpBufferedReader
  36. bufout *bufio.Writer
  37. timeoutMu sync.Mutex
  38. }
  39. // Email represents a single SMTP message.
  40. type Envelope struct {
  41. // Remote IP address
  42. RemoteAddress string
  43. // Message sent in EHLO command
  44. Helo string
  45. // Sender
  46. MailFrom *EmailAddress
  47. // Recipients
  48. RcptTo []EmailAddress
  49. Data string
  50. Subject string
  51. TLS bool
  52. }
  53. func NewClient(conn net.Conn, clientID uint64) *client {
  54. return &client{
  55. conn: conn,
  56. Envelope: &Envelope{
  57. RemoteAddress: conn.RemoteAddr().String(),
  58. },
  59. ConnectedAt: time.Now(),
  60. bufin: newSMTPBufferedReader(conn),
  61. bufout: bufio.NewWriter(conn),
  62. ID: clientID,
  63. }
  64. }
  65. func (c *client) responseAdd(r string) {
  66. c.response = c.response + r + "\r\n"
  67. }
  68. func (c *client) resetTransaction() {
  69. c.MailFrom = &EmailAddress{}
  70. c.RcptTo = []EmailAddress{}
  71. c.Data = ""
  72. c.Subject = ""
  73. }
  74. func (c *client) isInTransaction() bool {
  75. isMailFromEmpty := (c.MailFrom == nil || *c.MailFrom == (EmailAddress{}))
  76. if isMailFromEmpty {
  77. return false
  78. }
  79. return true
  80. }
  81. func (c *client) kill() {
  82. c.KilledAt = time.Now()
  83. }
  84. func (c *client) isAlive() bool {
  85. return c.KilledAt.IsZero()
  86. }
  87. func (c *client) scanSubject(reply string) {
  88. if c.Subject == "" && (len(reply) > 8) {
  89. test := strings.ToUpper(reply[0:9])
  90. if i := strings.Index(test, "SUBJECT: "); i == 0 {
  91. // first line with \r\n
  92. c.Subject = reply[9:]
  93. }
  94. } else if strings.HasSuffix(c.Subject, "\r\n") {
  95. // chop off the \r\n
  96. c.Subject = c.Subject[0 : len(c.Subject)-2]
  97. if (strings.HasPrefix(reply, " ")) || (strings.HasPrefix(reply, "\t")) {
  98. // subject is multi-line
  99. c.Subject = c.Subject + reply[1:]
  100. }
  101. }
  102. }
  103. func (c *client) setTimeout(t time.Duration) {
  104. defer c.timeoutMu.Unlock()
  105. c.timeoutMu.Lock()
  106. if c.conn != nil {
  107. c.conn.SetDeadline(time.Now().Add(t * time.Second))
  108. }
  109. }
  110. func (c *client) init(conn net.Conn, clientID uint64) {
  111. c.conn = conn
  112. // reset our reader & writer
  113. c.bufout.Reset(conn)
  114. c.bufin.Reset(conn)
  115. // reset session data
  116. c.state = 0
  117. c.KilledAt = time.Time{}
  118. c.ConnectedAt = time.Now()
  119. c.ID = clientID
  120. c.TLS = false
  121. c.errors = 0
  122. c.response = ""
  123. c.Helo = ""
  124. }
  125. func (c *client) getID() uint64 {
  126. return c.ID
  127. }