client.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. package guerrilla
  2. import (
  3. "bufio"
  4. "bytes"
  5. "crypto/tls"
  6. "errors"
  7. "fmt"
  8. "github.com/flashmob/go-guerrilla/log"
  9. "github.com/flashmob/go-guerrilla/mail"
  10. "github.com/flashmob/go-guerrilla/mail/rfc5321"
  11. "github.com/flashmob/go-guerrilla/response"
  12. "net"
  13. "net/textproto"
  14. "sync"
  15. "time"
  16. )
  17. // ClientState indicates which part of the SMTP transaction a given client is in.
  18. type ClientState int
  19. const (
  20. // The client has connected, and is awaiting our first response
  21. ClientGreeting = iota
  22. // We have responded to the client's connection and are awaiting a command
  23. ClientCmd
  24. // We have received the sender and recipient information
  25. ClientData
  26. // We have agreed with the client to secure the connection over TLS
  27. ClientStartTLS
  28. // Server will shutdown, client to shutdown on next command turn
  29. ClientShutdown
  30. )
  31. type client struct {
  32. *mail.Envelope
  33. ID uint64
  34. ConnectedAt time.Time
  35. KilledAt time.Time
  36. // Number of errors encountered during session with this client
  37. errors int
  38. state ClientState
  39. messagesSent int
  40. // Response to be written to the client (for debugging)
  41. response bytes.Buffer
  42. bufErr error
  43. conn net.Conn
  44. bufin *smtpBufferedReader
  45. bufout *bufio.Writer
  46. smtpReader *textproto.Reader
  47. ar *adjustableLimitedReader
  48. // guards access to conn
  49. connGuard sync.Mutex
  50. log log.Logger
  51. parser rfc5321.Parser
  52. }
  53. // NewClient allocates a new client.
  54. func NewClient(conn net.Conn, clientID uint64, logger log.Logger, envelope *mail.Pool) *client {
  55. c := &client{
  56. conn: conn,
  57. // Envelope will be borrowed from the envelope pool
  58. // the envelope could be 'detached' from the client later when processing
  59. Envelope: envelope.Borrow(getRemoteAddr(conn), clientID),
  60. ConnectedAt: time.Now(),
  61. bufin: newSMTPBufferedReader(conn),
  62. bufout: bufio.NewWriter(conn),
  63. ID: clientID,
  64. log: logger,
  65. }
  66. // used for reading the DATA state
  67. c.smtpReader = textproto.NewReader(c.bufin.Reader)
  68. return c
  69. }
  70. // sendResponse adds a response to be written on the next turn
  71. // the response gets buffered
  72. func (c *client) sendResponse(r ...interface{}) {
  73. c.bufout.Reset(c.conn)
  74. if c.log.IsDebug() {
  75. // an additional buffer so that we can log the response in debug mode only
  76. c.response.Reset()
  77. }
  78. var out string
  79. if c.bufErr != nil {
  80. c.bufErr = nil
  81. }
  82. for _, item := range r {
  83. switch v := item.(type) {
  84. case error:
  85. out = v.Error()
  86. case fmt.Stringer:
  87. out = v.String()
  88. case string:
  89. out = v
  90. }
  91. if _, c.bufErr = c.bufout.WriteString(out); c.bufErr != nil {
  92. c.log.WithError(c.bufErr).Error("could not write to c.bufout")
  93. }
  94. if c.log.IsDebug() {
  95. c.response.WriteString(out)
  96. }
  97. if c.bufErr != nil {
  98. return
  99. }
  100. }
  101. _, c.bufErr = c.bufout.WriteString("\r\n")
  102. if c.log.IsDebug() {
  103. c.response.WriteString("\r\n")
  104. }
  105. }
  106. // resetTransaction resets the SMTP transaction, ready for the next email (doesn't disconnect)
  107. // Transaction ends on:
  108. // -HELO/EHLO/REST command
  109. // -End of DATA command
  110. // TLS handhsake
  111. func (c *client) resetTransaction() {
  112. c.Envelope.ResetTransaction()
  113. }
  114. // isInTransaction returns true if the connection is inside a transaction.
  115. // A transaction starts after a MAIL command gets issued by the client.
  116. // Call resetTransaction to end the transaction
  117. func (c *client) isInTransaction() bool {
  118. if len(c.MailFrom.User) == 0 && !c.MailFrom.NullPath {
  119. return false
  120. }
  121. return true
  122. }
  123. // kill flags the connection to close on the next turn
  124. func (c *client) kill() {
  125. c.KilledAt = time.Now()
  126. }
  127. // isAlive returns true if the client is to close on the next turn
  128. func (c *client) isAlive() bool {
  129. return c.KilledAt.IsZero()
  130. }
  131. // setTimeout adjust the timeout on the connection, goroutine safe
  132. func (c *client) setTimeout(t time.Duration) {
  133. defer c.connGuard.Unlock()
  134. c.connGuard.Lock()
  135. if c.conn != nil {
  136. c.conn.SetDeadline(time.Now().Add(t * time.Second))
  137. }
  138. }
  139. // closeConn closes a client connection, , goroutine safe
  140. func (c *client) closeConn() {
  141. defer c.connGuard.Unlock()
  142. c.connGuard.Lock()
  143. c.conn.Close()
  144. c.conn = nil
  145. }
  146. // init is called after the client is borrowed from the pool, to get it ready for the connection
  147. func (c *client) init(conn net.Conn, clientID uint64, ep *mail.Pool) {
  148. c.conn = conn
  149. // reset our reader & writer
  150. c.bufout.Reset(conn)
  151. c.bufin.Reset(conn)
  152. // reset session data
  153. c.state = 0
  154. c.KilledAt = time.Time{}
  155. c.ConnectedAt = time.Now()
  156. c.ID = clientID
  157. c.errors = 0
  158. // borrow an envelope from the envelope pool
  159. c.Envelope = ep.Borrow(getRemoteAddr(conn), clientID)
  160. }
  161. // getID returns the client's unique ID
  162. func (c *client) getID() uint64 {
  163. return c.ID
  164. }
  165. // UpgradeToTLS upgrades a client connection to TLS
  166. func (c *client) upgradeToTLS(tlsConfig *tls.Config) error {
  167. var tlsConn *tls.Conn
  168. // wrap c.conn in a new TLS server side connection
  169. tlsConn = tls.Server(c.conn, tlsConfig)
  170. // Call handshake here to get any handshake error before reading starts
  171. err := tlsConn.Handshake()
  172. if err != nil {
  173. return err
  174. }
  175. // convert tlsConn to net.Conn
  176. c.conn = net.Conn(tlsConn)
  177. c.bufout.Reset(c.conn)
  178. c.bufin.Reset(c.conn)
  179. c.TLS = true
  180. return err
  181. }
  182. func getRemoteAddr(conn net.Conn) string {
  183. if addr, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
  184. // we just want the IP (not the port)
  185. return addr.IP.String()
  186. } else {
  187. return conn.RemoteAddr().Network()
  188. }
  189. }
  190. type pathParser func([]byte) error
  191. func (c *client) parsePath(in []byte, p pathParser) (mail.Address, error) {
  192. address := mail.Address{}
  193. var err error
  194. if len(in) > rfc5321.LimitPath {
  195. return address, errors.New(response.Canned.FailPathTooLong.String())
  196. }
  197. if err = p(in); err != nil {
  198. return address, errors.New(response.Canned.FailInvalidAddress.String())
  199. } else if c.parser.NullPath {
  200. // bounce has empty from address
  201. address = mail.Address{}
  202. } else if len(c.parser.LocalPart) > rfc5321.LimitLocalPart {
  203. err = errors.New(response.Canned.FailLocalPartTooLong.String())
  204. } else if len(c.parser.Domain) > rfc5321.LimitDomain {
  205. err = errors.New(response.Canned.FailDomainTooLong.String())
  206. } else {
  207. address = mail.Address{
  208. User: c.parser.LocalPart,
  209. Host: c.parser.Domain,
  210. ADL: c.parser.ADL,
  211. PathParams: c.parser.PathParams,
  212. NullPath: c.parser.NullPath,
  213. }
  214. }
  215. return address, err
  216. }
  217. func (s *server) rcptTo(in []byte) (address mail.Address, err error) {
  218. return address, err
  219. }