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

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()
      
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 matlab有关常微分方程的问题求解决
  • ¥15 perl MISA分析p3_in脚本出错
  • ¥15 k8s部署jupyterlab,jupyterlab保存不了文件
  • ¥15 ubuntu虚拟机打包apk错误
  • ¥199 rust编程架构设计的方案 有偿
  • ¥15 回答4f系统的像差计算
  • ¥15 java如何提取出pdf里的文字?
  • ¥100 求三轴之间相互配合画圆以及直线的算法
  • ¥100 c语言,请帮蒟蒻写一个题的范例作参考
  • ¥15 名为“Product”的列已属于此 DataTable