struct packet_stream* packet_stream_alloc(uint32_t start_seq, uint32_t start_ack, int direction, uint32_t max_buff_size, struct conntrack_entry *ce, unsigned int flags) { struct packet_stream *res = malloc(sizeof(struct packet_stream)); if (!res) { pom_oom(sizeof(struct packet_stream)); return NULL; } memset(res, 0, sizeof(struct packet_stream)); int rev_direction = POM_DIR_REVERSE(direction); res->cur_seq[direction] = start_seq; res->cur_ack[direction] = start_ack; res->cur_seq[rev_direction] = start_ack; res->cur_ack[rev_direction] = start_seq; res->max_buff_size = max_buff_size; res->ce = ce; if (pthread_mutex_init(&res->lock, NULL)) { pomlog(POMLOG_ERR "Error while initializing stream lock : %s", pom_strerror(errno)); free(res); return NULL; } if (pthread_mutex_init(&res->wait_lock, NULL)) { pomlog(POMLOG_ERR "Error while initializing stream wait lock : %s", pom_strerror(errno)); free(res); return NULL; } res->flags = flags; debug_stream("thread %p, entry %p, allocated, start_seq %u, start_ack %u, direction %u", pthread_self(), res, start_seq, start_ack, direction); return res; }
int stream_fill_gap(struct stream *stream, struct stream_pkt *p, uint32_t gap, int reverse_dir) { if (gap > stream->max_buff_size) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : gap of %u too big. not filling", pthread_self(), stream, pom_ptime_sec(p->pkt->ts), pom_ptime_usec(p->pkt->ts), p->seq, p->ack, gap); return POM_OK; } if (!reverse_dir) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : filling gap of %u in forward direction", pthread_self(), stream, pom_ptime_sec(p->pkt->ts), pom_ptime_usec(p->pkt->ts), p->seq, p->ack, gap); } else { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : filling gap of %u in reverse direction", pthread_self(), stream, pom_ptime_sec(p->pkt->ts), pom_ptime_usec(p->pkt->ts), p->seq, p->ack, gap); } uint32_t gap_step = gap; if (gap_step > STREAM_GAP_STEP_MAX) gap_step = STREAM_GAP_STEP_MAX; void *zero = malloc(gap_step); if (!zero) { pom_oom(gap_step); return POM_ERR; } memset(zero, 0, gap_step); struct proto_process_stack *s = &p->stack[p->stack_index]; uint32_t plen_old = s->plen; void *pload_old = s->pload; int dir_old = s->direction; int dir_new = s->direction; if (reverse_dir) dir_new = POM_DIR_REVERSE(s->direction); uint32_t pos; for (pos = 0; pos < gap; pos += gap_step) { if (pos + gap_step < gap) s->plen = gap_step; else s->plen = gap - pos; s->pload = zero; s->direction = dir_new; int res = stream->handler(stream->ce, p->pkt, p->stack, p->stack_index); if (res == PROTO_ERR) break; } free(zero); s->pload = pload_old; s->plen = plen_old; s->direction = dir_old; return POM_OK; }
static int packet_stream_is_packet_next(struct packet_stream *stream, struct packet_stream_pkt *pkt, int direction) { int rev_direction = POM_DIR_REVERSE(direction); uint32_t cur_seq = stream->cur_seq[direction]; uint32_t rev_seq = stream->cur_seq[rev_direction]; // Check that there is no gap with what we expect if ((cur_seq < pkt->seq && pkt->seq - cur_seq < PACKET_HALF_SEQ) || (cur_seq > pkt->seq && cur_seq - pkt->seq > PACKET_HALF_SEQ)) { // There is a gap debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : GAP : cur_seq %u, rev_seq %u", pthread_self(), stream, pkt->pkt->ts.tv_sec, pkt->pkt->ts.tv_usec, pkt->seq, pkt->ack, cur_seq, rev_seq); return 0; } if (stream->flags & PACKET_FLAG_STREAM_BIDIR) { // There is additional checking for bi dir stream if ((rev_seq < pkt->ack && pkt->ack - rev_seq < PACKET_HALF_SEQ) || (rev_seq > pkt->ack && rev_seq - pkt->ack > PACKET_HALF_SEQ)) { // The host processed data in the reverse direction which we haven't processed yet if (stream->t) conntrack_timer_queue(stream->t, stream->rev_dir_timeout); debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : reverse missing : cur_seq %u, rev_seq %u", pthread_self(), stream, pkt->pkt->ts.tv_sec, pkt->pkt->ts.tv_usec, pkt->seq, pkt->ack, cur_seq, rev_seq); return 0; } } // This packet can be processed debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : is next : cur_seq %u, rev_seq %u", pthread_self(), stream, pkt->pkt->ts.tv_sec, pkt->pkt->ts.tv_usec, pkt->seq, pkt->ack, cur_seq, rev_seq); return 1; }
static int analyzer_smtp_event_process_begin(struct event *evt, void *obj, struct proto_process_stack *stack, unsigned int stack_index) { struct analyzer *analyzer = obj; struct analyzer_smtp_priv *apriv = analyzer->priv; struct proto_process_stack *s = &stack[stack_index]; if (!s->ce) return POM_ERR; // Only process stuff if we have the DATA event or if we already have an event struct event_reg *evt_reg = event_get_reg(evt); struct data *evt_data = event_get_data(evt); struct analyzer_smtp_ce_priv *cpriv = conntrack_get_priv(s->ce, analyzer); // It's expected that an SMTP connection will always contain at least one message // So we always create the cpriv and event, no matter what if (!cpriv) { cpriv = malloc(sizeof(struct analyzer_smtp_ce_priv)); if (!cpriv) { pom_oom(sizeof(struct analyzer_smtp_ce_priv)); return POM_ERR; } memset(cpriv, 0, sizeof(struct analyzer_smtp_ce_priv)); if (conntrack_add_priv(s->ce, analyzer, cpriv, analyzer_smtp_ce_priv_cleanup) != POM_OK) { free(cpriv); return POM_ERR; } } if (!cpriv->evt_msg) { cpriv->evt_msg = event_alloc(apriv->evt_msg); if (!cpriv->evt_msg) return POM_ERR; } struct data *msg_data = event_get_data(cpriv->evt_msg); if (evt_reg == apriv->evt_cmd) { if (!cpriv->common_data_fetched) analyzer_smtp_event_fetch_common_data(cpriv, stack, stack_index, POM_DIR_REVERSE(s->direction)); // Process commands // A message was being transmitted and we recevied a new command if (event_is_started(cpriv->evt_msg)) { event_process_end(cpriv->evt_msg); cpriv->evt_msg = NULL; } char *cmd = PTYPE_STRING_GETVAL(evt_data[proto_smtp_cmd_name].value); if (!cmd) return POM_OK; char *arg = PTYPE_STRING_GETVAL(evt_data[proto_smtp_cmd_arg].value); if (arg) { while (*arg == ' ') arg++; } if (!strcasecmp(cmd, "MAIL")) { if (strncasecmp(arg, "FROM:", strlen("FROM:"))) { pomlog(POMLOG_DEBUG "Unparseable MAIL command"); return POM_OK; } arg += strlen("FROM:"); while (*arg == ' ') arg++; if (*arg == '<') arg++; size_t len; char *end = strchr(arg, '>'); if (end) len = end - arg; else len = strlen(arg); PTYPE_STRING_SETVAL_N(msg_data[analyzer_smtp_msg_from].value, arg, len); data_set(msg_data[analyzer_smtp_msg_from]); cpriv->last_cmd = analyzer_smtp_last_cmd_mail_from; } else if (!strcasecmp(cmd, "RCPT")) { if (strncasecmp(arg, "TO:", strlen("TO:"))) { pomlog(POMLOG_DEBUG "Unparseable RCPT command"); return POM_OK; } arg += strlen("TO:"); while (*arg == ' ') arg++; if (*arg == '<') arg++; size_t len; char *end = strchr(arg, '>'); if (end) len = end - arg; else len = strlen(arg); struct ptype *to = ptype_alloc("string"); if (!to) return POM_ERR; PTYPE_STRING_SETVAL_N(to, arg, len); if (data_item_add_ptype(msg_data, analyzer_smtp_msg_to, strdup("to"), to) != POM_OK) { ptype_cleanup(to); return POM_ERR; } cpriv->last_cmd = analyzer_smtp_last_cmd_rcpt_to; } else if (!strcasecmp(cmd, "DATA")) { cpriv->last_cmd = analyzer_smtp_last_cmd_data; if (!event_is_started(cpriv->evt_msg)) { analyzer_smtp_event_fill_common_data(cpriv, msg_data); event_process_begin(cpriv->evt_msg, stack, stack_index, event_get_timestamp(evt)); } else { pomlog(POMLOG_DEBUG "Message event already started !"); } } else if (!strcasecmp(cmd, "RSET")) { // Cleanup the event event_cleanup(cpriv->evt_msg); cpriv->evt_msg = NULL; cpriv->last_cmd = analyzer_smtp_last_cmd_other; } else if (!strcasecmp(cmd, "HELO") || !strcasecmp(cmd, "EHLO")) { if (cpriv->client_hello) { pomlog(POMLOG_DEBUG "We already have a client hello !"); free(cpriv->client_hello); } cpriv->client_hello = strdup(arg); if (!cpriv->client_hello) { pom_oom(strlen(arg) + 1); return POM_ERR; } cpriv->last_cmd = analyzer_smtp_last_cmd_other; } else if (!strcasecmp(cmd, "AUTH")) { if (!strncasecmp(arg, "PLAIN", strlen("PLAIN"))) { arg += strlen("PLAIN"); while (*arg == ' ') arg++; if (cpriv->evt_auth) { event_process_end(cpriv->evt_auth); cpriv->evt_auth = NULL; } if (strlen(arg)) { if (analyzer_smtp_parse_auth_plain(apriv, cpriv, arg) == POM_OK) { event_process_begin(cpriv->evt_auth, stack, stack_index, event_get_timestamp(evt)); cpriv->last_cmd = analyzer_smtp_last_cmd_auth_plain_creds; } } else { cpriv->last_cmd = analyzer_smtp_last_cmd_auth_plain; } } else if (!strncasecmp(arg, "LOGIN", strlen("LOGIN"))) { arg += strlen("LOGIN"); while (*arg == ' ') arg++; if (cpriv->evt_auth) { event_process_end(cpriv->evt_auth); cpriv->evt_auth = NULL; } cpriv->evt_auth = event_alloc(apriv->evt_auth); if (!cpriv->evt_auth) return POM_ERR; struct data *auth_data = event_get_data(cpriv->evt_auth); analyzer_smtp_event_fill_common_data(cpriv, auth_data); // Set the authentication type PTYPE_STRING_SETVAL(auth_data[analyzer_smtp_auth_type].value, "LOGIN"); data_set(auth_data[analyzer_smtp_auth_type]); if (strlen(arg)) { char *username = NULL; size_t out_len = 0; struct ptype *username_pt = NULL; if (decoder_decode_simple("base64", arg, strlen(arg), &username, &out_len) == POM_OK) { username_pt = ptype_alloc("string"); if (username_pt) { PTYPE_STRING_SETVAL_P(username_pt, username); if (data_item_add_ptype(auth_data, analyzer_smtp_auth_params, strdup("username"), username_pt) != POM_OK) { ptype_cleanup(username_pt); event_cleanup(cpriv->evt_auth); cpriv->evt_auth = NULL; username_pt = NULL; } } else { free(username); } } if (!username_pt) { cpriv->last_cmd = analyzer_smtp_last_cmd_other; event_process_begin(cpriv->evt_auth, stack, stack_index, event_get_timestamp(evt)); } } else { cpriv->last_cmd = analyzer_smtp_last_cmd_auth_login; } } } else if (cpriv->last_cmd == analyzer_smtp_last_cmd_auth_plain) { // We are expecting the credentials right now if (analyzer_smtp_parse_auth_plain(apriv, cpriv, cmd) == POM_OK) { event_process_begin(cpriv->evt_auth, stack, stack_index, event_get_timestamp(evt)); cpriv->last_cmd = analyzer_smtp_last_cmd_auth_plain_creds; } else { cpriv->last_cmd = analyzer_smtp_last_cmd_other; } } else if (cpriv->last_cmd == analyzer_smtp_last_cmd_auth_login) { char *username = NULL; size_t out_len = 0; struct ptype *username_pt = NULL; if (decoder_decode_simple("base64", cmd, strlen(cmd), &username, &out_len) == POM_OK) { username_pt = ptype_alloc("string"); if (username_pt) { PTYPE_STRING_SETVAL_P(username_pt, username); struct data *auth_data = event_get_data(cpriv->evt_auth); if (data_item_add_ptype(auth_data, analyzer_smtp_auth_params, strdup("username"), username_pt) != POM_OK) { ptype_cleanup(username_pt); event_process_end(cpriv->evt_auth); cpriv->evt_auth = NULL; username_pt = NULL; } } else { free(username); } } if (!username_pt) { cpriv->last_cmd = analyzer_smtp_last_cmd_other; } else { event_process_begin(cpriv->evt_auth, stack, stack_index, event_get_timestamp(evt)); cpriv->last_cmd = analyzer_smtp_last_cmd_auth_login_user; } } else if (cpriv->last_cmd == analyzer_smtp_last_cmd_auth_login_user) { char *password = NULL; size_t out_len = 0; struct ptype *password_pt = NULL; if (decoder_decode_simple("base64", cmd, strlen(cmd), &password, &out_len) == POM_OK) { password_pt = ptype_alloc("string"); if (password_pt) { PTYPE_STRING_SETVAL_P(password_pt, password); struct data *auth_data = event_get_data(cpriv->evt_auth); if (data_item_add_ptype(auth_data, analyzer_smtp_auth_params, strdup("password"), password_pt) != POM_OK) { ptype_cleanup(password_pt); event_process_end(cpriv->evt_auth); cpriv->evt_auth = NULL; password_pt = NULL; } } else { free(password); } } if (!password_pt) { cpriv->last_cmd = analyzer_smtp_last_cmd_other; } else { cpriv->last_cmd = analyzer_smtp_last_cmd_auth_login_pass; } } else { cpriv->last_cmd = analyzer_smtp_last_cmd_other; } } else if (evt_reg == apriv->evt_reply) { if (!cpriv->common_data_fetched) analyzer_smtp_event_fetch_common_data(cpriv, stack, stack_index, s->direction); // Process replies uint16_t code = *PTYPE_UINT16_GETVAL(evt_data[proto_smtp_reply_code].value); switch (cpriv->last_cmd) { default: case analyzer_smtp_last_cmd_other: if (code == 220 && evt_data[proto_smtp_reply_text].items && evt_data[proto_smtp_reply_text].items->value) { // STARTTLS returns 220 as well so ignore extra code 220 if (!cpriv->server_hello) { char *helo = PTYPE_STRING_GETVAL(evt_data[proto_smtp_reply_text].items->value); cpriv->server_hello = strdup(helo); if (!cpriv->server_hello) { pom_oom(strlen(helo) + 1); return POM_ERR; } } } break; case analyzer_smtp_last_cmd_mail_from: if (code != 250) { // FROM is invalid data_unset(msg_data[analyzer_smtp_msg_from]); } break; case analyzer_smtp_last_cmd_rcpt_to: // For now just don't do anything // It's best to keep a destination in there even if it's invalid or denied break; case analyzer_smtp_last_cmd_data: if (code == 354) { // The message is starting, keep last_cmd intact return POM_OK; } // Message is over (if ever transmited) if (event_is_started(cpriv->evt_msg)) { struct data *msg_data = event_get_data(cpriv->evt_msg); PTYPE_UINT16_SETVAL(msg_data[analyzer_smtp_msg_result].value, code); data_set(msg_data[analyzer_smtp_msg_result]); event_process_end(cpriv->evt_msg); cpriv->evt_msg = NULL; } break; case analyzer_smtp_last_cmd_auth_plain: case analyzer_smtp_last_cmd_auth_login: case analyzer_smtp_last_cmd_auth_login_user: // Check if authentication phase can continue if (code == 334) { // Don't reset cpriv->last_cmd return POM_OK; } else { struct data *evt_data = event_get_data(cpriv->evt_auth); PTYPE_BOOL_SETVAL(evt_data[analyzer_smtp_auth_success].value, 0); data_set(evt_data[analyzer_smtp_auth_success]); event_process_end(cpriv->evt_auth); cpriv->evt_auth = NULL; } break; case analyzer_smtp_last_cmd_auth_plain_creds: case analyzer_smtp_last_cmd_auth_login_pass: { // We just processed the credentials struct data *auth_data = event_get_data(cpriv->evt_auth); char success = 0; if (code == 235) success = 1; PTYPE_BOOL_SETVAL(auth_data[analyzer_smtp_auth_success].value, success); data_set(auth_data[analyzer_smtp_auth_success]); event_process_end(cpriv->evt_auth); cpriv->evt_auth = NULL; break; } } cpriv->last_cmd = analyzer_smtp_last_cmd_other; } return POM_OK; }
static int proto_smtp_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; struct proto_process_stack *s_next = &stack[stack_index + 1]; if (conntrack_get_unique_from_parent(stack, stack_index) != POM_OK) { pomlog(POMLOG_ERR "Could not get conntrack entry"); return PROTO_ERR; } // There should no need to keep the lock here since we are in the packet_stream lock from proto_tcp conntrack_unlock(s->ce); struct proto_smtp_priv *ppriv = proto_priv; struct proto_smtp_conntrack_priv *priv = s->ce->priv; if (!priv) { priv = malloc(sizeof(struct proto_smtp_conntrack_priv)); if (!priv) { pom_oom(sizeof(struct proto_smtp_conntrack_priv)); return PROTO_ERR; } memset(priv, 0, sizeof(struct proto_smtp_conntrack_priv)); priv->parser[POM_DIR_FWD] = packet_stream_parser_alloc(SMTP_MAX_LINE, PACKET_STREAM_PARSER_FLAG_TRIM); if (!priv->parser[POM_DIR_FWD]) { free(priv); return PROTO_ERR; } priv->parser[POM_DIR_REV] = packet_stream_parser_alloc(SMTP_MAX_LINE, PACKET_STREAM_PARSER_FLAG_TRIM); if (!priv->parser[POM_DIR_REV]) { packet_stream_parser_cleanup(priv->parser[POM_DIR_FWD]); free(priv); return PROTO_ERR; } priv->server_direction = POM_DIR_UNK; s->ce->priv = priv; } if (priv->flags & PROTO_SMTP_FLAG_INVALID) return PROTO_OK; struct packet_stream_parser *parser = priv->parser[s->direction]; if (packet_stream_parser_add_payload(parser, s->pload, s->plen) != POM_OK) return PROTO_ERR; char *line = NULL; size_t len = 0; while (1) { // Some check to do prior to parse the payload if (s->direction == POM_DIR_REVERSE(priv->server_direction)) { if (priv->flags & PROTO_SMTP_FLAG_STARTTLS) { // Last command was a STARTTLS command, this is the TLS negociation // Since we can't parse this, mark it as invalid priv->flags |= PROTO_SMTP_FLAG_INVALID; return PROTO_OK; } else if (priv->flags & PROTO_SMTP_FLAG_CLIENT_DATA) { // We are receiving payload data, check where the end is void *pload; size_t plen; packet_stream_parser_get_remaining(parser, &pload, &plen); if (!plen) return PROTO_OK; // Look for the "<CR><LF>.<CR><LF>" sequence if (priv->data_end_pos > 0) { // The previous packet ended with something that might be the final sequence // Check if we have the rest int i, found = 1; for (i = 0; i < PROTO_SMTP_DATA_END_LEN - priv->data_end_pos && i <= plen; i++) { if (*(char*)(pload + i) != PROTO_SMTP_DATA_END[priv->data_end_pos + i]) { found = 0; break; } } if (found) { // If we have already processed the dot after <CR><LF> there is no way to remove it // Thus we mark this connection as invalid. Most MTA will send at worst the last // 3 bytes of the end sequence in a sequence packet if (i != plen || (priv->data_end_pos >= 2 && plen < 3)) { pomlog(POMLOG_DEBUG "The final line was not at the of a packet as expected !"); priv->flags |= PROTO_SMTP_FLAG_INVALID; event_process_end(priv->data_evt); priv->data_evt = NULL; return PROTO_OK; } s_next->pload = pload; s_next->plen = plen - PROTO_SMTP_DATA_END_LEN + 2; // The last line return is part of the payload priv->flags |= PROTO_SMTP_FLAG_CLIENT_DATA_END; priv->flags &= ~PROTO_SMTP_FLAG_CLIENT_DATA; priv->data_end_pos = 0; return PROTO_OK; } priv->data_end_pos = 0; } char *dotline = pom_strnstr(pload, PROTO_SMTP_DATA_END, plen); if (dotline) { if (pload + plen - PROTO_SMTP_DATA_END_LEN != dotline) { pomlog(POMLOG_DEBUG "The final line was not at the of a packet as expected !"); priv->flags |= PROTO_SMTP_FLAG_INVALID; event_process_end(priv->data_evt); priv->data_evt = NULL; return PROTO_OK; } s_next->pload = pload; s_next->plen = plen - PROTO_SMTP_DATA_END_LEN + 2; // The last line return is part of the payload priv->flags |= PROTO_SMTP_FLAG_CLIENT_DATA_END; priv->flags &= ~PROTO_SMTP_FLAG_CLIENT_DATA; } else { // Check if the end of the payload contains part of the "<CR><LF>.<CR><LF>" sequence int i, found = 0; for (i = 1 ; (i < PROTO_SMTP_DATA_END_LEN) && (i <= plen); i++) { if (!memcmp(pload + plen - i, PROTO_SMTP_DATA_END, i)) { found = 1; break; } } if (found) priv->data_end_pos = i; s_next->pload = pload; s_next->plen = plen; } return PROTO_OK; } } // Process commands if (packet_stream_parser_get_line(parser, &line, &len) != POM_OK) return PROTO_ERR; if (!line) return PROTO_OK; if (!len) // Probably a missed packet return PROTO_OK; // Try to find the server direction if (priv->server_direction == POM_DIR_UNK) { unsigned int code = atoi(line); if (code > 0) { priv->server_direction = s->direction; } else { priv->server_direction = POM_DIR_REVERSE(s->direction); } } if (s->direction == priv->server_direction) { // Parse the response code and generate the event if ((len < 5) || // Server response is 3 digit error code, a space or hyphen and then at least one letter of text (line[3] != ' ' && line[3] != '-')) { pomlog(POMLOG_DEBUG "Too short or invalid response from server"); priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } int code = atoi(line); if (code == 0) { pomlog(POMLOG_DEBUG "Invalid response from server"); priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } if (event_has_listener(ppriv->evt_reply)) { struct data *evt_data = NULL; if (priv->reply_evt) { evt_data = event_get_data(priv->reply_evt); uint16_t cur_code = *PTYPE_UINT16_GETVAL(evt_data[proto_smtp_reply_code].value); if (cur_code != code) { pomlog(POMLOG_WARN "Multiline code not the same as previous line : %hu -> %hu", cur_code, code); event_process_end(priv->reply_evt); priv->reply_evt = NULL; } } if (!priv->reply_evt) { priv->reply_evt = event_alloc(ppriv->evt_reply); if (!priv->reply_evt) return PROTO_ERR; evt_data = event_get_data(priv->reply_evt); PTYPE_UINT16_SETVAL(evt_data[proto_smtp_reply_code].value, code); data_set(evt_data[proto_smtp_reply_code]); } if (len > 4) { struct ptype *txt = ptype_alloc("string"); if (!txt) return PROTO_ERR; PTYPE_STRING_SETVAL_N(txt, line + 4, len - 4); if (data_item_add_ptype(evt_data, proto_smtp_reply_text, strdup("text"), txt) != POM_OK) return PROTO_ERR; } if (!event_is_started(priv->reply_evt)) event_process_begin(priv->reply_evt, stack, stack_index, p->ts); } if (line[3] != '-') { // Last line in the response if (priv->reply_evt) { event_process_end(priv->reply_evt); priv->reply_evt = NULL; } } if (priv->flags & PROTO_SMTP_FLAG_STARTTLS) { // The last command was STARTTLS priv->flags &= ~PROTO_SMTP_FLAG_STARTTLS; if (code == 220) { // TLS has the go, we can't parse from now so mark as invalid priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } } } else { // Client command if (len < 4) { // Client commands are at least 4 bytes long pomlog(POMLOG_DEBUG "Too short or invalid query from client"); priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } // Make sure it's a command by checking it's at least a four letter word int i; for (i = 0; i < 4; i++) { // In some case it can also be a base64 encoded word if (! ((line[i] >= 'A' && line[i] <= 'Z') || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= '0' && line [i] <= '9') || line[i] == '=')) break; } if ((i < 4)) { pomlog(POMLOG_DEBUG "Recieved invalid client command"); priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } if (!strncasecmp(line, "DATA", strlen("DATA")) && len == strlen("DATA")) { priv->flags |= PROTO_SMTP_FLAG_CLIENT_DATA; } else if (!strncasecmp(line, "STARTTLS", strlen("STARTTLS")) && len == strlen("STARTTLS")) { priv->flags |= PROTO_SMTP_FLAG_STARTTLS; } if (event_has_listener(ppriv->evt_cmd)) { struct event *evt = event_alloc(ppriv->evt_cmd); if (!evt) return PROTO_ERR; size_t cmdlen = len; char *space = memchr(line, ' ', len); if (space) cmdlen = space - line; struct data *evt_data = event_get_data(evt); PTYPE_STRING_SETVAL_N(evt_data[proto_smtp_cmd_name].value, line, cmdlen); data_set(evt_data[proto_smtp_cmd_name]); if (space) { PTYPE_STRING_SETVAL_N(evt_data[proto_smtp_cmd_arg].value, space + 1, len - 1 - cmdlen); data_set(evt_data[proto_smtp_cmd_arg]); } if (priv->flags & PROTO_SMTP_FLAG_CLIENT_DATA) { // The event ends at the end of the message priv->data_evt = evt; return event_process_begin(evt, stack, stack_index, p->ts); } else { return event_process(evt, stack, stack_index, p->ts); } } } } return PROTO_OK; }
struct packet_stream_pkt *packet_stream_get_next(struct packet_stream *stream, unsigned int *direction) { struct packet_stream_pkt *res = NULL; int dirs[2] = { *direction, POM_DIR_REVERSE(*direction) }; int i, cur_dir; for (i = 0; i < 2 && !res; i++) { *direction = dirs[i]; cur_dir = *direction; while (stream->head[cur_dir]) { res = stream->head[cur_dir]; if (!packet_stream_is_packet_next(stream, res, cur_dir)) { res = NULL; break; } uint32_t cur_seq = stream->cur_seq[cur_dir]; uint32_t seq = res->seq; // Check for duplicate bytes if (cur_seq != seq) { if (packet_stream_is_packet_old_dupe(stream, res, cur_dir)) { // Packet is a duplicate, remove it stream->head[cur_dir] = res->next; if (res->next) { res->next->prev = NULL; } else { stream->tail[cur_dir] = NULL; } if (res->prev) { pomlog(POMLOG_WARN "Dequeing packet which wasn't the first in the list. This shouldn't happen !"); res->prev->next = res->next; } stream->cur_buff_size -= res->plen; packet_stream_free_packet(res); res = NULL; // Next packet please continue; } else { if (packet_stream_remove_dupe_bytes(stream, res, cur_dir) == POM_ERR) return NULL; } } break; } } if (!res) return NULL; // Dequeue the packet stream->head[cur_dir] = res->next; if (res->next) { res->next->prev = res->prev; } else { stream->tail[cur_dir] = NULL; } stream->cur_buff_size -= res->plen; return res; }
int packet_stream_force_dequeue(struct packet_stream *stream) { struct packet_stream_pkt *p = NULL; unsigned int next_dir = 0; while (1) { if (!stream->head[POM_DIR_FWD] && !stream->head[POM_DIR_REV]) return POM_OK; if (!stream->head[POM_DIR_FWD]) { next_dir = POM_DIR_REV; } else if (!stream->head[POM_DIR_REV]) { next_dir = POM_DIR_FWD; } else { // We have packets in both direction, lets see which one we'll process first int i; for (i = 0; i < POM_DIR_TOT; i++) { int r = POM_DIR_REVERSE(i); struct packet_stream_pkt *a = stream->head[i], *b = stream->head[r]; uint32_t end_seq = a->seq + a->plen; if ((end_seq <= b->ack && b->ack - end_seq < PACKET_HALF_SEQ) || (b->ack > end_seq && end_seq - b->ack > PACKET_HALF_SEQ)) break; } if (i == POM_DIR_TOT) { // There is a gap in both direction // Process the first packet received struct packet *a = stream->head[POM_DIR_FWD]->pkt, *b = stream->head[POM_DIR_REV]->pkt; if (a->ts.tv_sec < b->ts.tv_sec || (a->ts.tv_sec == b->ts.tv_sec && a->ts.tv_usec < b->ts.tv_usec)) { next_dir = POM_DIR_FWD; } else { next_dir = POM_DIR_REV; } debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : processing next by timestamp", pthread_self(), stream, stream->head[next_dir]->pkt->ts.tv_sec, stream->head[next_dir]->pkt->ts.tv_usec, stream->head[next_dir]->seq, stream->head[next_dir]->ack); } else { next_dir = i; } } p = stream->head[next_dir]; if (p->next) p->next->prev = NULL; else stream->tail[next_dir] = NULL; stream->head[next_dir] = p->next; stream->cur_buff_size -= p->plen; if (packet_stream_is_packet_old_dupe(stream, p, next_dir)) { packet_stream_free_packet(p); } else { break; } } if (packet_stream_remove_dupe_bytes(stream, p, next_dir) == POM_ERR) return POM_ERR; uint32_t gap = p->seq - stream->cur_seq[next_dir]; int res = PROTO_OK; if (gap) { if (gap < stream->max_buff_size) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : filling gap of %u", pthread_self(), stream, p->pkt->ts.tv_sec, p->pkt->ts.tv_usec, p->seq, p->ack, gap); uint32_t gap_step = gap; if (gap_step > 2048) gap_step = 2048; void *zero = malloc(gap_step); if (!zero) { pom_oom(gap_step); return POM_ERR; } memset(zero, 0, gap_step); struct proto_process_stack *s = &p->stack[p->stack_index]; uint32_t plen_old = s->plen; void *pload_old = s->pload; uint32_t pos; for (pos = 0; pos < gap; pos += gap_step) { if (pos + gap_step < gap) s->plen = gap_step; else s->plen = gap - pos; s->pload = zero; res = stream->handler(stream->ce, p->pkt, p->stack, p->stack_index); s->direction = next_dir; if (res == PROTO_ERR) break; } free(zero); s->pload = pload_old; s->plen = plen_old; } else { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : gap of %u too big. not filling", pthread_self(), stream, p->pkt->ts.tv_sec, p->pkt->ts.tv_usec, p->seq, p->ack, gap); } } if (res != PROTO_ERR) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : process forced", pthread_self(), stream, p->pkt->ts.tv_sec, p->pkt->ts.tv_usec, p->seq, p->ack); res = stream->handler(stream->ce, p->pkt, p->stack, p->stack_index); } stream->cur_seq[next_dir] = p->seq + p->plen; stream->cur_ack[next_dir] = p->ack; packet_stream_free_packet(p); if (res == PROTO_ERR) return POM_ERR; // See if we can process additional packets // Check if additional packets can be processed while ((p = packet_stream_get_next(stream, &next_dir))) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : process additional", pthread_self(), stream, p->pkt->ts.tv_sec, p->pkt->ts.tv_usec, p->seq, p->ack); if (stream->handler(stream->ce, p->pkt, p->stack, p->stack_index) == PROTO_ERR) return POM_ERR; stream->cur_seq[next_dir] += p->plen; stream->cur_ack[next_dir] = p->ack; packet_stream_free_packet(p); } return POM_OK; }
struct stream_pkt *stream_get_next(struct stream *stream, unsigned int *direction) { struct stream_pkt *res = NULL; int dirs[2] = { *direction, POM_DIR_REVERSE(*direction) }; // Make sure we have the sequences before we start looking int dir_flag = (*direction == POM_DIR_FWD ? STREAM_FLAG_GOT_FWD_STARTSEQ : STREAM_FLAG_GOT_REV_STARTSEQ); if ( ((stream->flags & STREAM_FLAG_BIDIR) && ((stream->flags & STREAM_FLAG_GOT_BOTH_STARTSEQ) != STREAM_FLAG_GOT_BOTH_STARTSEQ)) || (!(stream->flags & STREAM_FLAG_BIDIR) && !(stream->flags & dir_flag)) ) return NULL; int i, cur_dir; for (i = 0; i < 2 && !res; i++) { *direction = dirs[i]; cur_dir = *direction; while (stream->head[cur_dir]) { res = stream->head[cur_dir]; if (!stream_is_packet_next(stream, res, cur_dir)) { res = NULL; break; } uint32_t cur_seq = stream->cur_seq[cur_dir]; uint32_t seq = res->seq; // Check for duplicate bytes if (cur_seq != seq) { if (stream_is_packet_old_dupe(stream, res, cur_dir)) { // Packet is a duplicate, remove it stream->head[cur_dir] = res->next; if (res->next) { res->next->prev = NULL; } else { stream->tail[cur_dir] = NULL; } if (res->prev) { pomlog(POMLOG_WARN "Dequeing packet which wasn't the first in the list. This shouldn't happen !"); res->prev->next = res->next; } stream->cur_buff_size -= res->plen; stream_free_packet(res); res = NULL; // Next packet please continue; } else { if (stream_remove_dupe_bytes(stream, res, cur_dir) == POM_ERR) return NULL; } } break; } } if (!res) return NULL; // Dequeue the packet stream->head[cur_dir] = res->next; if (res->next) { res->next->prev = res->prev; } else { stream->tail[cur_dir] = NULL; } stream->cur_buff_size -= res->plen; return res; }
int stream_force_dequeue(struct stream *stream) { struct stream_pkt *p = NULL; unsigned int next_dir = 0; while (1) { if (!stream->head[POM_DIR_FWD] && !stream->head[POM_DIR_REV]) return POM_OK; if (!stream->head[POM_DIR_FWD]) { next_dir = POM_DIR_REV; } else if (!stream->head[POM_DIR_REV]) { next_dir = POM_DIR_FWD; } else { // We have packets in both direction, lets see which one we'll process first int i; for (i = 0; i < POM_DIR_TOT; i++) { int r = POM_DIR_REVERSE(i); struct stream_pkt *a = stream->head[i], *b = stream->head[r]; uint32_t end_seq = a->seq + a->plen; if ((end_seq <= b->ack && b->ack - end_seq < STREAM_HALF_SEQ) || (b->ack > end_seq && end_seq - b->ack > STREAM_HALF_SEQ)) break; } if (i == POM_DIR_TOT) { // There is a gap in both direction // Process the first packet received struct packet *a = stream->head[POM_DIR_FWD]->pkt, *b = stream->head[POM_DIR_REV]->pkt; if (a->ts < b->ts) { next_dir = POM_DIR_FWD; } else { next_dir = POM_DIR_REV; } debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : processing next by timestamp", pthread_self(), stream, pom_ptime_sec(stream->head[next_dir]->pkt->ts), pom_ptime_usec(stream->head[next_dir]->pkt->ts), stream->head[next_dir]->seq, stream->head[next_dir]->ack); } else { next_dir = i; } } p = stream->head[next_dir]; if (p->next) p->next->prev = NULL; else stream->tail[next_dir] = NULL; stream->head[next_dir] = p->next; stream->cur_buff_size -= p->plen; if (stream_is_packet_old_dupe(stream, p, next_dir)) { stream_free_packet(p); } else { break; } } if (stream_remove_dupe_bytes(stream, p, next_dir) == POM_ERR) return POM_ERR; // Flag the stream as running stream->flags |= STREAM_FLAG_RUNNING; // If we didn't we now know about the sequence int dir_flag = (next_dir == POM_DIR_FWD ? STREAM_FLAG_GOT_FWD_STARTSEQ : STREAM_FLAG_GOT_REV_STARTSEQ); if (!(stream->flags & dir_flag)) { stream->cur_seq[next_dir] = p->seq; // We know about the reverse direction as well now if (stream->flags & STREAM_FLAG_BIDIR) { stream->flags |= STREAM_FLAG_GOT_BOTH_STARTSEQ; stream->cur_seq[POM_DIR_REVERSE(next_dir)] = p->ack; } else { stream->flags |= dir_flag; } } int res = PROTO_OK; // Check if we were waiting on the reverse direction if (stream->flags & STREAM_FLAG_BIDIR) { unsigned int next_rev_dir = POM_DIR_REVERSE(next_dir); int rev_dir_flag = (next_rev_dir == POM_DIR_FWD ? STREAM_FLAG_GOT_FWD_DIR : STREAM_FLAG_GOT_REV_DIR); // Only fill a gap in the reverse direction if we've had packets in that direction if (stream->flags & rev_dir_flag) { uint32_t rev_seq = stream->cur_seq[next_rev_dir]; if ((rev_seq < p->ack && p->ack - rev_seq < STREAM_HALF_SEQ) || (rev_seq > p->ack && rev_seq - p->ack > STREAM_HALF_SEQ)) { // We were waiting for reverse uint32_t rev_gap = p->ack - stream->cur_seq[next_rev_dir]; res = stream_fill_gap(stream, p, rev_gap, 1); stream->cur_seq[next_rev_dir] = p->ack; } } } uint32_t gap = p->seq - stream->cur_seq[next_dir]; if (gap) { if (res != PROTO_ERR) res = stream_fill_gap(stream, p, gap, 0); } // Update the cur_seq in our direction stream->cur_seq[next_dir] = p->seq + p->plen; if (res != PROTO_ERR) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : process forced", pthread_self(), stream, pom_ptime_sec(p->pkt->ts), pom_ptime_usec(p->pkt->ts), p->seq, p->ack); res = stream->handler(stream->ce, p->pkt, p->stack, p->stack_index); } stream_free_packet(p); if (res == PROTO_ERR) return POM_ERR; // See if we can process additional packets // Check if additional packets can be processed while ((p = stream_get_next(stream, &next_dir))) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : process additional", pthread_self(), stream, pom_ptime_sec(p->pkt->ts), pom_ptime_usec(p->pkt->ts), p->seq, p->ack); if (stream->handler(stream->ce, p->pkt, p->stack, p->stack_index) == PROTO_ERR) return POM_ERR; stream->cur_seq[next_dir] += p->plen; stream_free_packet(p); } return POM_OK; }
int stream_process_packet(struct stream *stream, struct packet *pkt, struct proto_process_stack *stack, unsigned int stack_index, uint32_t seq, uint32_t ack) { if (!stream || !pkt || !stack) return PROTO_ERR; debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : start", pthread_self(), stream, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts), seq, ack); struct proto_process_stack *cur_stack = &stack[stack_index]; int direction = cur_stack->direction; int must_wait = 0; pom_mutex_lock(&stream->wait_lock); int res = pthread_mutex_trylock(&stream->lock); if (res == EBUSY) { // Already locked, let's wait a bit must_wait = 1; } else if (res) { pomlog(POMLOG_ERR "Error while locking packet stream lock : %s", pom_strerror(res)); abort(); return POM_ERR; } else { // We got the processing lock. But was it really this thread's turn ? struct stream_thread_wait *tmp = stream->wait_list_head; // A thread with a packet preceding ours is waiting if (tmp && (tmp->ts < pkt->ts)) { // No it wasn't, release it and signal the right thread must_wait = 2; pom_mutex_unlock(&stream->lock); debug_stream("thread %p, entry %p : signaling thread %p", pthread_self(), stream, stream->wait_list_head->thread); pthread_cond_broadcast(&stream->wait_list_head->cond); } else { // Yes it was. YAY ! pom_mutex_unlock(&stream->wait_lock); } } if (must_wait) { // Add ourself in the waiting list struct stream_thread_wait *lst = NULL; if (stream->wait_list_unused) { lst = stream->wait_list_unused; stream->wait_list_unused = lst->next; lst->next = NULL; } else { lst = malloc(sizeof(struct stream_thread_wait)); if (!lst) { pom_oom(sizeof(struct stream_thread_wait)); pom_mutex_unlock(&stream->wait_lock); return POM_ERR; } memset(lst, 0, sizeof(struct stream_thread_wait)); if (pthread_cond_init(&lst->cond, NULL)) { pom_mutex_unlock(&stream->wait_lock); pomlog(POMLOG_ERR "Error while initializing wait list condition : %s", pom_strerror(errno)); free(lst); return POM_ERR; } } lst->ts = pkt->ts; lst->thread = pthread_self(); struct stream_thread_wait *tmp; for (tmp = stream->wait_list_head; tmp && (tmp->ts < lst->ts); tmp = tmp->next); if (tmp) { lst->prev = tmp->prev; if (lst->prev) lst->prev->next = lst; else stream->wait_list_head = lst; lst->next = tmp; lst->next->prev = lst; } else { lst->prev = stream->wait_list_tail; if (lst->prev) lst->prev->next = lst; else stream->wait_list_head = lst; stream->wait_list_tail = lst; } while (1) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : waiting (%u)", pthread_self(), stream, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts), seq, ack, must_wait); if (pthread_cond_wait(&lst->cond, &stream->wait_lock)) { pomlog(POMLOG_ERR "Error while waiting for the packet stream wait cond : %s", pom_strerror(errno)); abort(); return POM_ERR; } if (stream->wait_list_head != lst) { // There is a small chance that another stream lock stream->wait_lock while pthread_cond_wait acquires it // If we are not the right thread, then simply signal the right one and wait again for our turn debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : wrong thread woke up", pthread_self(), stream, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts), seq, ack); pthread_cond_broadcast(&stream->wait_list_head->cond); continue; } break; } tmp = stream->wait_list_head; stream->wait_list_head = tmp->next; if (stream->wait_list_head) stream->wait_list_head->prev = NULL; else stream->wait_list_tail = NULL; tmp->next = stream->wait_list_unused; tmp->prev = NULL; stream->wait_list_unused = tmp; pom_mutex_unlock(&stream->wait_lock); pom_mutex_lock(&stream->lock); } debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : start locked : cur_seq %u, rev_seq %u", pthread_self(), stream, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts), seq, ack, stream->cur_seq[direction], stream->cur_seq[POM_DIR_REVERSE(direction)]); // Update the stream flags if (stream->flags & STREAM_FLAG_BIDIR) { // Update flags if (direction == POM_DIR_FWD && !(stream->flags & STREAM_FLAG_GOT_FWD_DIR)) { stream->flags |= STREAM_FLAG_GOT_FWD_DIR; } else if (direction == POM_DIR_REV && !(stream->flags & STREAM_FLAG_GOT_REV_DIR)) { stream->flags |= STREAM_FLAG_GOT_REV_DIR; } } // Update the last timestamp seen on the stream if (stream->last_ts < pkt->ts) stream->last_ts = pkt->ts; // Put this packet in our struct stream_pkt struct stream_pkt spkt = {0}; spkt.pkt = pkt; spkt.seq = seq; spkt.ack = ack; spkt.plen = cur_stack->plen; spkt.stack = stack; spkt.stack_index = stack_index; // Check that we are aware of the start sequence // If not, we queue int dir_flag = (direction == POM_DIR_FWD ? STREAM_FLAG_GOT_FWD_STARTSEQ : STREAM_FLAG_GOT_REV_STARTSEQ); if ( ((stream->flags & STREAM_FLAG_BIDIR) && ((stream->flags & STREAM_FLAG_GOT_BOTH_STARTSEQ) == STREAM_FLAG_GOT_BOTH_STARTSEQ)) || (!(stream->flags & STREAM_FLAG_BIDIR) && (stream->flags & dir_flag)) ) { // Check if the packet is worth processing uint32_t cur_seq = stream->cur_seq[direction]; if (cur_seq != seq) { if (stream_is_packet_old_dupe(stream, &spkt, direction)) { // cur_seq is after the end of the packet, discard it stream_end_process_packet(stream); debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : discard", pthread_self(), stream, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts), seq, ack); return PROTO_OK; } if (stream_remove_dupe_bytes(stream, &spkt, direction) == POM_ERR) { stream_end_process_packet(stream); return PROTO_ERR; } } // Ok let's process it then // Check if it is the packet we're waiting for if (stream_is_packet_next(stream, &spkt, direction)) { // Process it stream->cur_seq[direction] += cur_stack->plen; debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : process", pthread_self(), stream, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts), seq, ack); int res = stream->handler(stream->ce, pkt, stack, stack_index); if (res == PROTO_ERR) { stream_end_process_packet(stream); return PROTO_ERR; } // Flag the stream as running stream->flags |= STREAM_FLAG_RUNNING; // Check if additional packets can be processed struct stream_pkt *p = NULL; unsigned int cur_dir = direction; while ((p = stream_get_next(stream, &cur_dir))) { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : process additional", pthread_self(), stream, pom_ptime_sec(p->pkt->ts), pom_ptime_usec(p->pkt->ts), p->seq, p->ack); if (stream->handler(stream->ce, p->pkt, p->stack, p->stack_index) == POM_ERR) { stream_end_process_packet(stream); return PROTO_ERR; } stream->cur_seq[cur_dir] += p->plen; stream_free_packet(p); } stream_end_process_packet(stream); debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : done processed", pthread_self(), stream, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts), seq, ack); return res; } } else { debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : start_seq not known yet", pthread_self(), stream, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts), seq, ack); } // Queue the packet then debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : queue", pthread_self(), stream, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts), seq, ack); struct stream_pkt *p = malloc(sizeof(struct stream_pkt)); if (!p) { pom_oom(sizeof(struct stream_pkt)); stream_end_process_packet(stream); return PROTO_ERR; } memset(p, 0 , sizeof(struct stream_pkt)); int flags = 0; if (stream->flags & STREAM_FLAG_PACKET_NO_COPY) flags = PACKET_FLAG_FORCE_NO_COPY; p->pkt = packet_clone(pkt, flags); if (!p->pkt) { stream_end_process_packet(stream); free(p); return PROTO_ERR; } p->stack = core_stack_backup(stack, pkt, p->pkt); if (!p->stack) { stream_end_process_packet(stream); packet_release(p->pkt); free(p); return PROTO_ERR; } p->plen = cur_stack->plen; p->seq = seq; p->ack = ack; p->stack_index = stack_index; if (!stream->tail[direction]) { stream->head[direction] = p; stream->tail[direction] = p; } else { struct stream_pkt *tmp = stream->tail[direction]; while ( tmp && ((tmp->seq >= seq && tmp->seq - seq < STREAM_HALF_SEQ) || (tmp->seq <= seq && seq - tmp->seq > STREAM_HALF_SEQ))) { tmp = tmp->prev; } if (!tmp) { // Packet goes at the begining of the list p->next = stream->head[direction]; if (p->next) p->next->prev = p; else stream->tail[direction] = p; stream->head[direction] = p; } else { // Insert the packet after the current one p->next = tmp->next; p->prev = tmp; if (p->next) p->next->prev = p; else stream->tail[direction] = p; tmp->next = p; } } stream->cur_buff_size += cur_stack->plen; if (stream->cur_buff_size >= stream->max_buff_size) { // Buffer overflow debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : buffer overflow, forced dequeue", pthread_self(), stream, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts), seq, ack); if (stream_force_dequeue(stream) != POM_OK) { stream_end_process_packet(stream); return POM_ERR; } } stream_end_process_packet(stream); debug_stream("thread %p, entry %p, packet %u.%06u, seq %u, ack %u : done queued", pthread_self(), stream, pom_ptime_sec(pkt->ts), pom_ptime_usec(pkt->ts), seq, ack); return PROTO_OK; }