Advertisement

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)

还没有任何评论哟~