server.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. package guerrilla
  2. import (
  3. "crypto/rand"
  4. "crypto/tls"
  5. "fmt"
  6. "io"
  7. "net"
  8. "strings"
  9. "sync"
  10. "sync/atomic"
  11. "time"
  12. "crypto/x509"
  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/response"
  17. "io/ioutil"
  18. "path/filepath"
  19. )
  20. const (
  21. CommandVerbMaxLength = 16
  22. CommandLineMaxLength = 1024
  23. // Number of allowed unrecognized commands before we terminate the connection
  24. MaxUnrecognizedCommands = 5
  25. // The maximum total length of a reverse-path or forward-path is 256
  26. RFC2821LimitPath = 256
  27. // The maximum total length of a user name or other local-part is 64
  28. RFC2832LimitLocalPart = 64
  29. //The maximum total length of a domain name or number is 255
  30. RFC2821LimitDomain = 255
  31. // The minimum total number of recipients that must be buffered is 100
  32. RFC2821LimitRecipients = 100
  33. )
  34. const (
  35. // server has just been created
  36. ServerStateNew = iota
  37. // Server has just been stopped
  38. ServerStateStopped
  39. // Server has been started and is running
  40. ServerStateRunning
  41. // Server could not start due to an error
  42. ServerStateStartError
  43. )
  44. // Server listens for SMTP clients on the port specified in its config
  45. type server struct {
  46. configStore atomic.Value // stores guerrilla.ServerConfig
  47. tlsConfigStore atomic.Value
  48. timeout atomic.Value // stores time.Duration
  49. listenInterface string
  50. clientPool *Pool
  51. wg sync.WaitGroup // for waiting to shutdown
  52. listener net.Listener
  53. closedListener chan (bool)
  54. hosts allowedHosts // stores map[string]bool for faster lookup
  55. state int
  56. // If log changed after a config reload, newLogStore stores the value here until it's safe to change it
  57. logStore atomic.Value
  58. mainlogStore atomic.Value
  59. backendStore atomic.Value
  60. envelopePool *mail.Pool
  61. }
  62. type allowedHosts struct {
  63. table map[string]bool // host lookup table
  64. wildcards []string // host wildcard list (* is used as a wildcard)
  65. sync.Mutex // guard access to the map
  66. }
  67. // Creates and returns a new ready-to-run Server from a configuration
  68. func newServer(sc *ServerConfig, b backends.Backend, l log.Logger) (*server, error) {
  69. server := &server{
  70. clientPool: NewPool(sc.MaxClients),
  71. closedListener: make(chan (bool), 1),
  72. listenInterface: sc.ListenInterface,
  73. state: ServerStateNew,
  74. envelopePool: mail.NewPool(sc.MaxClients),
  75. }
  76. server.logStore.Store(l)
  77. server.backendStore.Store(b)
  78. logFile := sc.LogFile
  79. if logFile == "" {
  80. // none set, use the same log file as mainlog
  81. logFile = server.mainlog().GetLogDest()
  82. }
  83. // set level to same level as mainlog level
  84. mainlog, logOpenError := log.GetLogger(logFile, server.mainlog().GetLevel())
  85. server.mainlogStore.Store(mainlog)
  86. if logOpenError != nil {
  87. server.log().WithError(logOpenError).Errorf("Failed creating a logger for server [%s]", sc.ListenInterface)
  88. }
  89. server.setConfig(sc)
  90. server.setTimeout(sc.Timeout)
  91. if err := server.configureSSL(); err != nil {
  92. return server, err
  93. }
  94. return server, nil
  95. }
  96. func (s *server) configureSSL() error {
  97. sConfig := s.configStore.Load().(ServerConfig)
  98. if sConfig.TLS.AlwaysOn || sConfig.TLS.StartTLSOn {
  99. cert, err := tls.LoadX509KeyPair(sConfig.TLS.PublicKeyFile, sConfig.TLS.PrivateKeyFile)
  100. if err != nil {
  101. return fmt.Errorf("error while loading the certificate: %s", err)
  102. }
  103. tlsConfig := &tls.Config{
  104. Certificates: []tls.Certificate{cert},
  105. ClientAuth: tls.VerifyClientCertIfGiven,
  106. ServerName: sConfig.Hostname,
  107. }
  108. if len(sConfig.TLS.Protocols) > 0 {
  109. if min, ok := TLSProtocols[sConfig.TLS.Protocols[0]]; ok {
  110. tlsConfig.MinVersion = min
  111. }
  112. }
  113. if len(sConfig.TLS.Protocols) > 1 {
  114. if max, ok := TLSProtocols[sConfig.TLS.Protocols[1]]; ok {
  115. tlsConfig.MaxVersion = max
  116. }
  117. }
  118. if len(sConfig.TLS.Ciphers) > 0 {
  119. for _, val := range sConfig.TLS.Ciphers {
  120. if c, ok := TLSCiphers[val]; ok {
  121. tlsConfig.CipherSuites = append(tlsConfig.CipherSuites, c)
  122. }
  123. }
  124. }
  125. if len(sConfig.TLS.Curves) > 0 {
  126. for _, val := range sConfig.TLS.Curves {
  127. if c, ok := TLSCurves[val]; ok {
  128. tlsConfig.CurvePreferences = append(tlsConfig.CurvePreferences, c)
  129. }
  130. }
  131. }
  132. if len(sConfig.TLS.RootCAs) > 0 {
  133. caCert, err := ioutil.ReadFile(sConfig.TLS.RootCAs)
  134. if err != nil {
  135. s.log().WithError(err).Errorf("failed opening TLSRootCAs file [%s]", sConfig.TLS.RootCAs)
  136. } else {
  137. caCertPool := x509.NewCertPool()
  138. caCertPool.AppendCertsFromPEM(caCert)
  139. tlsConfig.RootCAs = caCertPool
  140. }
  141. }
  142. if len(sConfig.TLS.ClientAuthType) > 0 {
  143. if ca, ok := TLSClientAuthTypes[sConfig.TLS.ClientAuthType]; ok {
  144. tlsConfig.ClientAuth = ca
  145. }
  146. }
  147. tlsConfig.PreferServerCipherSuites = sConfig.TLS.PreferServerCipherSuites
  148. tlsConfig.Rand = rand.Reader
  149. s.tlsConfigStore.Store(tlsConfig)
  150. }
  151. return nil
  152. }
  153. // setBackend sets the backend to use for processing email envelopes
  154. func (s *server) setBackend(b backends.Backend) {
  155. s.backendStore.Store(b)
  156. }
  157. // backend gets the backend used to process email envelopes
  158. func (s *server) backend() backends.Backend {
  159. if b, ok := s.backendStore.Load().(backends.Backend); ok {
  160. return b
  161. }
  162. return nil
  163. }
  164. // Set the timeout for the server and all clients
  165. func (server *server) setTimeout(seconds int) {
  166. duration := time.Duration(int64(seconds))
  167. server.clientPool.SetTimeout(duration)
  168. server.timeout.Store(duration)
  169. }
  170. // goroutine safe config store
  171. func (server *server) setConfig(sc *ServerConfig) {
  172. server.configStore.Store(*sc)
  173. }
  174. // goroutine safe
  175. func (server *server) isEnabled() bool {
  176. sc := server.configStore.Load().(ServerConfig)
  177. return sc.IsEnabled
  178. }
  179. // Set the allowed hosts for the server
  180. func (server *server) setAllowedHosts(allowedHosts []string) {
  181. server.hosts.Lock()
  182. defer server.hosts.Unlock()
  183. server.hosts.table = make(map[string]bool, len(allowedHosts))
  184. server.hosts.wildcards = nil
  185. for _, h := range allowedHosts {
  186. if strings.Index(h, "*") != -1 {
  187. server.hosts.wildcards = append(server.hosts.wildcards, strings.ToLower(h))
  188. } else {
  189. server.hosts.table[strings.ToLower(h)] = true
  190. }
  191. }
  192. }
  193. // Begin accepting SMTP clients. Will block unless there is an error or server.Shutdown() is called
  194. func (server *server) Start(startWG *sync.WaitGroup) error {
  195. var clientID uint64
  196. clientID = 0
  197. listener, err := net.Listen("tcp", server.listenInterface)
  198. server.listener = listener
  199. if err != nil {
  200. startWG.Done() // don't wait for me
  201. server.state = ServerStateStartError
  202. return fmt.Errorf("[%s] Cannot listen on port: %s ", server.listenInterface, err.Error())
  203. }
  204. server.log().Infof("Listening on TCP %s", server.listenInterface)
  205. server.state = ServerStateRunning
  206. startWG.Done() // start successful, don't wait for me
  207. for {
  208. server.log().Debugf("[%s] Waiting for a new client. Next Client ID: %d", server.listenInterface, clientID+1)
  209. conn, err := listener.Accept()
  210. clientID++
  211. if err != nil {
  212. if e, ok := err.(net.Error); ok && !e.Temporary() {
  213. server.log().Infof("Server [%s] has stopped accepting new clients", server.listenInterface)
  214. // the listener has been closed, wait for clients to exit
  215. server.log().Infof("shutting down pool [%s]", server.listenInterface)
  216. server.clientPool.ShutdownState()
  217. server.clientPool.ShutdownWait()
  218. server.state = ServerStateStopped
  219. server.closedListener <- true
  220. return nil
  221. }
  222. server.mainlog().WithError(err).Info("Temporary error accepting client")
  223. continue
  224. }
  225. go func(p Poolable, borrow_err error) {
  226. c := p.(*client)
  227. if borrow_err == nil {
  228. server.handleClient(c)
  229. server.envelopePool.Return(c.Envelope)
  230. server.clientPool.Return(c)
  231. } else {
  232. server.log().WithError(borrow_err).Info("couldn't borrow a new client")
  233. // we could not get a client, so close the connection.
  234. conn.Close()
  235. }
  236. // intentionally placed Borrow in args so that it's called in the
  237. // same main goroutine.
  238. }(server.clientPool.Borrow(conn, clientID, server.log(), server.envelopePool))
  239. }
  240. }
  241. func (server *server) Shutdown() {
  242. if server.listener != nil {
  243. // This will cause Start function to return, by causing an error on listener.Accept
  244. server.listener.Close()
  245. // wait for the listener to listener.Accept
  246. <-server.closedListener
  247. // At this point Start will exit and close down the pool
  248. } else {
  249. server.clientPool.ShutdownState()
  250. // listener already closed, wait for clients to exit
  251. server.clientPool.ShutdownWait()
  252. server.state = ServerStateStopped
  253. }
  254. }
  255. func (server *server) GetActiveClientsCount() int {
  256. return server.clientPool.GetActiveClientsCount()
  257. }
  258. // Verifies that the host is a valid recipient.
  259. // host checking turned off if there is a single entry and it's a dot.
  260. func (server *server) allowsHost(host string) bool {
  261. server.hosts.Lock()
  262. defer server.hosts.Unlock()
  263. // if hosts contains a single dot, further processing is skipped
  264. if len(server.hosts.table) == 1 {
  265. if _, ok := server.hosts.table["."]; ok {
  266. return true
  267. }
  268. }
  269. if _, ok := server.hosts.table[strings.ToLower(host)]; ok {
  270. return true
  271. }
  272. // check the willdcards
  273. for _, w := range server.hosts.wildcards {
  274. if matched, err := filepath.Match(w, strings.ToLower(host)); matched && err == nil {
  275. return true
  276. }
  277. }
  278. return false
  279. }
  280. // Reads from the client until a terminating sequence is encountered,
  281. // or until a timeout occurs.
  282. func (server *server) readCommand(client *client, maxSize int64) (string, error) {
  283. var input, reply string
  284. var err error
  285. // In command state, stop reading at line breaks
  286. suffix := "\r\n"
  287. for {
  288. client.setTimeout(server.timeout.Load().(time.Duration))
  289. reply, err = client.bufin.ReadString('\n')
  290. input = input + reply
  291. if err != nil {
  292. break
  293. }
  294. if strings.HasSuffix(input, suffix) {
  295. // discard the suffix and stop reading
  296. input = input[0 : len(input)-len(suffix)]
  297. break
  298. }
  299. }
  300. return input, err
  301. }
  302. // flushResponse a response to the client. Flushes the client.bufout buffer to the connection
  303. func (server *server) flushResponse(client *client) error {
  304. client.setTimeout(server.timeout.Load().(time.Duration))
  305. return client.bufout.Flush()
  306. }
  307. func (server *server) isShuttingDown() bool {
  308. return server.clientPool.IsShuttingDown()
  309. }
  310. // Handles an entire client SMTP exchange
  311. func (server *server) handleClient(client *client) {
  312. defer client.closeConn()
  313. sc := server.configStore.Load().(ServerConfig)
  314. server.log().Infof("Handle client [%s], id: %d", client.RemoteIP, client.ID)
  315. // Initial greeting
  316. greeting := fmt.Sprintf("220 %s SMTP Guerrilla(%s) #%d (%d) %s",
  317. sc.Hostname, Version, client.ID,
  318. server.clientPool.GetActiveClientsCount(), time.Now().Format(time.RFC3339))
  319. helo := fmt.Sprintf("250 %s Hello", sc.Hostname)
  320. // ehlo is a multi-line reply and need additional \r\n at the end
  321. ehlo := fmt.Sprintf("250-%s Hello\r\n", sc.Hostname)
  322. // Extended feature advertisements
  323. messageSize := fmt.Sprintf("250-SIZE %d\r\n", sc.MaxSize)
  324. pipelining := "250-PIPELINING\r\n"
  325. advertiseTLS := "250-STARTTLS\r\n"
  326. advertiseEnhancedStatusCodes := "250-ENHANCEDSTATUSCODES\r\n"
  327. // The last line doesn't need \r\n since string will be printed as a new line.
  328. // Also, Last line has no dash -
  329. help := "250 HELP"
  330. if sc.TLS.AlwaysOn {
  331. tlsConfig, ok := server.tlsConfigStore.Load().(*tls.Config)
  332. if !ok {
  333. server.mainlog().Error("Failed to load *tls.Config")
  334. } else if err := client.upgradeToTLS(tlsConfig); err == nil {
  335. advertiseTLS = ""
  336. } else {
  337. server.log().WithError(err).Warnf("[%s] Failed TLS handshake", client.RemoteIP)
  338. // server requires TLS, but can't handshake
  339. client.kill()
  340. }
  341. }
  342. if !sc.TLS.StartTLSOn {
  343. // STARTTLS turned off, don't advertise it
  344. advertiseTLS = ""
  345. }
  346. for client.isAlive() {
  347. switch client.state {
  348. case ClientGreeting:
  349. client.sendResponse(greeting)
  350. client.state = ClientCmd
  351. case ClientCmd:
  352. client.bufin.setLimit(CommandLineMaxLength)
  353. input, err := server.readCommand(client, sc.MaxSize)
  354. server.log().Debugf("Client sent: %s", input)
  355. if err == io.EOF {
  356. server.log().WithError(err).Warnf("Client closed the connection: %s", client.RemoteIP)
  357. return
  358. } else if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
  359. server.log().WithError(err).Warnf("Timeout: %s", client.RemoteIP)
  360. return
  361. } else if err == LineLimitExceeded {
  362. client.sendResponse(response.Canned.FailLineTooLong)
  363. client.kill()
  364. break
  365. } else if err != nil {
  366. server.log().WithError(err).Warnf("Read error: %s", client.RemoteIP)
  367. client.kill()
  368. break
  369. }
  370. if server.isShuttingDown() {
  371. client.state = ClientShutdown
  372. continue
  373. }
  374. input = strings.Trim(input, " \r\n")
  375. cmdLen := len(input)
  376. if cmdLen > CommandVerbMaxLength {
  377. cmdLen = CommandVerbMaxLength
  378. }
  379. cmd := strings.ToUpper(input[:cmdLen])
  380. switch {
  381. case strings.Index(cmd, "HELO") == 0:
  382. client.Helo = strings.Trim(input[4:], " ")
  383. client.resetTransaction()
  384. client.sendResponse(helo)
  385. case strings.Index(cmd, "EHLO") == 0:
  386. client.Helo = strings.Trim(input[4:], " ")
  387. client.resetTransaction()
  388. client.sendResponse(ehlo,
  389. messageSize,
  390. pipelining,
  391. advertiseTLS,
  392. advertiseEnhancedStatusCodes,
  393. help)
  394. case strings.Index(cmd, "HELP") == 0:
  395. quote := response.GetQuote()
  396. client.sendResponse("214-OK\r\n", quote)
  397. case sc.XClientOn && strings.Index(cmd, "XCLIENT ") == 0:
  398. if toks := strings.Split(input[8:], " "); len(toks) > 0 {
  399. for i := range toks {
  400. if vals := strings.Split(toks[i], "="); len(vals) == 2 {
  401. if vals[1] == "[UNAVAILABLE]" {
  402. // skip
  403. continue
  404. }
  405. if vals[0] == "ADDR" {
  406. client.RemoteIP = vals[1]
  407. }
  408. if vals[0] == "HELO" {
  409. client.Helo = vals[1]
  410. }
  411. }
  412. }
  413. }
  414. client.sendResponse(response.Canned.SuccessMailCmd)
  415. case strings.Index(cmd, "MAIL FROM:") == 0:
  416. if client.isInTransaction() {
  417. client.sendResponse(response.Canned.FailNestedMailCmd)
  418. break
  419. }
  420. addr := input[10:]
  421. if !(strings.Index(addr, "<>") == 0) &&
  422. !(strings.Index(addr, " <>") == 0) {
  423. // Not Bounce, extract mail.
  424. if from, err := extractEmail(addr); err != nil {
  425. client.sendResponse(err)
  426. break
  427. } else {
  428. client.MailFrom = from
  429. }
  430. } else {
  431. // bounce has empty from address
  432. client.MailFrom = mail.Address{}
  433. }
  434. client.sendResponse(response.Canned.SuccessMailCmd)
  435. case strings.Index(cmd, "RCPT TO:") == 0:
  436. if len(client.RcptTo) > RFC2821LimitRecipients {
  437. client.sendResponse(response.Canned.ErrorTooManyRecipients)
  438. break
  439. }
  440. to, err := extractEmail(input[8:])
  441. if err != nil {
  442. client.sendResponse(err.Error())
  443. } else {
  444. if !server.allowsHost(to.Host) {
  445. client.sendResponse(response.Canned.ErrorRelayDenied, " ", to.Host)
  446. } else {
  447. client.PushRcpt(to)
  448. rcptError := server.backend().ValidateRcpt(client.Envelope)
  449. if rcptError != nil {
  450. client.PopRcpt()
  451. client.sendResponse(response.Canned.FailRcptCmd, " ", rcptError.Error())
  452. } else {
  453. client.sendResponse(response.Canned.SuccessRcptCmd)
  454. }
  455. }
  456. }
  457. case strings.Index(cmd, "RSET") == 0:
  458. client.resetTransaction()
  459. client.sendResponse(response.Canned.SuccessResetCmd)
  460. case strings.Index(cmd, "VRFY") == 0:
  461. client.sendResponse(response.Canned.SuccessVerifyCmd)
  462. case strings.Index(cmd, "NOOP") == 0:
  463. client.sendResponse(response.Canned.SuccessNoopCmd)
  464. case strings.Index(cmd, "QUIT") == 0:
  465. client.sendResponse(response.Canned.SuccessQuitCmd)
  466. client.kill()
  467. case strings.Index(cmd, "DATA") == 0:
  468. if len(client.RcptTo) == 0 {
  469. client.sendResponse(response.Canned.FailNoRecipientsDataCmd)
  470. break
  471. }
  472. client.sendResponse(response.Canned.SuccessDataCmd)
  473. client.state = ClientData
  474. case sc.TLS.StartTLSOn && strings.Index(cmd, "STARTTLS") == 0:
  475. client.sendResponse(response.Canned.SuccessStartTLSCmd)
  476. client.state = ClientStartTLS
  477. default:
  478. client.errors++
  479. if client.errors >= MaxUnrecognizedCommands {
  480. client.sendResponse(response.Canned.FailMaxUnrecognizedCmd)
  481. client.kill()
  482. } else {
  483. client.sendResponse(response.Canned.FailUnrecognizedCmd)
  484. }
  485. }
  486. case ClientData:
  487. // intentionally placed the limit 1MB above so that reading does not return with an error
  488. // if the client goes a little over. Anything above will err
  489. client.bufin.setLimit(int64(sc.MaxSize) + 1024000) // This a hard limit.
  490. n, err := client.Data.ReadFrom(client.smtpReader.DotReader())
  491. if n > sc.MaxSize {
  492. err = fmt.Errorf("Maximum DATA size exceeded (%d)", sc.MaxSize)
  493. }
  494. if err != nil {
  495. if err == LineLimitExceeded {
  496. client.sendResponse(response.Canned.FailReadLimitExceededDataCmd, " ", LineLimitExceeded.Error())
  497. client.kill()
  498. } else if err == MessageSizeExceeded {
  499. client.sendResponse(response.Canned.FailMessageSizeExceeded, " ", MessageSizeExceeded.Error())
  500. client.kill()
  501. } else {
  502. client.sendResponse(response.Canned.FailReadErrorDataCmd, " ", err.Error())
  503. client.kill()
  504. }
  505. server.log().WithError(err).Warn("Error reading data")
  506. client.resetTransaction()
  507. break
  508. }
  509. res := server.backend().Process(client.Envelope)
  510. if res.Code() < 300 {
  511. client.messagesSent++
  512. }
  513. client.sendResponse(res)
  514. client.state = ClientCmd
  515. if server.isShuttingDown() {
  516. client.state = ClientShutdown
  517. }
  518. client.resetTransaction()
  519. case ClientStartTLS:
  520. if !client.TLS && sc.TLS.StartTLSOn {
  521. tlsConfig, ok := server.tlsConfigStore.Load().(*tls.Config)
  522. if !ok {
  523. server.mainlog().Error("Failed to load *tls.Config")
  524. } else if err := client.upgradeToTLS(tlsConfig); err == nil {
  525. advertiseTLS = ""
  526. client.resetTransaction()
  527. } else {
  528. server.log().WithError(err).Warnf("[%s] Failed TLS handshake", client.RemoteIP)
  529. // Don't disconnect, let the client decide if it wants to continue
  530. }
  531. }
  532. // change to command state
  533. client.state = ClientCmd
  534. case ClientShutdown:
  535. // shutdown state
  536. client.sendResponse(response.Canned.ErrorShutdown)
  537. client.kill()
  538. }
  539. if client.bufErr != nil {
  540. server.log().WithError(client.bufErr).Debug("client could not buffer a response")
  541. return
  542. }
  543. // flush the response buffer
  544. if client.bufout.Buffered() > 0 {
  545. if server.log().IsDebug() {
  546. server.log().Debugf("Writing response to client: \n%s", client.response.String())
  547. }
  548. err := server.flushResponse(client)
  549. if err != nil {
  550. server.log().WithError(err).Debug("error writing response")
  551. return
  552. }
  553. }
  554. }
  555. }
  556. func (s *server) log() log.Logger {
  557. if l, ok := s.logStore.Load().(log.Logger); ok {
  558. return l
  559. }
  560. l, err := log.GetLogger(log.OutputStderr.String(), log.InfoLevel.String())
  561. if err == nil {
  562. s.logStore.Store(l)
  563. }
  564. return l
  565. }
  566. func (s *server) mainlog() log.Logger {
  567. if l, ok := s.mainlogStore.Load().(log.Logger); ok {
  568. return l
  569. }
  570. l, err := log.GetLogger(log.OutputStderr.String(), log.InfoLevel.String())
  571. if err == nil {
  572. s.mainlogStore.Store(l)
  573. }
  574. return l
  575. }