server_test.go 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150
  1. package guerrilla
  2. import (
  3. "os"
  4. "testing"
  5. "bufio"
  6. "net/textproto"
  7. "strings"
  8. "sync"
  9. "crypto/tls"
  10. "fmt"
  11. "io/ioutil"
  12. "net"
  13. "github.com/flashmob/go-guerrilla/backends"
  14. "github.com/flashmob/go-guerrilla/log"
  15. "github.com/flashmob/go-guerrilla/mail"
  16. "github.com/flashmob/go-guerrilla/mocks"
  17. )
  18. // getMockServerConfig gets a mock ServerConfig struct used for creating a new server
  19. func getMockServerConfig() *ServerConfig {
  20. sc := &ServerConfig{
  21. IsEnabled: true, // not tested here
  22. Hostname: "saggydimes.test.com",
  23. MaxSize: 1024, // smtp message max size
  24. TLS: ServerTLSConfig{
  25. PrivateKeyFile: "./tests/mail.guerrillamail.com.key.pem",
  26. PublicKeyFile: "./tests/mail.guerrillamail.com.cert.pem",
  27. StartTLSOn: true,
  28. AlwaysOn: false,
  29. },
  30. Timeout: 5,
  31. ListenInterface: "127.0.0.1:2529",
  32. MaxClients: 30, // not tested here
  33. LogFile: "./tests/testlog",
  34. }
  35. return sc
  36. }
  37. // getMockServerConn gets a new server using sc. Server will be using a mocked TCP connection
  38. // using the dummy backend
  39. // RCP TO command only allows test.com host
  40. func getMockServerConn(sc *ServerConfig, t *testing.T) (*mocks.Conn, *server) {
  41. var logOpenError error
  42. var mainlog log.Logger
  43. mainlog, logOpenError = log.GetLogger(sc.LogFile, "debug")
  44. if logOpenError != nil {
  45. mainlog.WithError(logOpenError).Errorf("Failed creating a logger for mock conn [%s]", sc.ListenInterface)
  46. }
  47. backend, err := backends.New(
  48. backends.BackendConfig{"log_received_mails": true, "save_workers_size": 1},
  49. mainlog)
  50. if err != nil {
  51. t.Error("new dummy backend failed because:", err)
  52. }
  53. server, err := newServer(sc, backend, mainlog)
  54. if err != nil {
  55. //t.Error("new server failed because:", err)
  56. } else {
  57. server.setAllowedHosts([]string{"test.com"})
  58. }
  59. conn := mocks.NewConn()
  60. return conn, server
  61. }
  62. // test the RootCAs tls config setting
  63. var rootCAPK = `-----BEGIN CERTIFICATE-----
  64. MIIDqjCCApKgAwIBAgIJALh2TrsBR5MiMA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNV
  65. BAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEhMB8G
  66. A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv
  67. c3QwIBcNMTgwNTE4MDYzOTU2WhgPMjExODA0MjQwNjM5NTZaMGkxCzAJBgNVBAYT
  68. AlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEhMB8GA1UE
  69. CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3Qw
  70. ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCcb0ulYT1o5ysor5UtWYW
  71. q/ZY3PyK3/4YBZq5JoX4xk7GNQQ+3p/Km7QPoBXfgjFLZXEV2R0bE5hHMXfLa5Xb
  72. 64acb9VqCqDvPFXcaNP4rEdBKDVN2p0PEi917tcKBSrZn5Yl+iOhtcBpQDvhHgn/
  73. 9MdmIAKB3+yK+4l9YhT40XfDXCQqzfg4XcNaEgTzZHcDJz+KjWJuJChprcx27MTI
  74. Ndxs9nmFA2rK16rjgjtwjZ4t9dXsljdOcx59s6dIQ0GnEM8qdKxi/vEx4+M/hbGf
  75. v7H75LsuKRrVJINAmfy9fmc6VAXjFU0ZVxGK5eVnzsh/hY08TSSrlCCKAJpksjJz
  76. AgMBAAGjUzBRMB0GA1UdDgQWBBSZsYWs+8FYe4z4c6LLmFB4TeeV/jAfBgNVHSME
  77. GDAWgBSZsYWs+8FYe4z4c6LLmFB4TeeV/jAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
  78. SIb3DQEBCwUAA4IBAQAcXt/FaILkOCMj8bTUx42vi2N9ZTiEuRbYi24IyGOokbDR
  79. pSsIxiz+HDPUuX6/X/mHidl24aS9wdv5JTXMr44/BeGK1WC7gMueZBxAqONpaG1Q
  80. VU0e3q1YwXKcupKQ7kVWl0fuY3licv0+s4zBcTLKkmWAYqsb/n0KtCMyqewi+Rqa
  81. Zj5Z3OcWOq9Ad9fZWKcG8k/sgeTk9z0X1mZcEyWWxqsUmxvN+SdWLoug1xJVVbMN
  82. CipZ0vBIi9KOhQgzuIFhoTcd6myUtov52/EFqlX6UuFpY2gEWw/f/yu+SI08v4w9
  83. KwxgAKBkhx2JYZKtu1EsPIMDyS0aahcDnHqnrGAi
  84. -----END CERTIFICATE-----`
  85. var clientPrvKey = `-----BEGIN RSA PRIVATE KEY-----
  86. MIIEowIBAAKCAQEA5ZLmMBdKkVyVmN0VhDSFGvgKp24ejHPCv+wfuf3vlU9cwKfH
  87. R3vejleZAVRcidscfA0Jsub/Glsr0XwecagtpvTI+Fp1ik6sICOz+VW3958qaAi8
  88. TjbUMjcDHJeSLcjr725CH5uIvhRzR+daYaJQhAcL2MEt8M9WIF6AjtDZEH9R6oM8
  89. t5FkO0amImlnipYXNBFghmzkZzfGXXRQLw2A+u6keLcjCrn9h2BaofGIjQfYcu/3
  90. fH4cIFR4z/soGKameqnCUz7dWmbf4tAI+8QR0VXXBKhiHDm98tPSeH994hC52Uul
  91. rjEVcM5Uox5hazS2PK06oSc1YuFZONqeeGqj6wIDAQABAoIBADERzRHKaK3ZVEBw
  92. QQEZGLpC+kP/TZhHxgCvv7hJhsQrSnADbJzi5RcXsiSOm5j7tILvZntO1IgVpLAK
  93. D5fLkrZ069/pteXyGuhjuTw6DjBnXPEPrPAq2ABDse6SlzQiFgv/TTLkU74NMPbV
  94. hIQJ5ZvSxb12zRMDviz9Bg2ApmTX6k2iPjQBnEHgKzb64IdMcEb5HE1qNt0v0lRA
  95. sGBMZZKQWbt2m0pSbAbnB3S9GcpJkRgFFMdTaUScIWO6ICT2hBP2pw2/4M2Zrmlt
  96. bsyWu9uswBzhvu+/pg2E66V6mji0EzDMlXqjlO5jro6t7P33t1zkd/i/ykKmtDLp
  97. IpR94UECgYEA9Y4EIjOyaBWJ6TRQ6a/tehGPbwIOgvEiTYXRJqdU49qn/i4YZjSm
  98. F4iibJz+JeOIQXSwa9F7gRlaspIuHgIJoer7BrITMuhr+afqMLkxK0pijul/qAbm
  99. HdpFn8IxjpNu4/GoAENbEVy50SMST9yWh5ulEkHHftd4/NJKoJQ2PZ8CgYEA71bb
  100. lFVh1MFclxRKECmpyoqUAzwGlMoHJy/jaBYuWG4X7rzxqDRrgPH3as6gXpRiSZ+K
  101. 5fC+wcU7dKnHtJOkBDk6J5ev2+hbwg+yq3w4+l3bPDvf2TJyXjXjRDZo12pxFD58
  102. ybCOF6ItbIDXqT5pvo3PMjgMwu1Ycie+h6hA3jUCgYEAsq93XpQT/R2/T44cWxEE
  103. VFG2+GacvLhP5+26ttAJPA1/Nb3BT458Vp+84iCT6GpcWpVZU/wKTXVvxIYPPRLq
  104. g4MEzGiFBASRngiMqIv6ta/ZbHmJxXHPvmV5SLn9aezrQsA1KovZFxdMuF03FBpH
  105. B8NBKbnoO+r8Ra2ZVKTFm60CgYAZw8Dpi/N3IsWj4eRDLyj/C8H5Qyn2NHVmq4oQ
  106. d2rPzDI5Wg+tqs7z15hp4Ap1hAW8pTcfn7X5SBEpculzr/0VE1AGWRbuVmoiTuxN
  107. 95ZupVHnfw6O5BZZu/VWL4FDx0qbAksOrznso2G+b3RH3NcnUz69yjjddw1xZIPn
  108. OJ6bDQKBgDUcWYu/2amU18D5vJpppUgRq2084WPUeXsaniTbmWfOC8NAn8CKLY0N
  109. V4yGSu98apDuqEVqL0VFQEgqK+5KTvRdXXYi36XYRbbVUgV13xveq2YTvjNbPM60
  110. QWG9YmgH7hVYGusuh5nQeS0qiIpwyws2H5mBVrGXrQ1Xb0MLWj8/
  111. -----END RSA PRIVATE KEY-----`
  112. // signed using the Root (rootCAPK)
  113. var clientPubKey = `-----BEGIN CERTIFICATE-----
  114. MIIDWDCCAkACCQCHoh4OvUySOzANBgkqhkiG9w0BAQsFADBpMQswCQYDVQQGEwJV
  115. UzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxITAfBgNVBAoM
  116. GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MCAX
  117. DTE4MDUxODA2NDQ0NVoYDzMwMTcwOTE4MDY0NDQ1WjBxMQswCQYDVQQGEwJVUzET
  118. MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEhMB8G
  119. A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv
  120. c3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDlkuYwF0qRXJWY3RWE
  121. NIUa+Aqnbh6Mc8K/7B+5/e+VT1zAp8dHe96OV5kBVFyJ2xx8DQmy5v8aWyvRfB5x
  122. qC2m9Mj4WnWKTqwgI7P5Vbf3nypoCLxONtQyNwMcl5ItyOvvbkIfm4i+FHNH51ph
  123. olCEBwvYwS3wz1YgXoCO0NkQf1Hqgzy3kWQ7RqYiaWeKlhc0EWCGbORnN8ZddFAv
  124. DYD67qR4tyMKuf2HYFqh8YiNB9hy7/d8fhwgVHjP+ygYpqZ6qcJTPt1aZt/i0Aj7
  125. xBHRVdcEqGIcOb3y09J4f33iELnZS6WuMRVwzlSjHmFrNLY8rTqhJzVi4Vk42p54
  126. aqPrAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAIQmlo8iCpyYggkbpfDmThBPHfy1
  127. cZcCi/tRFoFe1ccwn2ezLMIKmW38ZebiroawwqrZgU6AP+dMxVKLMjpyLPSrpFKa
  128. 3o/LbVF7qMfH8/y2q8t7javd6rxoENH9uxLyHhauzI1iWy0whoDWBNiZrPBTBCjq
  129. jDGZARZqGyrPeXi+RNe1cMvZCxAFy7gqEtWFLWWrp0gYNPvxkHhhQBrUcF+8T/Nf
  130. 9G4hKZSN/KAgC0CNBVuNrdyNc3l8H66BfwwL5X0+pesBYZM+MEfmBZOo+p7OWx2r
  131. ug8tR8eSL1vGleONtFRBUVG7NbtjhBf9FhvPZcSRR10od/vWHku9E01i4xg=
  132. -----END CERTIFICATE-----`
  133. func truncateIfExists(filename string) error {
  134. if _, err := os.Stat(filename); !os.IsNotExist(err) {
  135. return os.Truncate(filename, 0)
  136. }
  137. return nil
  138. }
  139. func deleteIfExists(filename string) error {
  140. if _, err := os.Stat(filename); !os.IsNotExist(err) {
  141. return os.Remove(filename)
  142. }
  143. return nil
  144. }
  145. func cleanTestArtifacts(t *testing.T) {
  146. if err := deleteIfExists("rootca.test.pem"); err != nil {
  147. t.Error(err)
  148. }
  149. if err := deleteIfExists("client.test.key"); err != nil {
  150. t.Error(err)
  151. }
  152. if err := deleteIfExists("client.test.pem"); err != nil {
  153. t.Error(err)
  154. }
  155. if err := deleteIfExists("./tests/mail.guerrillamail.com.key.pem"); err != nil {
  156. t.Error(err)
  157. }
  158. if err := deleteIfExists("./tests/mail.guerrillamail.com.cert.pem"); err != nil {
  159. t.Error(err)
  160. }
  161. if err := deleteIfExists("./tests/different-go-guerrilla.pid"); err != nil {
  162. t.Error(err)
  163. }
  164. if err := deleteIfExists("./tests/go-guerrilla.pid"); err != nil {
  165. t.Error(err)
  166. }
  167. if err := deleteIfExists("./tests/go-guerrilla2.pid"); err != nil {
  168. t.Error(err)
  169. }
  170. if err := deleteIfExists("./tests/pidfile.pid"); err != nil {
  171. t.Error(err)
  172. }
  173. if err := deleteIfExists("./tests/pidfile2.pid"); err != nil {
  174. t.Error(err)
  175. }
  176. if err := truncateIfExists("./tests/testlog"); err != nil {
  177. t.Error(err)
  178. }
  179. if err := truncateIfExists("./tests/testlog2"); err != nil {
  180. t.Error(err)
  181. }
  182. }
  183. func TestTLSConfig(t *testing.T) {
  184. defer cleanTestArtifacts(t)
  185. if err := ioutil.WriteFile("rootca.test.pem", []byte(rootCAPK), 0644); err != nil {
  186. t.Fatal("couldn't create rootca.test.pem file.", err)
  187. return
  188. }
  189. if err := ioutil.WriteFile("client.test.key", []byte(clientPrvKey), 0644); err != nil {
  190. t.Fatal("couldn't create client.test.key file.", err)
  191. return
  192. }
  193. if err := ioutil.WriteFile("client.test.pem", []byte(clientPubKey), 0644); err != nil {
  194. t.Fatal("couldn't create client.test.pem file.", err)
  195. return
  196. }
  197. s := server{}
  198. s.setConfig(&ServerConfig{
  199. TLS: ServerTLSConfig{
  200. StartTLSOn: true,
  201. PrivateKeyFile: "client.test.key",
  202. PublicKeyFile: "client.test.pem",
  203. RootCAs: "rootca.test.pem",
  204. ClientAuthType: "NoClientCert",
  205. Curves: []string{"P521", "P384"},
  206. Ciphers: []string{"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"},
  207. Protocols: []string{"tls1.0", "tls1.2"},
  208. },
  209. })
  210. if err := s.configureTLS(); err != nil {
  211. t.Error(err)
  212. }
  213. c := s.tlsConfigStore.Load().(*tls.Config)
  214. if len(c.CurvePreferences) != 2 {
  215. t.Error("c.CurvePreferences should have two elements")
  216. } else if c.CurvePreferences[0] != tls.CurveP521 && c.CurvePreferences[1] != tls.CurveP384 {
  217. t.Error("c.CurvePreferences curves not setup")
  218. }
  219. if !strings.Contains(string(c.RootCAs.Subjects()[0]), "Mountain View") {
  220. t.Error("c.RootCAs not correctly set")
  221. }
  222. if c.ClientAuth != tls.NoClientCert {
  223. t.Error("c.ClientAuth should be tls.NoClientCert")
  224. }
  225. if len(c.CipherSuites) != 2 {
  226. t.Error("c.CipherSuites length should be 2")
  227. }
  228. if c.CipherSuites[0] != tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 && c.CipherSuites[1] != tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA {
  229. t.Error("c.CipherSuites not correctly set ")
  230. }
  231. if c.MinVersion != tls.VersionTLS10 {
  232. t.Error("c.MinVersion should be tls.VersionTLS10")
  233. }
  234. if c.MaxVersion != tls.VersionTLS12 {
  235. t.Error("c.MinVersion should be tls.VersionTLS10")
  236. }
  237. if c.PreferServerCipherSuites != false {
  238. t.Error("PreferServerCipherSuites should be false")
  239. }
  240. }
  241. func TestHandleClient(t *testing.T) {
  242. var mainlog log.Logger
  243. var logOpenError error
  244. defer cleanTestArtifacts(t)
  245. sc := getMockServerConfig()
  246. mainlog, logOpenError = log.GetLogger(sc.LogFile, "debug")
  247. if logOpenError != nil {
  248. mainlog.WithError(logOpenError).Errorf("Failed creating a logger for mock conn [%s]", sc.ListenInterface)
  249. }
  250. conn, server := getMockServerConn(sc, t)
  251. // call the serve.handleClient() func in a goroutine.
  252. client := NewClient(conn.Server, 1, mainlog, mail.NewPool(5))
  253. var wg sync.WaitGroup
  254. wg.Add(1)
  255. go func() {
  256. server.handleClient(client)
  257. wg.Done()
  258. }()
  259. // Wait for the greeting from the server
  260. r := textproto.NewReader(bufio.NewReader(conn.Client))
  261. line, _ := r.ReadLine()
  262. // fmt.Println(line)
  263. w := textproto.NewWriter(bufio.NewWriter(conn.Client))
  264. if err := w.PrintfLine("HELO test.test.com"); err != nil {
  265. t.Error(err)
  266. }
  267. line, _ = r.ReadLine()
  268. //fmt.Println(line)
  269. if err := w.PrintfLine("QUIT"); err != nil {
  270. t.Error(err)
  271. }
  272. line, _ = r.ReadLine()
  273. //fmt.Println("line is:", line)
  274. expected := "221 2.0.0 Bye"
  275. if strings.Index(line, expected) != 0 {
  276. t.Error("expected", expected, "but got:", line)
  277. }
  278. wg.Wait() // wait for handleClient to exit
  279. }
  280. func TestGithubIssue197(t *testing.T) {
  281. var mainlog log.Logger
  282. var logOpenError error
  283. defer cleanTestArtifacts(t)
  284. sc := getMockServerConfig()
  285. mainlog, logOpenError = log.GetLogger(sc.LogFile, "debug")
  286. if logOpenError != nil {
  287. mainlog.WithError(logOpenError).Errorf("Failed creating a logger for mock conn [%s]", sc.ListenInterface)
  288. }
  289. conn, server := getMockServerConn(sc, t)
  290. server.backend().Start()
  291. // we assume that 1.1.1.1 is a domain (ip-literal syntax is incorrect)
  292. // [2001:DB8::FF00:42:8329] is an address literal
  293. server.setAllowedHosts([]string{"1.1.1.1", "[2001:DB8::FF00:42:8329]"})
  294. client := NewClient(conn.Server, 1, mainlog, mail.NewPool(5))
  295. var wg sync.WaitGroup
  296. wg.Add(1)
  297. go func() {
  298. server.handleClient(client)
  299. wg.Done()
  300. }()
  301. // Wait for the greeting from the server
  302. r := textproto.NewReader(bufio.NewReader(conn.Client))
  303. line, _ := r.ReadLine()
  304. // fmt.Println(line)
  305. w := textproto.NewWriter(bufio.NewWriter(conn.Client))
  306. if err := w.PrintfLine("HELO test.test.com"); err != nil {
  307. t.Error(err)
  308. }
  309. line, _ = r.ReadLine()
  310. // Case 1
  311. if err := w.PrintfLine("rcpt to: <hi@[1.1.1.1]>"); err != nil {
  312. t.Error(err)
  313. }
  314. line, _ = r.ReadLine()
  315. if client.parser.IP == nil {
  316. t.Error("[1.1.1.1] not parsed as address-liteal")
  317. }
  318. // case 2, should be parsed as domain
  319. if err := w.PrintfLine("rcpt to: <[email protected]>"); err != nil {
  320. t.Error(err)
  321. }
  322. line, _ = r.ReadLine()
  323. if client.parser.IP != nil {
  324. t.Error("1.1.1.1 should not be parsed as an IP (syntax requires IP addresses to be in braces, eg <hi@[1.1.1.1]>")
  325. }
  326. // case 3
  327. // prefix ipv6 is case insensitive
  328. if err := w.PrintfLine("rcpt to: <hi@[ipv6:2001:DB8::FF00:42:8329]>"); err != nil {
  329. t.Error(err)
  330. }
  331. line, _ = r.ReadLine()
  332. if client.parser.IP == nil {
  333. t.Error("[ipv6:2001:DB8::FF00:42:8329] should be parsed as an address-literal, it wasnt")
  334. }
  335. // case 4
  336. if err := w.PrintfLine("rcpt to: <hi@[IPv6:2001:0db8:0000:0000:0000:ff00:0042:8329]>"); err != nil {
  337. t.Error(err)
  338. }
  339. line, _ = r.ReadLine()
  340. if client.parser.Domain != "2001:DB8::FF00:42:8329" && client.parser.IP == nil {
  341. t.Error("[IPv6:2001:0db8:0000:0000:0000:ff00:0042:8329] is same as 2001:DB8::FF00:42:8329, lol")
  342. }
  343. if err := w.PrintfLine("QUIT"); err != nil {
  344. t.Error(err)
  345. }
  346. line, _ = r.ReadLine()
  347. //fmt.Println("line is:", line)
  348. expected := "221 2.0.0 Bye"
  349. if strings.Index(line, expected) != 0 {
  350. t.Error("expected", expected, "but got:", line)
  351. }
  352. wg.Wait() // wait for handleClient to exit
  353. }
  354. var githubIssue198data string
  355. var customBackend = func() backends.Decorator {
  356. return func(p backends.Processor) backends.Processor {
  357. return backends.ProcessWith(
  358. func(e *mail.Envelope, task backends.SelectTask) (backends.Result, error) {
  359. if task == backends.TaskSaveMail {
  360. githubIssue198data = e.DeliveryHeader + e.Data.String()
  361. }
  362. return p.Process(e, task)
  363. })
  364. }
  365. }
  366. // TestGithubIssue198 is an interesting test because it shows how to do an integration test for
  367. // a backend using a custom backend.
  368. func TestGithubIssue198(t *testing.T) {
  369. var mainlog log.Logger
  370. var logOpenError error
  371. defer cleanTestArtifacts(t)
  372. sc := getMockServerConfig()
  373. mainlog, logOpenError = log.GetLogger(sc.LogFile, "debug")
  374. backends.Svc.AddProcessor("custom", customBackend)
  375. if logOpenError != nil {
  376. mainlog.WithError(logOpenError).Errorf("Failed creating a logger for mock conn [%s]", sc.ListenInterface)
  377. }
  378. conn, server := getMockServerConn(sc, t)
  379. be, err := backends.New(map[string]interface{}{
  380. "save_process": "HeadersParser|Header|custom", "primary_mail_host": "example.com"},
  381. mainlog)
  382. if err != nil {
  383. t.Error(err)
  384. return
  385. }
  386. server.setBackend(be)
  387. if err := server.backend().Start(); err != nil {
  388. t.Error(err)
  389. return
  390. }
  391. server.setAllowedHosts([]string{"1.1.1.1", "[2001:DB8::FF00:42:8329]"})
  392. client := NewClient(conn.Server, 1, mainlog, mail.NewPool(5))
  393. client.RemoteIP = "127.0.0.1"
  394. var wg sync.WaitGroup
  395. wg.Add(1)
  396. go func() {
  397. server.handleClient(client)
  398. wg.Done()
  399. }()
  400. // Wait for the greeting from the server
  401. r := textproto.NewReader(bufio.NewReader(conn.Client))
  402. line, _ := r.ReadLine()
  403. w := textproto.NewWriter(bufio.NewWriter(conn.Client))
  404. // Test with HELO greeting
  405. line = sendMessage("HELO", true, w, t, line, r, err, client)
  406. if !strings.Contains(githubIssue198data, " SMTPS ") {
  407. t.Error("'with SMTPS' not present")
  408. }
  409. if !strings.Contains(githubIssue198data, "from 127.0.0.1") {
  410. t.Error("'from 127.0.0.1' not present")
  411. }
  412. /////////////////////
  413. if err := w.PrintfLine("RSET"); err != nil {
  414. t.Error(err)
  415. }
  416. // Test with EHLO
  417. line, _ = r.ReadLine()
  418. line = sendMessage("EHLO", true, w, t, line, r, err, client)
  419. if !strings.Contains(githubIssue198data, " ESMTPS ") {
  420. t.Error("'with ESMTPS' not present")
  421. }
  422. /////////////////////
  423. if err := w.PrintfLine("RSET"); err != nil {
  424. t.Error(err)
  425. }
  426. line, _ = r.ReadLine()
  427. // Test with EHLO & no TLS
  428. line = sendMessage("EHLO", false, w, t, line, r, err, client)
  429. /////////////////////
  430. if !strings.Contains(githubIssue198data, " ESMTP ") {
  431. t.Error("'with ESTMP' not present")
  432. }
  433. if err := w.PrintfLine("QUIT"); err != nil {
  434. t.Error(err)
  435. }
  436. line, _ = r.ReadLine()
  437. expected := "221 2.0.0 Bye"
  438. if strings.Index(line, expected) != 0 {
  439. t.Error("expected", expected, "but got:", line)
  440. }
  441. wg.Wait() // wait for handleClient to exit
  442. }
  443. func sendMessage(greet string, TLS bool, w *textproto.Writer, t *testing.T, line string, r *textproto.Reader, err error, client *client) string {
  444. if err := w.PrintfLine(greet + " test.test.com"); err != nil {
  445. t.Error(err)
  446. }
  447. for {
  448. line, _ = r.ReadLine()
  449. if strings.Index(line, "250 ") == 0 {
  450. break
  451. }
  452. if strings.Index(line, "250") != 0 {
  453. t.Error(err)
  454. }
  455. }
  456. if err := w.PrintfLine("MAIL FROM: [email protected]>"); err != nil {
  457. t.Error(err)
  458. }
  459. line, _ = r.ReadLine()
  460. if err := w.PrintfLine("RCPT TO: <hi@[ipv6:2001:DB8::FF00:42:8329]>"); err != nil {
  461. t.Error(err)
  462. }
  463. line, _ = r.ReadLine()
  464. client.Hashes = append(client.Hashes, "abcdef1526777763")
  465. client.TLS = TLS
  466. if err := w.PrintfLine("DATA"); err != nil {
  467. t.Error(err)
  468. }
  469. line, _ = r.ReadLine()
  470. if err := w.PrintfLine("Subject: Test subject\r\n\r\nHello Sir,\nThis is a test.\r\n."); err != nil {
  471. t.Error(err)
  472. }
  473. line, _ = r.ReadLine()
  474. return line
  475. }
  476. func TestGithubIssue199(t *testing.T) {
  477. var mainlog log.Logger
  478. var logOpenError error
  479. defer cleanTestArtifacts(t)
  480. sc := getMockServerConfig()
  481. mainlog, logOpenError = log.GetLogger(sc.LogFile, "debug")
  482. if logOpenError != nil {
  483. mainlog.WithError(logOpenError).Errorf("Failed creating a logger for mock conn [%s]", sc.ListenInterface)
  484. }
  485. conn, server := getMockServerConn(sc, t)
  486. server.backend().Start()
  487. server.setAllowedHosts([]string{"grr.la", "fake.com", "[1.1.1.1]", "[2001:db8::8a2e:370:7334]", "saggydimes.test.com"})
  488. client := NewClient(conn.Server, 1, mainlog, mail.NewPool(5))
  489. var wg sync.WaitGroup
  490. wg.Add(1)
  491. go func() {
  492. server.handleClient(client)
  493. wg.Done()
  494. }()
  495. // Wait for the greeting from the server
  496. r := textproto.NewReader(bufio.NewReader(conn.Client))
  497. line, _ := r.ReadLine()
  498. // fmt.Println(line)
  499. w := textproto.NewWriter(bufio.NewWriter(conn.Client))
  500. if err := w.PrintfLine("HELO test"); err != nil {
  501. t.Error(err)
  502. }
  503. line, _ = r.ReadLine()
  504. // case 1
  505. if err := w.PrintfLine(
  506. "MAIL FROM: <\" yo-- man wazz'''up? surprise surprise, this is [email protected] \"@example.com>"); err != nil {
  507. t.Error(err)
  508. }
  509. line, _ = r.ReadLine()
  510. // [SPACE][SPACE]yo--[SPACE]man[SPACE]wazz'''up?[SPACE]surprise[SPACE]surprise,[SPACE]this[SPACE]is[SPACE][email protected][SPACE]
  511. if client.parser.LocalPart != " yo-- man wazz'''up? surprise surprise, this is [email protected] " {
  512. t.Error("expecting local part: [ yo-- man wazz'''up? surprise surprise, this is [email protected] ], got client.parser.LocalPart")
  513. }
  514. if !client.parser.LocalPartQuotes {
  515. t.Error("was expecting client.parser.LocalPartQuotes true, got false")
  516. }
  517. // from should just as above but without angle brackets <>
  518. if from := client.MailFrom.String(); from != "\" yo-- man wazz'''up? surprise surprise, this is [email protected] \"@example.com" {
  519. t.Error("mail from was:", from)
  520. }
  521. if line != "250 2.1.0 OK" {
  522. t.Error("line did not have: 250 2.1.0 OK, got", line)
  523. }
  524. if err := w.PrintfLine("RSET"); err != nil {
  525. t.Error(err)
  526. }
  527. line, _ = r.ReadLine()
  528. // case 2, address literal mailboxes
  529. if err := w.PrintfLine("MAIL FROM: <hi@[1.1.1.1]>"); err != nil {
  530. t.Error(err)
  531. }
  532. line, _ = r.ReadLine()
  533. // stringer should be aware its an ip and return the host part in angle brackets
  534. if from := client.MailFrom.String(); from != "hi@[1.1.1.1]" {
  535. t.Error("mail from was:", from)
  536. }
  537. if err := w.PrintfLine("RSET"); err != nil {
  538. t.Error(err)
  539. }
  540. line, _ = r.ReadLine()
  541. // case 3
  542. if err := w.PrintfLine("MAIL FROM: <hi@[IPv6:2001:0db8:0000:0000:0000:8a2e:0370:7334]>"); err != nil {
  543. t.Error(err)
  544. }
  545. line, _ = r.ReadLine()
  546. // stringer should be aware its an ip and return the host part in angle brackets, and ipv6 should be normalized
  547. if from := client.MailFrom.String(); from != "hi@[2001:db8::8a2e:370:7334]" {
  548. t.Error("mail from was:", from)
  549. }
  550. if err := w.PrintfLine("RSET"); err != nil {
  551. t.Error(err)
  552. }
  553. line, _ = r.ReadLine()
  554. // case 4
  555. // rcpt to: <hi@[IPv6:2001:0db8:0000:0000:0000:ff00:0042:8329]>
  556. if err := w.PrintfLine("MAIL FROM: <>"); err != nil {
  557. t.Error(err)
  558. }
  559. line, _ = r.ReadLine()
  560. if err := w.PrintfLine("RCPT TO: <Postmaster>"); err != nil {
  561. t.Error(err)
  562. }
  563. line, _ = r.ReadLine()
  564. // stringer should return an empty string
  565. if from := client.MailFrom.String(); from != "" {
  566. t.Error("mail from was:", from)
  567. }
  568. // note here the saggydimes.test.com was added because no host was specified in the RCPT TO command
  569. if rcpt := client.RcptTo[0].String(); rcpt != "[email protected]" {
  570. t.Error("mail from was:", rcpt)
  571. }
  572. // additional cases
  573. /*
  574. user part:
  575. " al\ph\a "@grr.la should be " alpha "@grr.la.
  576. "alpha"@grr.la should be [email protected].
  577. "alp\h\a"@grr.la should be [email protected].
  578. */
  579. if err := w.PrintfLine("RSET"); err != nil {
  580. t.Error(err)
  581. }
  582. line, _ = r.ReadLine()
  583. if err := w.PrintfLine("RCPT TO: <\" al\\ph\\a \"@grr.la>"); err != nil {
  584. t.Error(err)
  585. }
  586. line, _ = r.ReadLine()
  587. if client.RcptTo[0].User != " alpha " {
  588. t.Error(client.RcptTo[0].User)
  589. }
  590. // the unnecessary \\ should be removed
  591. if rcpt := client.RcptTo[0].String(); rcpt != "\" alpha \"@grr.la" {
  592. t.Error(rcpt)
  593. }
  594. if err := w.PrintfLine("RSET"); err != nil {
  595. t.Error(err)
  596. }
  597. line, _ = r.ReadLine()
  598. if err := w.PrintfLine("RCPT TO: <\"alpha\"@grr.la>"); err != nil {
  599. t.Error(err)
  600. }
  601. line, _ = r.ReadLine()
  602. // we don't need to quote, so stringer should return without the quotes
  603. if rcpt := client.RcptTo[0].String(); rcpt != "[email protected]" {
  604. t.Error(rcpt)
  605. }
  606. if err := w.PrintfLine("RSET"); err != nil {
  607. t.Error(err)
  608. }
  609. line, _ = r.ReadLine()
  610. if err := w.PrintfLine("RCPT TO: <\"a\\l\\pha\"@grr.la>"); err != nil {
  611. t.Error(err)
  612. }
  613. line, _ = r.ReadLine()
  614. // we don't need to quote, so stringer should return without the quotes
  615. if rcpt := client.RcptTo[0].String(); rcpt != "[email protected]" {
  616. t.Error(rcpt)
  617. }
  618. if err := w.PrintfLine("QUIT"); err != nil {
  619. t.Error(err)
  620. }
  621. line, _ = r.ReadLine()
  622. wg.Wait() // wait for handleClient to exit
  623. }
  624. func TestGithubIssue200(t *testing.T) {
  625. var mainlog log.Logger
  626. var logOpenError error
  627. defer cleanTestArtifacts(t)
  628. sc := getMockServerConfig()
  629. mainlog, logOpenError = log.GetLogger(sc.LogFile, "debug")
  630. if logOpenError != nil {
  631. mainlog.WithError(logOpenError).Errorf("Failed creating a logger for mock conn [%s]", sc.ListenInterface)
  632. }
  633. conn, server := getMockServerConn(sc, t)
  634. server.backend().Start()
  635. server.setAllowedHosts([]string{"1.1.1.1", "[2001:DB8::FF00:42:8329]"})
  636. client := NewClient(conn.Server, 1, mainlog, mail.NewPool(5))
  637. var wg sync.WaitGroup
  638. wg.Add(1)
  639. go func() {
  640. server.handleClient(client)
  641. wg.Done()
  642. }()
  643. // Wait for the greeting from the server
  644. r := textproto.NewReader(bufio.NewReader(conn.Client))
  645. line, _ := r.ReadLine()
  646. // fmt.Println(line)
  647. w := textproto.NewWriter(bufio.NewWriter(conn.Client))
  648. if err := w.PrintfLine("HELO test\"><script>alert('hi')</script>test.com"); err != nil {
  649. t.Error(err)
  650. }
  651. line, _ = r.ReadLine()
  652. if line != "550 5.5.2 Syntax error" {
  653. t.Error("line expected to be: 550 5.5.2 Syntax error, got", line)
  654. }
  655. if err := w.PrintfLine("HELO test.com"); err != nil {
  656. t.Error(err)
  657. }
  658. line, _ = r.ReadLine()
  659. if !strings.Contains(line, "250") {
  660. t.Error("line did not have 250 code, got", line)
  661. }
  662. if err := w.PrintfLine("QUIT"); err != nil {
  663. t.Error(err)
  664. }
  665. line, _ = r.ReadLine()
  666. //fmt.Println("line is:", line)
  667. expected := "221 2.0.0 Bye"
  668. if strings.Index(line, expected) != 0 {
  669. t.Error("expected", expected, "but got:", line)
  670. }
  671. wg.Wait() // wait for handleClient to exit
  672. }
  673. func TestGithubIssue201(t *testing.T) {
  674. var mainlog log.Logger
  675. var logOpenError error
  676. defer cleanTestArtifacts(t)
  677. sc := getMockServerConfig()
  678. mainlog, logOpenError = log.GetLogger(sc.LogFile, "debug")
  679. if logOpenError != nil {
  680. mainlog.WithError(logOpenError).Errorf("Failed creating a logger for mock conn [%s]", sc.ListenInterface)
  681. }
  682. conn, server := getMockServerConn(sc, t)
  683. server.backend().Start()
  684. // note that saggydimes.test.com is the hostname of the server, it comes form the config
  685. // it will be used for rcpt to:<postmaster> which does not specify a host
  686. server.setAllowedHosts([]string{"a.com", "saggydimes.test.com"})
  687. client := NewClient(conn.Server, 1, mainlog, mail.NewPool(5))
  688. var wg sync.WaitGroup
  689. wg.Add(1)
  690. go func() {
  691. server.handleClient(client)
  692. wg.Done()
  693. }()
  694. // Wait for the greeting from the server
  695. r := textproto.NewReader(bufio.NewReader(conn.Client))
  696. line, _ := r.ReadLine()
  697. // fmt.Println(line)
  698. w := textproto.NewWriter(bufio.NewWriter(conn.Client))
  699. if err := w.PrintfLine("HELO test"); err != nil {
  700. t.Error(err)
  701. }
  702. line, _ = r.ReadLine()
  703. // case 1
  704. if err := w.PrintfLine("RCPT TO: <[email protected]>"); err != nil {
  705. t.Error(err)
  706. }
  707. line, _ = r.ReadLine()
  708. if line != "250 2.1.5 OK" {
  709. t.Error("line did not have: 250 2.1.5 OK, got", line)
  710. }
  711. // case 2
  712. if err := w.PrintfLine("RCPT TO: <[email protected]>"); err != nil {
  713. t.Error(err)
  714. }
  715. line, _ = r.ReadLine()
  716. if line != "454 4.1.1 Error: Relay access denied: not-a.com" {
  717. t.Error("line is not:454 4.1.1 Error: Relay access denied: not-a.com, got", line)
  718. }
  719. // case 3 (no host specified)
  720. if err := w.PrintfLine("RCPT TO: <poSTmAsteR>"); err != nil {
  721. t.Error(err)
  722. }
  723. line, _ = r.ReadLine()
  724. if line != "250 2.1.5 OK" {
  725. t.Error("line is not:[250 2.1.5 OK], got", line)
  726. }
  727. // case 4
  728. if err := w.PrintfLine("RCPT TO: <\"po\\ST\\mAs\\t\\eR\">"); err != nil {
  729. t.Error(err)
  730. }
  731. line, _ = r.ReadLine()
  732. if line != "250 2.1.5 OK" {
  733. t.Error("line is not:[250 2.1.5 OK], got", line)
  734. }
  735. // the local part should be just "postmaster" (normalized)
  736. if client.parser.LocalPart != "postmaster" {
  737. t.Error("client.parser.LocalPart was not postmaster, got:", client.parser.LocalPart)
  738. }
  739. if client.parser.LocalPartQuotes {
  740. t.Error("client.parser.LocalPartQuotes was true, expecting false")
  741. }
  742. if err := w.PrintfLine("QUIT"); err != nil {
  743. t.Error(err)
  744. }
  745. line, _ = r.ReadLine()
  746. //fmt.Println("line is:", line)
  747. expected := "221 2.0.0 Bye"
  748. if strings.Index(line, expected) != 0 {
  749. t.Error("expected", expected, "but got:", line)
  750. }
  751. wg.Wait() // wait for handleClient to exit
  752. }
  753. func TestXClient(t *testing.T) {
  754. var mainlog log.Logger
  755. var logOpenError error
  756. defer cleanTestArtifacts(t)
  757. sc := getMockServerConfig()
  758. sc.XClientOn = true
  759. mainlog, logOpenError = log.GetLogger(sc.LogFile, "debug")
  760. if logOpenError != nil {
  761. mainlog.WithError(logOpenError).Errorf("Failed creating a logger for mock conn [%s]", sc.ListenInterface)
  762. }
  763. conn, server := getMockServerConn(sc, t)
  764. // call the serve.handleClient() func in a goroutine.
  765. client := NewClient(conn.Server, 1, mainlog, mail.NewPool(5))
  766. var wg sync.WaitGroup
  767. wg.Add(1)
  768. go func() {
  769. server.handleClient(client)
  770. wg.Done()
  771. }()
  772. // Wait for the greeting from the server
  773. r := textproto.NewReader(bufio.NewReader(conn.Client))
  774. line, _ := r.ReadLine()
  775. // fmt.Println(line)
  776. w := textproto.NewWriter(bufio.NewWriter(conn.Client))
  777. if err := w.PrintfLine("HELO test.test.com"); err != nil {
  778. t.Error(err)
  779. }
  780. line, _ = r.ReadLine()
  781. //fmt.Println(line)
  782. if err := w.PrintfLine("XCLIENT ADDR=212.96.64.216 NAME=[UNAVAILABLE]"); err != nil {
  783. t.Error(err)
  784. }
  785. line, _ = r.ReadLine()
  786. if client.RemoteIP != "212.96.64.216" {
  787. t.Error("client.RemoteIP should be 212.96.64.216, but got:", client.RemoteIP)
  788. }
  789. expected := "250 2.1.0 OK"
  790. if strings.Index(line, expected) != 0 {
  791. t.Error("expected", expected, "but got:", line)
  792. }
  793. // try malformed input
  794. if err := w.PrintfLine("XCLIENT c"); err != nil {
  795. t.Error(err)
  796. }
  797. line, _ = r.ReadLine()
  798. expected = "250 2.1.0 OK"
  799. if strings.Index(line, expected) != 0 {
  800. t.Error("expected", expected, "but got:", line)
  801. }
  802. if err := w.PrintfLine("QUIT"); err != nil {
  803. t.Error(err)
  804. }
  805. line, _ = r.ReadLine()
  806. wg.Wait() // wait for handleClient to exit
  807. }
  808. // The backend gateway should time out after 1 second because it sleeps for 2 sec.
  809. // The transaction should wait until finished, and then test to see if we can do
  810. // a second transaction
  811. func TestGatewayTimeout(t *testing.T) {
  812. defer cleanTestArtifacts(t)
  813. bcfg := backends.BackendConfig{
  814. "save_workers_size": 1,
  815. "save_process": "HeadersParser|Debugger",
  816. "log_received_mails": true,
  817. "primary_mail_host": "example.com",
  818. "gw_save_timeout": "1s",
  819. "gw_val_rcpt_timeout": "1s",
  820. "sleep_seconds": 2,
  821. }
  822. cfg := &AppConfig{
  823. LogFile: log.OutputOff.String(),
  824. AllowedHosts: []string{"grr.la"},
  825. }
  826. cfg.BackendConfig = bcfg
  827. d := Daemon{Config: cfg}
  828. err := d.Start()
  829. if err != nil {
  830. t.Error("server didn't start")
  831. } else {
  832. conn, err := net.Dial("tcp", "127.0.0.1:2525")
  833. if err != nil {
  834. return
  835. }
  836. in := bufio.NewReader(conn)
  837. str, err := in.ReadString('\n')
  838. if err != nil {
  839. t.Error(err)
  840. }
  841. if _, err := fmt.Fprint(conn, "HELO host\r\n"); err != nil {
  842. t.Error(err)
  843. }
  844. str, err = in.ReadString('\n')
  845. // perform 2 transactions
  846. // both should panic.
  847. for i := 0; i < 2; i++ {
  848. if _, err := fmt.Fprint(conn, "MAIL FROM:<[email protected]>r\r\n"); err != nil {
  849. t.Error(err)
  850. }
  851. if str, err = in.ReadString('\n'); err != nil {
  852. t.Error(err)
  853. }
  854. if _, err := fmt.Fprint(conn, "RCPT TO:<[email protected]>\r\n"); err != nil {
  855. t.Error(err)
  856. }
  857. if str, err = in.ReadString('\n'); err != nil {
  858. t.Error(err)
  859. }
  860. if _, err := fmt.Fprint(conn, "DATA\r\n"); err != nil {
  861. t.Error(err)
  862. }
  863. if str, err = in.ReadString('\n'); err != nil {
  864. t.Error(err)
  865. }
  866. if _, err := fmt.Fprint(conn, "Subject: Test subject\r\n"); err != nil {
  867. t.Error(err)
  868. }
  869. if _, err := fmt.Fprint(conn, "\r\n"); err != nil {
  870. t.Error(err)
  871. }
  872. if _, err := fmt.Fprint(conn, "A an email body\r\n"); err != nil {
  873. t.Error(err)
  874. }
  875. if _, err := fmt.Fprint(conn, ".\r\n"); err != nil {
  876. t.Error(err)
  877. }
  878. str, err = in.ReadString('\n')
  879. expect := "transaction timeout"
  880. if err != nil {
  881. t.Error(err)
  882. } else if !strings.Contains(str, expect) {
  883. t.Error("Expected the reply to have'", expect, "'but got", str)
  884. }
  885. }
  886. _ = str
  887. d.Shutdown()
  888. }
  889. }
  890. // The processor will panic and gateway should recover from it
  891. func TestGatewayPanic(t *testing.T) {
  892. defer cleanTestArtifacts(t)
  893. bcfg := backends.BackendConfig{
  894. "save_workers_size": 1,
  895. "save_process": "HeadersParser|Debugger",
  896. "log_received_mails": true,
  897. "primary_mail_host": "example.com",
  898. "gw_save_timeout": "2s",
  899. "gw_val_rcpt_timeout": "2s",
  900. "sleep_seconds": 1,
  901. }
  902. cfg := &AppConfig{
  903. LogFile: log.OutputOff.String(),
  904. AllowedHosts: []string{"grr.la"},
  905. }
  906. cfg.BackendConfig = bcfg
  907. d := Daemon{Config: cfg}
  908. err := d.Start()
  909. if err != nil {
  910. t.Error("server didn't start")
  911. } else {
  912. conn, err := net.Dial("tcp", "127.0.0.1:2525")
  913. if err != nil {
  914. return
  915. }
  916. in := bufio.NewReader(conn)
  917. if _, err := in.ReadString('\n'); err != nil {
  918. t.Error(err)
  919. }
  920. if _, err := fmt.Fprint(conn, "HELO host\r\n"); err != nil {
  921. t.Error(err)
  922. }
  923. if _, err = in.ReadString('\n'); err != nil {
  924. t.Error(err)
  925. }
  926. // perform 2 transactions
  927. // both should timeout. The reason why 2 is because we want to make
  928. // sure that the client waits until processing finishes, and the
  929. // timeout event is captured.
  930. for i := 0; i < 2; i++ {
  931. if _, err := fmt.Fprint(conn, "MAIL FROM:<[email protected]>r\r\n"); err != nil {
  932. t.Error(err)
  933. }
  934. if _, err = in.ReadString('\n'); err != nil {
  935. t.Error(err)
  936. }
  937. if _, err := fmt.Fprint(conn, "RCPT TO:<[email protected]>\r\n"); err != nil {
  938. t.Error(err)
  939. }
  940. if _, err = in.ReadString('\n'); err != nil {
  941. t.Error(err)
  942. }
  943. if _, err := fmt.Fprint(conn, "DATA\r\n"); err != nil {
  944. t.Error(err)
  945. }
  946. if _, err = in.ReadString('\n'); err != nil {
  947. t.Error(err)
  948. }
  949. if _, err := fmt.Fprint(conn, "Subject: Test subject\r\n"); err != nil {
  950. t.Error(err)
  951. }
  952. if _, err := fmt.Fprint(conn, "\r\n"); err != nil {
  953. t.Error(err)
  954. }
  955. if _, err := fmt.Fprint(conn, "A an email body\r\n"); err != nil {
  956. t.Error(err)
  957. }
  958. if _, err := fmt.Fprint(conn, ".\r\n"); err != nil {
  959. t.Error(err)
  960. }
  961. if str, err := in.ReadString('\n'); err != nil {
  962. t.Error(err)
  963. } else {
  964. expect := "storage failed"
  965. if !strings.Contains(str, expect) {
  966. t.Error("Expected the reply to have'", expect, "'but got", str)
  967. }
  968. }
  969. }
  970. d.Shutdown()
  971. }
  972. }
  973. func TestAllowsHosts(t *testing.T) {
  974. defer cleanTestArtifacts(t)
  975. s := server{}
  976. allowedHosts := []string{
  977. "spam4.me",
  978. "grr.la",
  979. "newhost.com",
  980. "example.*",
  981. "*.test",
  982. "wild*.card",
  983. "multiple*wild*cards.*",
  984. "[::FFFF:C0A8:1]", // ip4 in ipv6 format. It's actually 192.168.0.1
  985. "[2001:db8::ff00:42:8329]", // same as 2001:0db8:0000:0000:0000:ff00:0042:8329
  986. "[127.0.0.1]",
  987. }
  988. s.setAllowedHosts(allowedHosts)
  989. testTable := map[string]bool{
  990. "spam4.me": true,
  991. "dont.match": false,
  992. "example.com": true,
  993. "another.example.com": false,
  994. "anything.test": true,
  995. "wild.card": true,
  996. "wild.card.com": false,
  997. "multipleXwildXcards.com": true,
  998. }
  999. for host, allows := range testTable {
  1000. if res := s.allowsHost(host); res != allows {
  1001. t.Error(host, ": expected", allows, "but got", res)
  1002. }
  1003. }
  1004. testTableIP := map[string]bool{
  1005. "192.168.0.1": true,
  1006. "2001:0db8:0000:0000:0000:ff00:0042:8329": true,
  1007. "127.0.0.1": true,
  1008. }
  1009. for host, allows := range testTableIP {
  1010. if res := s.allowsIp(net.ParseIP(host)); res != allows {
  1011. t.Error(host, ": expected", allows, "but got", res)
  1012. }
  1013. }
  1014. // only wildcard - should match anything
  1015. s.setAllowedHosts([]string{"*"})
  1016. if !s.allowsHost("match.me") {
  1017. t.Error("match.me: expected true but got false")
  1018. }
  1019. // turns off
  1020. s.setAllowedHosts([]string{"."})
  1021. if !s.allowsHost("match.me") {
  1022. t.Error("match.me: expected true but got false")
  1023. }
  1024. // no wilcards
  1025. s.setAllowedHosts([]string{"grr.la", "example.com"})
  1026. }