解析电子邮件“已收到:”标题

时间:2009-02-02 17:51:15

标签: email parsing

我们需要根据RFC 5321解析Received:电子邮件标头。我们需要提取邮件已遍历的域\ IP。此外,我们需要确定IP是否是内部IP。是否已经有一个可以提供帮助的库,特别是在C \ C ++中。

例如,

Received: from server.mymailhost.com (mail.mymailhost.com [126.43.75.123])
    by pilot01.cl.msu.edu (8.10.2/8.10.2) with ESMTP id NAA23597;
    Fri, 12 Jul 2002 16:11:20 -0400 (EDT)

我们需要提取“by”服务器。

感谢

8 个答案:

答案 0 :(得分:5)

“已接收”行使用的格式在RFC 2821中定义,正则表达式无法解析它。

(无论如何,您可以尝试,对于由已知软件生成的有限的标题子集,您可能会成功,但当您将其附加到真实邮件中发现的奇怪内容时,它将会失败。)

使用现有的RFC 2821解析器,你应该没问题,否则你应该期望失败,并编写软件来应对它。不要像安全系统那样重要的东西。

  

我们需要提取“by”服务器。

'from'更有可能被使用。 “by”行中给出的主机名是由主机本身看到的,因此无法保证它将是一个可公开解析的FQDN。当然,你不会在那里获得有效的(TCP-Info)。

答案 1 :(得分:4)

有一个Perl Received module,它是SpamAssassin代码的一个分支。它返回带有相关信息的Received标头的哈希值。例如

{ ip => '64.12.136.4', 
  id => '875522', 
  by => 'xxx.com',
  helo => 'imo-m01.mx.aol.com' }

答案 2 :(得分:2)

vmime应该没问题,任何邮件库都可以让你这么做。

答案 3 :(得分:1)

您可能希望使用正则表达式

。(小于?=通过)*(?=带)

这将给你pilot01.cl.msu.edu(8.10.2 / 8.10.2)

编辑: 我觉得很有趣的是,当它实际上得到了OP所要求的时候,它被修改了。

C#:

string header = "Received: from server.mymailhost.com (mail.mymailhost.com [126.43.75.123]) by pilot01.cl.msu.edu (8.10.2/8.10.2) with ESMTP id NAA23597; Fri, 12 Jul 2002 16:11:20 -0400 (EDT)";
       System.Text.RegularExpressions.Regex r = new System.Text.RegularExpressions.Regex(@"(?<=by).*(?=with)");
       System.Text.RegularExpressions.Match m = r.Match(header);
       Console.WriteLine(m.Captures[0].Value);
       Console.ReadKey();

我没有声称它是完整的,但我想知道是否给了它-1的人甚至尝试过。 MEH ..

答案 4 :(得分:0)

您可以使用正则表达式。它看起来像这样(未经测试):

#include <regex.h>

regex_t *re = malloc(sizeof(regex_t));

const char *restr = "by ([A-Za-z.]+) \(([^\)]*)\)";

check(regcomp(re, restr, REG_EXTENDED | REG_ICASE), "regcomp");

size_t nmatch = 1;

regmatch_t *matches = malloc(sizeof(regmatch_t) * nmatch);

int ret = regexec(re, YOUR_STRING, nmatch, matches, 0);

check(ret != 0, "regexec");

int size;

size = matches[2].rm_eo - matches[2].rm_so;
char *host = malloc(sizeof(char) * size);
strncpy(host, YOUR_STRING + matches[2].rm_so, size );
host[size] = '\0';

size = matches[3].rm_eo - matches[3].rm_so;
char *ip = malloc(sizeof(char) * size);
strncpy(ip, YOUR_STRING + matches[3].rm_so, size );
ip[size] = '\0';

check是一个宏,可以帮助您确定是否存在任何问题:

#define check(condition, description) if (condition) { fprintf(stdout, "%s:%i - %s - %s\n", __FILE__, __LINE__, description, strerror(errno)); exit(1); }

答案 5 :(得分:0)

typedef struct mailHeaders{
    char name[100];
    char value[2000];
}mailHeaders;

int header_count = 0;
mailHeaders headers[30];    // A struct to hold the name value pairs

char *GetMailHeader(char *name)
{
    char *value = NULL;;
    int i;

    for(i=0;i<header_count;i++){
        if(strcmp(name,headers[i].name) == 0){
            value = headers[i].value;
            break;
        }
    }
    return(value);
}


void ReadMail(void)
{

    //Loop through the email message line by line to separate the headers.  Then save the name value pairs to a linked list or struct.  
          char *Received = NULL // Received header
    char *mail = NULL;  // Buffer that has the email message.
    char *line = NULL;  // A line of text in the email. 
    char *name = NULL;  // Header name
    char *value = NULL; // Header value

    int index = -1;     // Header index


    memset(&headers,'\0',sizeof(mailHeaders));

    line = strtok(mail,"\n");
    while(line != NULL)
    {

        if(*line == '\t') // Tabbed headers
        {
            strcat(headers[index].value,line); // Concatenate the tabbed values
        }
        else
        {
            name = line;  
            value = strchr(line,':');  // Split the name value pairs.  
            if(value != NULL)
            {
                *value='\0';    // NULL the colon 
                value++;        // Move the pointer past the NULL character to separate the name and value
                index++;
                strcpy(headers[index].name,name);    // Copy the name to the data structure
                strcpy(headers[index].value,value);  // Copy the value to the data structure
            }

        }

        if(*line == '\r') // End of headers
            break;

        line = strtok(NULL,"\n"); // Get next header
        header_count = index;
    }

          Received = GetMailHeader("Received");

}

答案 6 :(得分:-2)

即使手动逐行解析这些标题也不难。一个正则表达式可以通过查看by\s+(\w)+\(来帮助那里。对于C ++,您可以尝试使用librarythat one

答案 7 :(得分:-2)

您是否考虑过使用regular expressions

Here是一个内部的,不可路由的地址范围列表。