doulu1945
2018-05-11 12:25
浏览 406
已采纳

Golang中smtp的问题(紧急,信号SIGSEGV:分段违规)

I have small application which could verify if email exists on mail server. I know that my implementation do not give 100% result, but let it be. So, I got func which get emails slice and check every email in this slice:

func CheckMails(mails []string) []string {
    var existingMails []string
    fmt.Printf("!!!!!!!!!!!!!!STARTING!!!!!!!!!!!! %s 


", mails[1])
    for i := 0; i < len(mails); i++ {
        err := validateHost(mails[i])
        if err != nil {
            fmt.Printf("Error validating host. %s", err)
        }
        smtpErr, ok := err.(checkmail.SmtpError)
        if ok {
            fmt.Printf("Code: %s, Msg: %s", smtpErr.Code(), smtpErr)
            if smtpErr.Code() == "dia" {
                break
            }
        } else {
            fmt.Println("Email exists")
            existingMails = append(existingMails, mails[i])
        }
    }
    fmt.Printf("!!!!!!!!!!!!!!ENDING!!!!!!!!!!!! %s 


", mails[1])
    return existingMails
}

Next - my func which literally checks if email exists (btw this functions is from small lib github.com/badoux/checkmail but in case I use only this function I copypaste it to my code to log errors, variables, etc.):

func validateHost(email string) error {
    _, host := split(email)
    mx, err := net.LookupMX(host)
    if err != nil {
        fmt.Printf("Error, UnresolvableHost! %s", err)
    }

    client, err := smtp.Dial(fmt.Sprintf("%s:%d", mx[0].Host, 25))
    //  fmt.Println(client)
    defer client.Close()
    if err != nil {
        //fmt.Println(client)
        //log.Fatalln(err)
        fmt.Printf("SmtpError! %s 
", err)
    }

    t := time.AfterFunc(forceDisconnectAfter, func() { client.Close() })
    defer t.Stop()

    // t := NewTimer(10, func() { client.Close() })
    // defer t.Stop()

    err = client.Hello("checkmail.me")
    //err = client.Hello("gmail.com")
    //  fmt.Println(client)
    if err != nil {
        //log.Fatalln(err)
        fmt.Printf("client.Hello SmtpError! %s 
", err)
    }

    err = client.Mail("lansome-cowboy@gmail.com")
    if err != nil {
        fmt.Printf("client.MailSmtpError! %s 
", err)
    }
    err = client.Rcpt(email)
    if err != nil {
        fmt.Printf("client.Rcpt SmtpError! %s 
", err)
    }
    return nil
}

And also I made support function which interrupts checking if response from server is too long:

   // CheckMailsWithExpectedInterval want to get expectected number of seconds which you are ready to wait while
// the mail trys to be validated (this time is for ONE mail, slice could have a lot of mails to check)
// and slice of mails which should be validated
func CheckMailsWithExpectedInterval(expectedSec int, mails []string) (ok bool, existingMails []string) {
    done := make(chan struct{})
    t1 := time.Now()
    var eMails []string
    go func() {
        eMails = CheckMails(mails)
        close(done)
    }()

    select {
    case <-done:
        if len(eMails) > 1 {
            ok = false
        } else {
            ok = true
            existingMails = eMails
        }
    case <-time.After(time.Duration(expectedSec) * time.Second):
    }

    fmt.Printf("
Time since:")
    fmt.Println(time.Since(t1))
    return ok, existingMails
}

And other support functions which are need for functions above:

    func split(email string) (account, host string) {
    i := strings.LastIndexByte(email, '@')
    account = email[:i]
    host = email[i+1:]
    return
}

const forceDisconnectAfter = time.Second * 10

The example of slice which come into functions is

email1 := []string{
        "andreasId@fromatob.com",
        "Andreas.Wolff@fromatob.com",
        "AndreasWolff@fromatob.com",
        "Wolff.Andreas@fromatob.com",
        "WolffAndreas@fromatob.com",
        "Andreas@fromatob.com,Wolff@fromatob.com",
        "A.Wolff@fromatob.com",
        "AWolff@fromatob.com",
        "Andreas.W@fromatob.com",
        "AndreasW@fromatob.com",
        "Wolff.A@fromatob.com",
        "WolffA@fromatob.com",
        "W.Andreas@fromatob.com",
        "WAndreas@fromatob.com",
    }

And I got next problem. Sometimes my application panic with next:

    panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x84fefa]
goroutine 260 [running]:
net/smtp.(*Client).Close(0x0, 0x0, 0x0)
    /usr/lib/go-1.10/src/net/smtp/smtp.go:76 +0x2a
panic(0x90a360, 0xb94ae0)
    /usr/lib/go-1.10/src/runtime/panic.go:502 +0x24a
net/smtp.(*Client).Hello(0x0, 0x97d912, 0x9, 0x0, 0x0)
    /usr/lib/go-1.10/src/net/smtp/smtp.go:100 +0x78
magictool/mailchecker.validateHost(0xc4204c7020, 0x23, 0x0, 0x0)
    /home/username/go/src/magictool/mailchecker/mailchecker.go:115 +0x749
magictool/mailchecker.CheckMails(0xc4201c0000, 0xf, 0xf, 0x0, 0x0, 0x0)
    /home/username/go/src/magictool/mailchecker/mailchecker.go:20 +0x1d1
magictool/mailchecker.CheckMailsWithExpectedInterval.func1(0xc4201c0000, 0xf, 0xf, 0xc42038f5c0, 0xc4203b7ec0)
    /home/username/go/src/magictool/mailchecker/mailchecker.go:49 +0x3f
created by magictool/mailchecker.CheckMailsWithExpectedInterval
    /home/username/go/src/magictool/mailchecker/mailchecker.go:48 +0x12c

I really can't understand how to handle this panic. Also, fyi, the application crashed after ~3-5 slices checked, couple hours ago was checked 10 slices of mails and after that it crashes.

And, it can be important, past 1-2 weeks everything was fine and no errors/panics. I give to function 20-50-100-150 slices of emails (1 slice is near 15 emails) and it was no errors.

Any help would be appreciated!

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

1条回答 默认 最新

  • doufulian4076 2018-05-11 12:35
    已采纳

    You're not doing proper error handling. This is probably causing your problem. In particular, you'll notice from the stack trace that you're getting a panic on line 76 of the standard library's smtp.go file. This line reads:

    return c.Text.Close()
    

    So it seems you're trying to close an invalid connection.

    This makes intuitive sense, when we read your code:

    client, err := smtp.Dial(fmt.Sprintf("%s:%d", mx[0].Host, 25))
    //  fmt.Println(client)
    defer client.Close()
    if err != nil {
        //fmt.Println(client)
        //log.Fatalln(err)
        fmt.Printf("SmtpError! %s 
    ", err)
    }
    

    You call defer client.Close() before you even know if the client connection is valid. This means that if the SMTP connection fails, you'll be attempting to close an invalid connection, which very likely would lead to the observed behavior.

    To correct this, you must make two changes.

    1. Don't just print errors, actually handle them. This probably just means returning them:

      client, err := smtp.Dial(fmt.Sprintf("%s:%d", mx[0].Host, 25))
      defer client.Close()
      if err != nil {
          return err
      }
      

      Do this for every one of your errors.

    2. Only call client.Close() after you know you have a valid connection!

      client, err := smtp.Dial(fmt.Sprintf("%s:%d", mx[0].Host, 25))
      if err != nil {
          return err
      }
      defer client.Close()
      
    已采纳该答案
    打赏 评论

相关推荐 更多相似问题