suricata smtp协议解析源码注释五
发布时间
阅读量:
阅读量
一。MIME头部字段解析函数ProcessMimeHeaders
SMTPParse-> SMTPProcessRequest-> SMTPProcessCommandDATA-> MimeDecParseLine-> ProcessMimeEntity-> ProcessMimeHeaders
主要完成以下几个工作:
1。调研函数FindMimeHeader根据冒号查找头部字段name和value,如content-type等等,并对其进行存储。
2。解析完头部字段后,分析头部字段的重要内容,如文本格式,编码类型,是否包含附件,是否有boundary,是否嵌套格式等,并根据这些信息设置相应的标志位,在解析body数据时使用。
参数:buf 行数据,len:行数据长度
static int ProcessMimeHeaders(const uint8_t *buf, uint32_t len,
MimeDecParseState *state)
{
int ret = MIME_DEC_OK;
MimeDecField *field;
uint8_t *bptr = NULL, *rptr = NULL;
uint32_t blen = 0;
MimeDecEntity *entity = (MimeDecEntity *) state->stack->top->data;
//调用函数FindMimeHeader解析mime头部字段,并存储到mime_state中,
//解析完成后设置HEADER_DONE标志,如果成功解析到name则设置HEADER_START标志
//该函数解析完头部字段后,所有字段存放在MimeDecField 类型的链表中
/* Look for mime header in current line */
ret = FindMimeHeader(buf, len, state);
if (ret != MIME_DEC_OK) {
SCLogDebug("Error: FindMimeHeader() function failed: %d", ret);
return ret;
}
//下边的函数MimeDecFindField主要是变量头部字段链表,查找指定的字段名称,返回那个节点指针
//如果解析标志为HEADER_DONE则头部解析完成,分析头部重要字段如:content-type
//content-transer-code,content-dispositon,查找关键字符串如,"message/"
//"boundary"等,设置相关的编码标志
/* Post-processing after all headers done */
if (state->state_flag == HEADER_DONE) {
//查找编码并设置编码标志
/* First determine encoding by looking at Content-Transfer-Encoding */
field = MimeDecFindField(entity, CTNT_TRAN_STR);
if (field != NULL) {
/* Look for base64 */
if (FindBuffer(field->value, field->value_len, (const uint8_t *)BASE64_STR, strlen(BASE64_STR))) {
SCLogDebug("Base64 encoding found");
entity->ctnt_flags |= CTNT_IS_BASE64;
} else if (FindBuffer(field->value, field->value_len, (const uint8_t *)QP_STR, strlen(QP_STR))) {
/* Look for quoted-printable */
SCLogDebug("quoted-printable encoding found");
entity->ctnt_flags |= CTNT_IS_QP;
}
}
//查找是否有附件并设置附件标志
/* Check for file attachment in content disposition */
field = MimeDecFindField(entity, CTNT_DISP_STR);
if (field != NULL) {
bptr = FindMimeHeaderToken(field, "filename=", TOK_END_STR, &blen);
if (bptr != NULL) {
SCLogDebug("File attachment found in disposition");
entity->ctnt_flags |= CTNT_IS_ATTACHMENT;
/* Copy over using dynamic memory */
entity->filename = SCMalloc(blen);
if (unlikely(entity->filename == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
return MIME_DEC_ERR_MEM;
}
memcpy(entity->filename, bptr, blen);
entity->filename_len = blen;
}
}
//分析content-type的内容,查找boundary,message/等
/* Check for boundary, encapsulated message, and file name in Content-Type */
field = MimeDecFindField(entity, CTNT_TYPE_STR);
if (field != NULL) {
//如果找到boundary字符串,表示当前信件体包含子信件体,设置变量found_child为1
//解析body数据时会判断这个变量
/* Check if child entity boundary definition found */
bptr = FindMimeHeaderToken(field, BND_START_STR, TOK_END_STR, &blen);
if (bptr != NULL) {
state->found_child = 1;
//设置content标志,说明本信件体有多个部分组成
entity->ctnt_flags |= CTNT_IS_MULTIPART;
//boundary字符串长度检查
if (blen > (BOUNDARY_BUF - 2)) {
state->stack->top->data->anomaly_flags |= ANOM_LONG_BOUNDARY;
return MIME_DEC_ERR_PARSE;
}
//存储boundary字符串到本信件体,后续解析body数据时在其中查找boundary字符串
/* Store boundary in parent node */
state->stack->top->bdef = SCMalloc(blen);
if (unlikely(state->stack->top->bdef == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
return MIME_DEC_ERR_MEM;
}
memcpy(state->stack->top->bdef, bptr, blen);
state->stack->top->bdef_len = blen;
}
//判断是否有附件,有的话取出附件名称
/* Look for file name (if not already found) */
if (!(entity->ctnt_flags & CTNT_IS_ATTACHMENT)) {
bptr = FindMimeHeaderToken(field, "name=", TOK_END_STR, &blen);
if (bptr != NULL) {
SCLogDebug("File attachment found");
entity->ctnt_flags |= CTNT_IS_ATTACHMENT;
/* Copy over using dynamic memory */
entity->filename = SCMalloc(blen);
if (unlikely(entity->filename == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC, "memory allocation failed");
return MIME_DEC_ERR_MEM;
}
memcpy(entity->filename, bptr, blen);
entity->filename_len = blen;
}
}
/* Pull out short-hand content type */
entity->ctnt_type = GetToken(field->value, field->value_len, " \r\n;",
&rptr, &entity->ctnt_type_len);
if (entity->ctnt_type != NULL) {
/* Check for encapsulated message */
if (FindBuffer(entity->ctnt_type, entity->ctnt_type_len,
(const uint8_t *)MSG_STR, strlen(MSG_STR)))
{
//这块在查找头字段中的值类型字符串"message/",这个格式的邮件
//还没研究过,应该也简单
SCLogDebug("Found encapsulated message entity");
entity->ctnt_flags |= CTNT_IS_ENV;
/* Create and push child to stack */
MimeDecEntity *child = MimeDecAddEntity(entity);
if (child == NULL)
return MIME_DEC_ERR_MEM;
child->ctnt_flags |= (CTNT_IS_ENCAP | CTNT_IS_MSG);
PushStack(state->stack);
state->stack->top->data = child;
/* Mark as encapsulated child */
state->stack->top->is_encap = 1;
/* Ready to parse headers */
state->state_flag = HEADER_READY;
} else if (FindBuffer(entity->ctnt_type, entity->ctnt_type_len,
(const uint8_t *)MULTIPART_STR, strlen(MULTIPART_STR)))
{
/* Check for multipart */
SCLogDebug("Found multipart entit");
entity->ctnt_flags |= CTNT_IS_MULTIPART;
} else if (FindBuffer(entity->ctnt_type, entity->ctnt_type_len,
(const uint8_t *)TXT_STR, strlen(TXT_STR)))
{
/* Check for plain text */
SCLogDebug("Found plain text entity");
entity->ctnt_flags |= CTNT_IS_TEXT;
} else if (FindBuffer(entity->ctnt_type, entity->ctnt_type_len,
(const uint8_t *)HTML_STR, strlen(HTML_STR)))
{
/* Check for html */
SCLogDebug("Found html entity");
entity->ctnt_flags |= CTNT_IS_HTML;
}
//以上最近的三个部分都是判断conten-type的类型值,并设置标志,解码时使用
}
}
//解析msgid,如果有的话
/* Store pointer to Message-ID */
field = MimeDecFindField(entity, MSG_ID_STR);
if (field != NULL) {
entity->msg_id = field->value;
entity->msg_id_len = field->value_len;
}
//因为头部字段解析完成了,所以后续的数据就是body数据了,设置dody_begin=1
/* Flag beginning of body */
state->body_begin = 1;
state->body_end = 0;
}
return ret;
}
二。解析mime头部字段的函数FindMimeHeader
SMTPParse-> SMTPProcessRequest-> SMTPProcessCommandDATA-> MimeDecParseLine-> ProcessMimeEntity-> ProcessMimeHeaders->FindMimeHeader
上边的函数解析头部字段调用了这个函数, 查找存储等, 这个函数主要就是在一行数据中查找冒号, 冒号前为字段名称, 冒号后为字段值, 字段值可能分为多行传输, 所以字段值的多行作为多个数据片段存放在一个链表中,根据解析状态设置解析标志HEADER_DONE,HEADER_STARTED,BODY_STARTED。
static int FindMimeHeader(const uint8_t *buf, uint32_t blen,
MimeDecParseState *state)
{
int ret = MIME_DEC_OK;
//分配存放头部字段名称和值
uint8_t *hname, *hval = NULL;
//头部字段值链表的节点类型
DataValue *dv;
uint32_t hlen, vlen;
//两个临时变量,解析过程中设置和判断
//上个name结束设置finish_header,遇到新的name设置new_header
int finish_header = 0, new_header = 0;
//这个就是从配置文件里读出来的mime解析相关的配置,例如是否设置了解析mime等
MimeDecConfig *mdcfg = MimeDecGetConfig();
//头部字段解析:
//因为只有在本次数据中找到冒号才表示解析到一个头部字段的name,才能表示上一个name解析完成
//才能对上次的name和value进行存储,理解这个解析代过程很重要重要。
//在行数据中查找冒号并解析出冒号前的字段名称
/* Find first header */
hname = FindMimeHeaderStart(buf, blen, &hlen);
if (hname != NULL) {
//检查字段名称长度(最大76包含冒号),长度不合法时设置异常标志,跟踪解析状态
/* Warn and track but don't do anything yet */
if (hlen > MAX_HEADER_NAME) {
state->stack->top->data->anomaly_flags |= ANOM_LONG_HEADER_NAME;
state->msg->anomaly_flags |= ANOM_LONG_HEADER_NAME;
SCLogDebug("Error: Header name exceeds limit (%u > %u)",
hlen, MAX_HEADER_NAME);
}
//根据名称及名称长度,解析出后冒号后边的值
/* Value starts after 'header:' (normalize spaces) */
hval = hname + hlen + 1;
if (hval - buf >= (int)blen) {
SCLogDebug("No Header value found");
hval = NULL;
} else {
while (hval[0] == ' ') {
/* If last character before end of bounds, set to NULL */
if (hval - buf >= (int)blen - 1) {
SCLogDebug("No Header value found");
hval = NULL;
break;
}
hval++;
}
}
//如果设置了头部解析开始标志,则标识后续数据应该出现冒号分割的头部信息即字段名称和值,
//所以如果本次解析出了name,则表示遇到了新的name,那么上次的name解析结束,于是设置
//finish_header=1,本函数后边代码使用
/* If new header found, then previous header is finished */
if (state->state_flag == HEADER_STARTED) {
finish_header = 1;
}
//因为解析到了name,说明发现了新name,后续数据则是这个新的name的开始,
//包含其可能不能一次传输完成的name的一部分,或者其值的一部分数据或者全部
//总之,后续数据属于这个新name的,于是设置new_header=1,设置HEADER_STARTED标志
/* Now process new header */
new_header = 1;
/* Must wait for next line to determine if finished */
state->state_flag = HEADER_STARTED;
} else if (blen == 0) {
//如果数据长度为0,则是空行,意味着头部字段结束,头部字段和数据之间以空行分割。
/* Found body */
/* No more headers */
state->state_flag = HEADER_DONE;
//头部字段解析结束,上次的name和value还没有保存,因为只有下次解析出现name时,
//才能表示上次name的信息结束了,
//于是设置该标志,后边代码判断后,保存上次头部字段信息。
finish_header = 1;
SCLogDebug("All Header processing finished");
} else if (state->state_flag == HEADER_STARTED) {
//本段代码含义:如果没有找到name,但是设置了HEADER_STARTED标志,
//那说明本次数据是上次name的value的一部分,这个value 是分成多个行传输的,
//于是分配数据节点,复制数据
/* Found multi-line value (ie. Received header) */
/* If max header value exceeded, flag it */
vlen = blen;
if ((mdcfg != NULL) && (state->hvlen + vlen > mdcfg->header_value_depth)) {
SCLogDebug("Error: Header value of length (%u) is too long",
state->hvlen + vlen);
vlen = mdcfg->header_value_depth - state->hvlen;
state->stack->top->data->anomaly_flags |= ANOM_LONG_HEADER_VALUE;
state->msg->anomaly_flags |= ANOM_LONG_HEADER_VALUE;
}
if (vlen > 0) {
//分配数据节点,复制数据,这个数据是上次找到name的value的一部分,
//后续会合并到一起存储
dv = AddDataValue(state->hvalue);
if (dv == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "AddDataValue() function failed");
return MIME_DEC_ERR_MEM;
}
if (state->hvalue == NULL) {
state->hvalue = dv;
}
dv->value = SCMalloc(vlen);
if (unlikely(dv->value == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
return MIME_DEC_ERR_MEM;
}
memcpy(dv->value, buf, vlen);
dv->value_len = vlen;
state->hvlen += vlen;
}
} else {
//如果在本次数据中没有找到name,解析标志不是HEADER_STARTED,
//此时的解析标志只能是HEADER_READY,因为只有这两个标志才能进入本函数。
//重要说明:
//HEADER_READY在解析遇到boundary时才会设置,也就是说,本来遇到boundary了,
//后续数据应该可以解析出name,但是数据中并没有冒号分割的name,那么这个数据
//就认为是没有头部字段的body数据。
//于是设置解析标志为BODY_STARTED,(mime_state)state->body_begin = 1,
//并调用ProcessBodyLine处理本次的body数据。
/* Likely a body without headers */
SCLogDebug("No headers found");
state->state_flag = BODY_STARTED;
/* Flag beginning of body */
state->body_begin = 1;
state->body_end = 0;
ret = ProcessBodyLine(buf, blen, state);
if (ret != MIME_DEC_OK) {
SCLogDebug("Error: ProcessBodyLine() function failed");
return ret;
}
}
//这个局部变量为1,说明找到新name,name开始,上次解析的name结束,
//所以要保存上次的name和value。
/* If we need to finish a header, then do so below and then cleanup */
if (finish_header) {
/* Store the header value */
//下边这个函数主要就是变量mime_state->hvalue链表,将所有数据片段合并起来,
//重新分配内存,存储到头部字段类型结构体MimeDecField中,然后将这个结构体连接
//到(MimeDecEntity)entity->field_list中,意思就是将该name的所有value合并成一个,
//然后插入该信件体的头部字段链表中。
ret = StoreMimeHeader(state);
if (ret != MIME_DEC_OK) {
SCLogDebug("Error: StoreMimeHeader() function failed");
return ret;
}
}
/* When next header is found, we always create a new one */
if (new_header) {
//如果遇到新name则分配内存复制临时变量hname到(mime_state)state->hname中,
/* Copy name and value to state */
state->hname = SCMalloc(hlen);
if (unlikely(state->hname == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
return MIME_DEC_ERR_MEM;
}
memcpy(state->hname, hname, hlen);
state->hlen = hlen;
if (state->hvalue != NULL) {
SCLogDebug("Error: Parser failed due to unexpected header "
"value");
return MIME_DEC_ERR_DATA;
}
//如果name对应的value也解析到了,那么将该value存储到
//(mime_state)state->hvalue临时链表中,之所以说它是临时链表,
//是因为hvalue只存放在解析过程中头部字段name的value的所有片段,
//这些片段同属与一个name,当遇到新的name或者遇到没有头部字段的body数据时,
//说明一个name的value都完整了,就会将其合并后存储到当前信件体的头部字段链表中,
//并销毁(mime_state)state->hvalue链表,当解析新的name时,
//其value的数据片段又会存储到这个临时链表中。
if (hval != NULL) {
/* If max header value exceeded, flag it */
vlen = blen - (hval - buf);
if ((mdcfg != NULL) && (state->hvlen + vlen > mdcfg->header_value_depth)) {
SCLogDebug("Error: Header value of length (%u) is too long",
state->hvlen + vlen);
vlen = mdcfg->header_value_depth - state->hvlen;
state->stack->top->data->anomaly_flags |= ANOM_LONG_HEADER_VALUE;
state->msg->anomaly_flags |= ANOM_LONG_HEADER_VALUE;
}
if (vlen > 0) {
state->hvalue = AddDataValue(NULL);
if (state->hvalue == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "AddDataValue() function failed");
return MIME_DEC_ERR_MEM;
}
state->hvalue->value = SCMalloc(vlen);
if (unlikely(state->hvalue->value == NULL)) {
SCLogError(SC_ERR_MEM_ALLOC, "Memory allocation failed");
return MIME_DEC_ERR_MEM;
}
memcpy(state->hvalue->value, hval, vlen);
state->hvalue->value_len = vlen;
state->hvlen += vlen;
}
}
}
return ret;
}
全部评论 (0)
还没有任何评论哟~
