Exemple #1
0
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;
}
Exemple #2
0
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;
}
Exemple #3
0
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;

}
Exemple #4
0
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;
}
Exemple #5
0
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;

}
Exemple #6
0
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;
}
Exemple #7
0
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;
}
Exemple #8
0
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;
}
Exemple #9
0
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;
}
Exemple #10
0
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;
}