You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by al...@apache.org on 2017/11/14 01:48:28 UTC

[14/25] nifi-minifi-cpp git commit: MINIFICPP-250: Initial implementation fo CapturePacket Processor that uses lipcap.

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/c0a788b3/thirdparty/pcap++/Packet++/src/GreLayer.cpp
----------------------------------------------------------------------
diff --git a/thirdparty/pcap++/Packet++/src/GreLayer.cpp b/thirdparty/pcap++/Packet++/src/GreLayer.cpp
new file mode 100644
index 0000000..6cd3bb3
--- /dev/null
+++ b/thirdparty/pcap++/Packet++/src/GreLayer.cpp
@@ -0,0 +1,595 @@
+#define LOG_MODULE PacketLogModuleGreLayer
+
+#include <GreLayer.h>
+#include <EthLayer.h>
+#include <IPv4Layer.h>
+#include <IPv6Layer.h>
+#include <PPPoELayer.h>
+#include <VlanLayer.h>
+#include <MplsLayer.h>
+#include <PayloadLayer.h>
+#include <Logger.h>
+#include <IpUtils.h>
+#if defined(WIN32) || defined(WINx64) //for using ntohl, ntohs, etc.
+#include <winsock2.h>
+#elif LINUX
+#include <in.h> //for using ntohl, ntohs, etc.
+#elif MAC_OS_X
+#include <arpa/inet.h> //for using ntohl, ntohs, etc.
+#endif
+
+// ==============
+// GreLayer class
+// ==============
+
+namespace pcpp
+{
+
+ProtocolType GreLayer::getGREVersion(uint8_t* greData, size_t greDataLen)
+{
+	if (greDataLen < sizeof(gre_basic_header))
+		return UnknownProtocol;
+
+	uint8_t version = *(greData+1);
+	version &= 0x07;
+	if (version == 0)
+		return GREv0;
+	else if (version == 1)
+		return GREv1;
+	else
+		return UnknownProtocol;
+}
+
+uint8_t* GreLayer::getFieldValue(GreField field, bool returnOffsetEvenIfFieldMissing)
+{
+	uint8_t* ptr = m_Data + sizeof(gre_basic_header);
+
+	gre_basic_header* header = (gre_basic_header*)m_Data;
+
+	GreField curField = GreChecksumOrRouting;
+
+	while (curField < 4)
+	{
+		bool curFieldExists = false;
+
+		uint8_t* origPtr = ptr;
+
+		switch (curField)
+		{
+		case GreChecksumOrRouting:
+			if (header->checksumBit == 1 || header->routingBit == 1)
+			{
+				curFieldExists = true;
+				ptr += sizeof(uint32_t);
+			}
+			break;
+		case GreKey:
+			if (header->keyBit == 1)
+			{
+				curFieldExists = true;
+				ptr += sizeof(uint32_t);
+			}
+			break;
+		case GreSeq:
+			if (header->sequenceNumBit == 1)
+			{
+				curFieldExists = true;
+				ptr += sizeof(uint32_t);
+			}
+			break;
+		case GreAck:
+			if (header->ackSequenceNumBit == 1)
+			{
+				curFieldExists = true;
+				ptr += sizeof(uint32_t);
+			}
+			break;
+		default: // shouldn't get there
+			return NULL;
+		}
+
+		if (field == curField)
+		{
+			if (curFieldExists || returnOffsetEvenIfFieldMissing)
+				return origPtr;
+			else
+				return NULL;
+		}
+
+		int curFieldAsInt = (int)curField;
+		curFieldAsInt++;
+		curField = static_cast<GreField>(curFieldAsInt);
+	}
+
+	return NULL;
+}
+
+void GreLayer::computeCalculateFieldsInner()
+{
+	gre_basic_header* header = (gre_basic_header*)m_Data;
+	if (m_NextLayer != NULL)
+	{
+		switch (m_NextLayer->getProtocol())
+		{
+		case IPv4:
+			header->protocol = htons(PCPP_ETHERTYPE_IP);
+			break;
+		case IPv6:
+			header->protocol = htons(PCPP_ETHERTYPE_IPV6);
+			break;
+		case VLAN:
+			header->protocol = htons(PCPP_ETHERTYPE_VLAN);
+			break;
+		case MPLS:
+			header->protocol = htons(PCPP_ETHERTYPE_MPLS);
+			break;
+		case PPP_PPTP:
+			header->protocol = htons(PCPP_ETHERTYPE_PPP);
+			break;
+
+		default:
+			break;
+		}
+	}
+}
+
+bool GreLayer::getSequenceNumber(uint32_t& seqNumber)
+{
+	gre_basic_header* header = (gre_basic_header*)m_Data;
+
+	if (header->sequenceNumBit == 0)
+		return false;
+
+	uint32_t* val = (uint32_t*)getFieldValue(GreSeq, false);
+	if (val == NULL)
+		return false;
+
+	seqNumber = ntohl(*val);
+	return true;
+}
+
+bool GreLayer::setSequenceNumber(uint32_t seqNumber)
+{
+	gre_basic_header* header = (gre_basic_header*)m_Data;
+
+	bool needToExtendLayer = false;
+
+	if (header->sequenceNumBit == 0)
+		needToExtendLayer = true;
+
+	uint8_t* offsetPtr = getFieldValue(GreSeq, true);
+
+	int offset = offsetPtr - m_Data;
+	if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t)))
+	{
+		header->sequenceNumBit = 0;
+		LOG_ERROR("Couldn't extend layer to set sequence number");
+		return false;
+	}
+
+	header = (gre_basic_header*)m_Data;
+	header->sequenceNumBit = 1;
+	uint32_t* seqPtr = (uint32_t*)(m_Data + offset);
+	*seqPtr = htonl(seqNumber);
+
+	return true;
+}
+
+bool GreLayer::unsetSequenceNumber()
+{
+	gre_basic_header* header = (gre_basic_header*)m_Data;
+
+	if (header->sequenceNumBit == 0)
+	{
+		LOG_ERROR("Couldn't unset sequence number as it's already unset");
+		return false;
+	}
+
+	uint8_t* offsetPtr = getFieldValue(GreSeq, true);
+
+	int offset = offsetPtr - m_Data;
+	if (!shortenLayer(offset, sizeof(uint32_t)))
+	{
+		LOG_ERROR("Couldn't shorted layer to unset sequence number");
+		return false;
+	}
+
+	header = (gre_basic_header*)m_Data;
+	header->sequenceNumBit = 0;
+	return true;
+}
+
+void GreLayer::parseNextLayer()
+{
+	size_t headerLen = getHeaderLen();
+	if (m_DataLen <= headerLen)
+		return;
+
+	gre_basic_header* header = (gre_basic_header*)m_Data;
+	switch (ntohs(header->protocol))
+	{
+	case PCPP_ETHERTYPE_IP:
+		m_NextLayer = new IPv4Layer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet);
+		break;
+	case PCPP_ETHERTYPE_IPV6:
+		m_NextLayer = new IPv6Layer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet);
+		break;
+	case PCPP_ETHERTYPE_VLAN:
+		m_NextLayer = new VlanLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet);
+		break;
+	case PCPP_ETHERTYPE_MPLS:
+		m_NextLayer = new MplsLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet);
+		break;
+	case PCPP_ETHERTYPE_PPP:
+		m_NextLayer = new PPP_PPTPLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet);
+		break;
+	default:
+		m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet);
+	}
+}
+
+size_t GreLayer::getHeaderLen()
+{
+	size_t result = sizeof(gre_basic_header);
+
+	gre_basic_header* header = (gre_basic_header*)m_Data;
+
+	if (header->checksumBit == 1 || header->routingBit == 1 )
+		result += 4;
+	if (header->keyBit == 1)
+		result += 4;
+	if (header->sequenceNumBit == 1)
+		result += 4;
+	if (header->ackSequenceNumBit == 1)
+		result += 4;
+
+	return result;
+}
+
+
+
+// ================
+// GREv0Layer class
+// ================
+
+
+GREv0Layer::GREv0Layer()
+{
+	m_DataLen = sizeof(gre_basic_header);
+	m_Data = new uint8_t[m_DataLen];
+	memset(m_Data, 0, m_DataLen);
+	m_Protocol = GREv0;
+}
+
+bool GREv0Layer::getChecksum(uint16_t& checksum)
+{
+	if (getGreHeader()->checksumBit == 0)
+		return false;
+
+	uint16_t* val = (uint16_t*)getFieldValue(GreChecksumOrRouting, false);
+	if (val == NULL)
+		return false;
+
+	checksum = ntohs(*val);
+	return true;
+}
+
+bool GREv0Layer::setChecksum(uint16_t checksum)
+{
+	gre_basic_header* header = getGreHeader();
+
+	bool needToExtendLayer = false;
+
+	if (header->routingBit == 0 && header->checksumBit == 0)
+		needToExtendLayer = true;
+
+	uint8_t* offsetPtr = getFieldValue(GreChecksumOrRouting, true);
+	int offset = offsetPtr - m_Data;
+	// extend layer in 4 bytes to keep 4-byte alignment
+	if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t)))
+	{
+		LOG_ERROR("Couldn't extend layer to set checksum");
+		return false;
+	}
+
+	uint16_t* checksumPtr = (uint16_t*)(m_Data + offset);
+	*checksumPtr = htons(checksum);
+
+	// if layer was extended in 4 bytes, make sure the offset field stays 0
+	if (needToExtendLayer)
+	{
+		checksumPtr++;
+		*checksumPtr = 0;
+	}
+
+	header = getGreHeader();
+	header->checksumBit = 1;
+
+	return true;
+}
+
+bool GREv0Layer::unsetChecksum()
+{
+	gre_basic_header* header = getGreHeader();
+
+	if (header->checksumBit == 0)
+	{
+		LOG_ERROR("Couldn't unset checksum as it's already unset");
+		return false;
+	}
+
+	// if both routing and checksum are unset we need to shorted the layer
+	bool needToShortenLayer = (header->routingBit == 0);
+
+	uint8_t* offsetPtr = getFieldValue(GreChecksumOrRouting, true);
+	int offset = offsetPtr - m_Data;
+	if (needToShortenLayer && !shortenLayer(offset, sizeof(uint32_t)))
+	{
+		LOG_ERROR("Couldn't extend layer to unset checksum");
+		return false;
+	}
+
+	if (!needToShortenLayer) // meaning routing bit is set - only zero the checksum field
+	{
+		uint16_t* checksumPtr = (uint16_t*)(m_Data + offset);
+		*checksumPtr = 0;
+	}
+
+	header = getGreHeader();
+	header->checksumBit = 0;
+
+	return true;
+}
+
+bool GREv0Layer::getOffset(uint16_t& offset)
+{
+	if (getGreHeader()->routingBit == 0)
+		return false;
+
+	uint8_t* val = (uint8_t*)getFieldValue(GreChecksumOrRouting, false);
+	if (val == NULL)
+		return false;
+
+	offset = ntohs(*(val+2));
+	return true;
+}
+
+bool GREv0Layer::getKey(uint32_t& key)
+{
+	if (getGreHeader()->keyBit == 0)
+		return false;
+
+	uint32_t* val = (uint32_t*)getFieldValue(GreKey, false);
+	if (val == NULL)
+		return false;
+
+	key = ntohl(*val);
+	return true;
+}
+
+bool GREv0Layer::setKey(uint32_t key)
+{
+	gre_basic_header* header = getGreHeader();
+
+	bool needToExtendLayer = false;
+
+	if (header->keyBit == 0)
+		needToExtendLayer = true;
+
+	uint8_t* offsetPtr = getFieldValue(GreKey, true);
+
+	int offset = offsetPtr - m_Data;
+	if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t)))
+	{
+		header->keyBit = 0;
+		LOG_ERROR("Couldn't extend layer to set key");
+		return false;
+	}
+
+	header = getGreHeader();
+	header->keyBit = 1;
+	uint32_t* keyPtr = (uint32_t*)(m_Data + offset);
+	*keyPtr = htonl(key);
+
+	return true;
+}
+
+bool GREv0Layer::unsetKey()
+{
+	gre_basic_header* header = getGreHeader();
+
+	if (header->keyBit == 0)
+	{
+		LOG_ERROR("Couldn't unset key as it's already unset");
+		return false;
+	}
+
+	uint8_t* offsetPtr = getFieldValue(GreKey, true);
+
+	int offset = offsetPtr - m_Data;
+	if (!shortenLayer(offset, sizeof(uint32_t)))
+	{
+		LOG_ERROR("Couldn't shorted layer to unset key");
+		return false;
+	}
+
+	header = (gre_basic_header*)m_Data;
+	header->keyBit = 0;
+	return true;
+}
+
+void GREv0Layer::computeCalculateFields()
+{
+	computeCalculateFieldsInner();
+
+	if (getGreHeader()->checksumBit == 0)
+		return;
+
+	// calculate checksum
+	setChecksum(0);
+
+	ScalarBuffer<uint16_t> buffer;
+	buffer.buffer = (uint16_t*)m_Data;
+	buffer.len = m_DataLen;
+	size_t checksum = compute_checksum(&buffer, 1);
+
+	setChecksum(checksum);
+}
+
+std::string GREv0Layer::toString()
+{
+	return "GRE Layer, version 0";
+}
+
+
+// ================
+// GREv1Layer class
+// ================
+
+GREv1Layer::GREv1Layer(uint16_t callID)
+{
+	m_DataLen = sizeof(gre1_header);
+	m_Data = new uint8_t[m_DataLen];
+	memset(m_Data, 0, m_DataLen);
+	m_Protocol = GREv1;
+
+	gre1_header* header = getGreHeader();
+	header->keyBit = 1;
+	header->version = 1;
+	header->callID = htons(callID);
+}
+
+bool GREv1Layer::getAcknowledgmentNum(uint32_t& ackNum)
+{
+	if (getGreHeader()->ackSequenceNumBit == 0)
+		return false;
+
+	uint32_t* val = (uint32_t*)getFieldValue(GreAck, false);
+	if (val == NULL)
+		return false;
+
+	ackNum = ntohl(*val);
+	return true;
+}
+
+bool GREv1Layer::setAcknowledgmentNum(uint32_t ackNum)
+{
+	bool needToExtendLayer = false;
+
+	gre1_header* header = getGreHeader();
+
+	if (header->ackSequenceNumBit == 0)
+		needToExtendLayer = true;
+
+	uint8_t* offsetPtr = getFieldValue(GreAck, true);
+	int offset = offsetPtr - m_Data;
+	if (needToExtendLayer && !extendLayer(offset, sizeof(uint32_t)))
+	{
+		LOG_ERROR("Couldn't extend layer to set ack number");
+		return false;
+	}
+
+	header = getGreHeader();
+	header->ackSequenceNumBit = 1;
+	uint32_t* ackPtr = (uint32_t*)(m_Data + offset);
+	*ackPtr = htonl(ackNum);
+	return true;
+}
+
+bool GREv1Layer::unsetAcknowledgmentNum()
+{
+	gre1_header* header = getGreHeader();
+
+	if (header->ackSequenceNumBit == 0)
+	{
+		LOG_ERROR("Couldn't unset ack number as it's already unset");
+		return false;
+	}
+
+	uint8_t* offsetPtr = getFieldValue(GreAck, true);
+
+	int offset = offsetPtr - m_Data;
+	if (!shortenLayer(offset, sizeof(uint32_t)))
+	{
+		LOG_ERROR("Couldn't shorted layer to unset ack number");
+		return false;
+	}
+
+	header = getGreHeader();
+	header->ackSequenceNumBit = 0;
+	return true;
+}
+
+void GREv1Layer::computeCalculateFields()
+{
+	computeCalculateFieldsInner();
+
+	getGreHeader()->payloadLength = htons(m_DataLen - getHeaderLen());
+}
+
+std::string GREv1Layer::toString()
+{
+	return "GRE Layer, version 1";
+}
+
+
+
+// ===================
+// PPP_PPTPLayer class
+// ===================
+
+PPP_PPTPLayer::PPP_PPTPLayer(uint8_t address, uint8_t control)
+{
+	m_DataLen = sizeof(ppp_pptp_header);
+	m_Data = new uint8_t[m_DataLen];
+	memset(m_Data, 0, m_DataLen);
+	m_Protocol = PPP_PPTP;
+
+	ppp_pptp_header* header = getPPP_PPTPHeader();
+	header->address = address;
+	header->control = control;
+}
+
+
+void PPP_PPTPLayer::parseNextLayer()
+{
+	size_t headerLen = getHeaderLen();
+	if (m_DataLen <= headerLen)
+		return;
+
+	switch (ntohs(getPPP_PPTPHeader()->protocol))
+	{
+	case PCPP_PPP_IP:
+		m_NextLayer = new IPv4Layer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet);
+		break;
+	case PCPP_PPP_IPV6:
+		m_NextLayer = new IPv6Layer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet);
+		break;
+	default:
+		m_NextLayer = new PayloadLayer(m_Data + headerLen, m_DataLen - headerLen, this, m_Packet);
+		break;
+	}
+}
+
+void PPP_PPTPLayer::computeCalculateFields()
+{
+	ppp_pptp_header* header = getPPP_PPTPHeader();
+	if (m_NextLayer != NULL)
+	{
+		switch (m_NextLayer->getProtocol())
+		{
+		case IPv4:
+			header->protocol = htons(PCPP_PPP_IP);
+			break;
+		case IPv6:
+			header->protocol = htons(PCPP_PPP_IPV6);
+			break;
+		default:
+			break;
+		}
+	}
+	else
+		header->protocol = 0;
+}
+
+} // namespace pcpp

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/c0a788b3/thirdparty/pcap++/Packet++/src/HttpLayer.cpp
----------------------------------------------------------------------
diff --git a/thirdparty/pcap++/Packet++/src/HttpLayer.cpp b/thirdparty/pcap++/Packet++/src/HttpLayer.cpp
new file mode 100644
index 0000000..0651b53
--- /dev/null
+++ b/thirdparty/pcap++/Packet++/src/HttpLayer.cpp
@@ -0,0 +1,1322 @@
+#define LOG_MODULE PacketLogModuleHttpLayer
+
+#include "Logger.h"
+#include "HttpLayer.h"
+#include <string.h>
+#include <algorithm>
+#include <stdlib.h>
+
+
+namespace pcpp
+{
+
+static std::map<uint16_t, bool> createHTTPPortMap()
+{
+	std::map<uint16_t, bool> result;
+	result[80] = true;
+	result[8080] = true;
+	return result;
+}
+
+static const std::map<uint16_t, bool> HTTPPortMap = createHTTPPortMap();
+
+
+
+
+// -------- Class HttpMessage -----------------
+
+const std::map<uint16_t, bool>* HttpMessage::getHTTPPortMap()
+{
+	return &HTTPPortMap;
+}
+
+
+HeaderField* HttpMessage::addField(const std::string& fieldName, const std::string& fieldValue)
+{
+	if (getFieldByName(fieldName) != NULL)
+	{
+		LOG_ERROR("Field '%s' already exists!", fieldName.c_str());
+		return NULL;
+	}
+
+	return TextBasedProtocolMessage::addField(fieldName, fieldValue);
+}
+
+HeaderField* HttpMessage::addField(const HeaderField& newField)
+{
+	if (getFieldByName(newField.getFieldName()) != NULL)
+	{
+		LOG_ERROR("Field '%s' already exists!",newField.getFieldName().c_str());
+		return NULL;
+	}
+
+	return TextBasedProtocolMessage::addField(newField);
+}
+
+HeaderField* HttpMessage::insertField(HeaderField* prevField, const std::string& fieldName, const std::string& fieldValue)
+{
+	if (getFieldByName(fieldName) != NULL)
+	{
+		LOG_ERROR("Field '%s' already exists!", fieldName.c_str());
+		return NULL;
+	}
+
+	return TextBasedProtocolMessage::insertField(prevField, fieldName, fieldValue);
+}
+
+HeaderField* HttpMessage::insertField(HeaderField* prevField, const HeaderField& newField)
+{
+	if (getFieldByName(newField.getFieldName()) != NULL)
+	{
+		LOG_ERROR("Field '%s' already exists!",newField.getFieldName().c_str());
+		return NULL;
+	}
+
+	return TextBasedProtocolMessage::insertField(prevField, newField);
+}
+
+
+
+// -------- Class HttpRequestLayer -----------------
+
+HttpRequestLayer::HttpRequestLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet) : HttpMessage(data, dataLen, prevLayer, packet)
+{
+	m_Protocol = HTTPRequest;
+	m_FirstLine = new HttpRequestFirstLine(this);
+	m_FieldsOffset = m_FirstLine->getSize();
+	parseFields();
+}
+
+HttpRequestLayer::HttpRequestLayer(HttpMethod method, std::string uri, HttpVersion version)
+{
+	m_Protocol = HTTPRequest;
+	m_FirstLine = new HttpRequestFirstLine(this, method, version, uri);
+	m_FieldsOffset = m_FirstLine->getSize();
+}
+
+HttpRequestLayer::HttpRequestLayer(const HttpRequestLayer& other) : HttpMessage(other)
+{
+	m_FirstLine = new HttpRequestFirstLine(this);
+}
+
+HttpRequestLayer& HttpRequestLayer::operator=(const HttpRequestLayer& other)
+{
+	HttpMessage::operator=(other);
+
+	if (m_FirstLine != NULL)
+		delete m_FirstLine;
+
+	m_FirstLine = new HttpRequestFirstLine(this);
+
+	return *this;
+}
+
+
+std::string HttpRequestLayer::getUrl()
+{
+	HeaderField* hostField = getFieldByName(PCPP_HTTP_HOST_FIELD);
+	if (hostField == NULL)
+		return m_FirstLine->getUri();
+
+	return hostField->getFieldValue() + m_FirstLine->getUri();
+}
+
+HttpRequestLayer::~HttpRequestLayer()
+{
+	delete m_FirstLine;
+}
+
+std::string HttpRequestLayer::toString()
+{
+	static const int maxLengthToPrint = 120;
+	std::string result = "HTTP request, ";
+	int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line
+	if (size <= maxLengthToPrint)
+	{
+		char* firstLine = new char[size+1];
+		strncpy(firstLine, (char*)m_Data, size);
+		firstLine[size] = 0;
+		result += std::string(firstLine);
+		delete[] firstLine;
+	}
+	else
+	{
+		char firstLine[maxLengthToPrint+1];
+		strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3);
+		firstLine[maxLengthToPrint-3] = '.';
+		firstLine[maxLengthToPrint-2] = '.';
+		firstLine[maxLengthToPrint-1] = '.';
+		firstLine[maxLengthToPrint] = 0;
+		result += std::string(firstLine);
+	}
+
+	return result;
+}
+
+
+
+
+
+
+
+// -------- Class HttpRequestFirstLine -----------------
+
+
+const std::string MethodEnumToString[9] = {
+		"GET",
+		"HEAD",
+		"POST",
+		"PUT",
+		"DELETE",
+		"TRACE",
+		"OPTIONS",
+		"CONNECT",
+		"PATCH"
+};
+
+const std::string VersionEnumToString[3] = {
+		"0.9",
+		"1.0",
+		"1.1"
+};
+
+HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest) : m_HttpRequest(httpRequest)
+{
+	m_Method = parseMethod((char*)m_HttpRequest->m_Data, m_HttpRequest->getDataLen());
+	if (m_Method == HttpRequestLayer::HttpMethodUnknown)
+	{
+		m_UriOffset = -1;
+		LOG_DEBUG("Couldn't resolve HTTP request method");
+	}
+	else
+		m_UriOffset = MethodEnumToString[m_Method].length() + 1;
+
+	parseVersion();
+
+	char* endOfFirstLine;
+	if ((endOfFirstLine = (char *)memchr((char*)(m_HttpRequest->m_Data + m_VersionOffset), '\n', m_HttpRequest->m_DataLen-(size_t)m_VersionOffset)) != NULL)
+	{
+		m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpRequest->m_Data + 1;
+		m_IsComplete = true;
+	}
+	else
+	{
+		m_FirstLineEndOffset = m_HttpRequest->getDataLen();
+		m_IsComplete = false;
+	}
+
+	LOG_DEBUG("Method='%s'; HTTP version='%s'; URI='%s'", MethodEnumToString[m_Method].c_str(), VersionEnumToString[m_Version].c_str(), getUri().c_str());
+}
+
+HttpRequestFirstLine::HttpRequestFirstLine(HttpRequestLayer* httpRequest, HttpRequestLayer::HttpMethod method, HttpVersion version, std::string uri)
+		throw(HttpRequestFirstLineException)
+{
+	if (method == HttpRequestLayer::HttpMethodUnknown)
+	{
+		m_Exception.setMessage("Method supplied was HttpMethodUnknown");
+		throw m_Exception;
+	}
+
+	if (version == HttpVersionUnknown)
+	{
+		m_Exception.setMessage("Version supplied was HttpVersionUnknown");
+		throw m_Exception;
+	}
+
+	m_HttpRequest = httpRequest;
+
+	m_Method = method;
+	m_Version = version;
+
+	std::string firstLine = MethodEnumToString[m_Method] + " " + uri + " "  + "HTTP/" + VersionEnumToString[m_Version] + "\r\n";
+
+	m_UriOffset =  MethodEnumToString[m_Method].length() + 1;
+	m_FirstLineEndOffset = firstLine.length();
+	m_VersionOffset = m_UriOffset + uri.length() + 6;
+
+	m_HttpRequest->m_DataLen = firstLine.length();
+	m_HttpRequest->m_Data = new uint8_t[m_HttpRequest->m_DataLen];
+	memcpy(m_HttpRequest->m_Data, firstLine.c_str(), m_HttpRequest->m_DataLen);
+
+	m_IsComplete = true;
+}
+
+
+HttpRequestLayer::HttpMethod HttpRequestFirstLine::parseMethod(char* data, size_t dataLen)
+{
+	if (dataLen < 4)
+	{
+		return HttpRequestLayer::HttpMethodUnknown;
+	}
+
+	switch (data[0])
+	{
+	case 'G':
+		if (data[1] == 'E' && data[2] == 'T' && data[3] == ' ')
+			return HttpRequestLayer::HttpGET;
+		else
+			return HttpRequestLayer::HttpMethodUnknown;
+		break;
+
+	case 'D':
+		if (dataLen < 7)
+			return HttpRequestLayer::HttpMethodUnknown;
+		else if (data[1] == 'E' && data[2] == 'L' && data[3] == 'E' && data[4] == 'T' && data[5] == 'E' && data[6] == ' ')
+			return HttpRequestLayer::HttpDELETE;
+		else
+			return HttpRequestLayer::HttpMethodUnknown;
+		break;
+
+	case 'C':
+		if (dataLen < 8)
+			return HttpRequestLayer::HttpMethodUnknown;
+		else if (data[1] == 'O' && data[2] == 'N' && data[3] == 'N' && data[5] == 'E' && data[5] == 'C' && data[6] == 'T' && data[7] == ' ')
+			return HttpRequestLayer::HttpCONNECT;
+		else
+			return HttpRequestLayer::HttpMethodUnknown;
+		break;
+
+	case 'T':
+		if (dataLen < 6)
+			return HttpRequestLayer::HttpMethodUnknown;
+		else if (data[1] == 'R' && data[2] == 'A' && data[3] == 'C' && data[4] == 'E' && data[5] == ' ')
+			return HttpRequestLayer::HttpTRACE;
+		else
+			return HttpRequestLayer::HttpMethodUnknown;
+		break;
+
+
+	case 'H':
+		if (dataLen < 5)
+			return HttpRequestLayer::HttpMethodUnknown;
+		else if (data[1] == 'E' && data[2] == 'A' && data[3] == 'D' && data[4] == ' ')
+			return HttpRequestLayer::HttpHEAD;
+		else
+			return HttpRequestLayer::HttpMethodUnknown;
+		break;
+
+	case 'O':
+		if (dataLen < 8)
+			return HttpRequestLayer::HttpMethodUnknown;
+		else if (data[1] == 'P' && data[2] == 'T' && data[3] == 'I' && data[4] == 'O' && data[5] == 'N' && data[6] == 'S' && data[7] == ' ')
+			return HttpRequestLayer::HttpOPTIONS;
+		else
+			return HttpRequestLayer::HttpMethodUnknown;
+		break;
+
+	case 'P':
+		switch (data[1])
+		{
+		case 'U':
+			if (data[2] == 'T' && data[3] == ' ')
+				return HttpRequestLayer::HttpPUT;
+			else
+				return HttpRequestLayer::HttpMethodUnknown;
+			break;
+
+		case 'O':
+			if (dataLen < 5)
+				return HttpRequestLayer::HttpMethodUnknown;
+			else if (data[2] == 'S' && data[3] == 'T' && data[4] == ' ')
+				return HttpRequestLayer::HttpPOST;
+			else
+				return HttpRequestLayer::HttpMethodUnknown;
+			break;
+
+		case 'A':
+			if (dataLen < 6)
+				return HttpRequestLayer::HttpMethodUnknown;
+			else if (data[2] == 'T' && data[3] == 'C' && data[4] == 'H' && data[5] == ' ')
+				return HttpRequestLayer::HttpPATCH;
+			else
+				return HttpRequestLayer::HttpMethodUnknown;
+			break;
+
+		default:
+			return HttpRequestLayer::HttpMethodUnknown;
+		}
+		break;
+
+	default:
+		return HttpRequestLayer::HttpMethodUnknown;
+	}
+}
+
+void HttpRequestFirstLine::parseVersion()
+{
+	char* data = (char*)(m_HttpRequest->m_Data + m_UriOffset);
+	char* verPos = strstr(data, " HTTP/");
+	if (verPos == NULL)
+	{
+		m_Version = HttpVersionUnknown;
+		m_VersionOffset = -1;
+		return;
+	}
+
+	// verify packet doesn't end before the version, meaning still left place for " HTTP/x.y" (9 chars)
+	if ((uint16_t)(verPos + 9 - (char*)m_HttpRequest->m_Data) > m_HttpRequest->getDataLen())
+	{
+		m_Version = HttpVersionUnknown;
+		m_VersionOffset = -1;
+		return;
+	}
+
+	//skip " HTTP/" (6 chars)
+	verPos += 6;
+	switch (verPos[0])
+	{
+	case '0':
+		if (verPos[1] == '.' && verPos[2] == '9')
+			m_Version = ZeroDotNine;
+		else
+			m_Version = HttpVersionUnknown;
+		break;
+
+	case '1':
+		if (verPos[1] == '.' && verPos[2] == '0')
+			m_Version = OneDotZero;
+		else if (verPos[1] == '.' && verPos[2] == '1')
+			m_Version = OneDotOne;
+		else
+			m_Version = HttpVersionUnknown;
+		break;
+
+	default:
+		m_Version = HttpVersionUnknown;
+	}
+
+	m_VersionOffset = verPos - (char*)m_HttpRequest->m_Data;
+}
+
+bool HttpRequestFirstLine::setMethod(HttpRequestLayer::HttpMethod newMethod)
+{
+	if (newMethod == HttpRequestLayer::HttpMethodUnknown)
+	{
+		LOG_ERROR("Requested method is HttpMethodUnknown");
+		return false;
+	}
+
+	//extend or shorten layer
+	int lengthDifference = MethodEnumToString[newMethod].length() - MethodEnumToString[m_Method].length();
+	if (lengthDifference > 0)
+	{
+		if (!m_HttpRequest->extendLayer(0, lengthDifference))
+		{
+			LOG_ERROR("Cannot change layer size");
+			return false;
+		}
+	}
+	else if (lengthDifference < 0)
+	{
+		if (!m_HttpRequest->shortenLayer(0, 0-lengthDifference))
+		{
+			LOG_ERROR("Cannot change layer size");
+			return false;
+
+		}
+	}
+
+	if (lengthDifference != 0)
+		m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), lengthDifference);
+
+	memcpy(m_HttpRequest->m_Data, MethodEnumToString[newMethod].c_str(), MethodEnumToString[newMethod].length());
+
+	m_UriOffset += lengthDifference;
+	m_VersionOffset += lengthDifference;
+
+	return true;
+}
+
+std::string HttpRequestFirstLine::getUri()
+{
+	std::string result;
+	if (m_UriOffset != -1 && m_VersionOffset != -1)
+		result.assign((char*)(m_HttpRequest->m_Data + m_UriOffset), m_VersionOffset-6-m_UriOffset);
+
+	//else first line is illegal, return empty string
+
+	return result;
+}
+
+bool HttpRequestFirstLine::setUri(std::string newUri)
+{
+	// make sure the new URI begins with "/"
+	if (newUri.compare(0, 1, "/") != 0)
+		newUri = "/" + newUri;
+
+	//extend or shorten layer
+	std::string currentUri = getUri();
+	int lengthDifference = newUri.length() - currentUri.length();
+	if (lengthDifference > 0)
+	{
+		if (!m_HttpRequest->extendLayer(m_UriOffset, lengthDifference))
+		{
+			LOG_ERROR("Cannot change layer size");
+			return false;
+		}
+	}
+	else if (lengthDifference < 0)
+	{
+		if (!m_HttpRequest->shortenLayer(m_UriOffset, 0-lengthDifference))
+		{
+			LOG_ERROR("Cannot change layer size");
+			return false;
+		}
+	}
+
+	if (lengthDifference != 0)
+		m_HttpRequest->shiftFieldsOffset(m_HttpRequest->getFirstField(), lengthDifference);
+
+	memcpy(m_HttpRequest->m_Data + m_UriOffset, newUri.c_str(), newUri.length());
+
+	m_VersionOffset += lengthDifference;
+
+	return true;
+}
+
+void HttpRequestFirstLine::setVersion(HttpVersion newVersion)
+{
+	if (m_VersionOffset == -1)
+		return;
+
+	if (newVersion == HttpVersionUnknown)
+		return;
+
+	char* verPos = (char*)(m_HttpRequest->m_Data + m_VersionOffset);
+	memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3);
+}
+
+
+
+
+
+
+// -------- Class HttpResponseLayer -----------------
+
+
+
+const std::string StatusCodeEnumToString[80] = {
+		"Continue",
+		"Switching Protocols",
+		"Processing",
+		"OK",
+		"Created",
+		"Accepted",
+		"Non-Authoritative Information",
+		"No Content",
+		"Reset Content",
+		"Partial Content",
+		"Multi-Status",
+		"Already Reported",
+		"IM Used",
+		"Multiple Choices",
+		"Moved Permanently",
+		"Found",
+		"See Other",
+		"Not Modified",
+		"Use Proxy",
+		"Switch Proxy",
+		"Temporary Redirect",
+		"Permanent Redirect",
+		"Bad Request",
+		"Unauthorized",
+		"Payment Required",
+		"Forbidden",
+		"Not Found",
+		"Method Not Allowed",
+		"Not Acceptable",
+		"Proxy Authentication Required",
+		"Request Timeout",
+		"Conflict",
+		"Gone",
+		"Length Required",
+		"Precondition Failed",
+		"Request Entity Too Large",
+		"Request-URI Too Long",
+		"Unsupported Media Type",
+		"Requested Range Not Satisfiable",
+		"Expectation Failed",
+		"I'm a teapot",
+		"Authentication Timeout",
+		"Method Failure",
+		"Unprocessable Entity",
+		"Locked",
+		"Failed Dependency",
+		"Upgrade Required",
+		"Precondition Required",
+		"Too Many Requests",
+		"Request Header Fields Too Large",
+		"Login Timeout",
+		"No Response",
+		"Retry With",
+		"Blocked by Windows Parental Controls",
+		"Unavailable For Legal Reasons",
+		"Request Header Too Large",
+		"Cert Error",
+		"No Cert",
+		"HTTP to HTTPS",
+		"Token expired/invalid",
+		"Client Closed Request",
+		"Internal Server Error",
+		"Not Implemented",
+		"Bad Gateway",
+		"Service Unavailable",
+		"Gateway Timeout",
+		"HTTP Version Not Supported",
+		"Variant Also Negotiates",
+		"Insufficient Storage",
+		"Loop Detected",
+		"Bandwidth Limit Exceeded",
+		"Not Extended",
+		"Network Authentication Required",
+		"Origin Error",
+		"Web server is down",
+		"Connection timed out",
+		"Proxy Declined Request",
+		"A timeout occurred",
+		"Network read timeout error",
+		"Network connect timeout error"
+};
+
+
+const int StatusCodeEnumToInt[80] = {
+		100,
+		101,
+		102,
+		200,
+		201,
+		202,
+		203,
+		204,
+		205,
+		206,
+		207,
+		208,
+		226,
+		300,
+		301,
+		302,
+		303,
+		304,
+		305,
+		306,
+		307,
+		308,
+		400,
+		401,
+		402,
+		403,
+		404,
+		405,
+		406,
+		407,
+		408,
+		409,
+		410,
+		411,
+		412,
+		413,
+		414,
+		415,
+		416,
+		417,
+		418,
+		419,
+		420,
+		422,
+		423,
+		424,
+		426,
+		428,
+		429,
+		431,
+		440,
+		444,
+		449,
+		450,
+		451,
+		494,
+		495,
+		496,
+		497,
+		498,
+		499,
+		500,
+		501,
+		502,
+		503,
+		504,
+		505,
+		506,
+		507,
+		508,
+		509,
+		510,
+		511,
+		520,
+		521,
+		522,
+		523,
+		524,
+		598,
+		599
+};
+
+
+
+HttpResponseLayer::HttpResponseLayer(uint8_t* data, size_t dataLen, Layer* prevLayer, Packet* packet)  : HttpMessage(data, dataLen, prevLayer, packet)
+{
+	m_Protocol = HTTPResponse;
+	m_FirstLine = new HttpResponseFirstLine(this);
+	m_FieldsOffset = m_FirstLine->getSize();
+	parseFields();
+}
+
+HttpResponseLayer::HttpResponseLayer(HttpVersion version, HttpResponseLayer::HttpResponseStatusCode statusCode, std::string statusCodeString)
+{
+	m_Protocol = HTTPResponse;
+	m_FirstLine = new HttpResponseFirstLine(this, version, statusCode, statusCodeString);
+	m_FieldsOffset = m_FirstLine->getSize();
+}
+
+HttpResponseLayer::~HttpResponseLayer()
+{
+	delete m_FirstLine;
+}
+
+
+HttpResponseLayer::HttpResponseLayer(const HttpResponseLayer& other) : HttpMessage(other)
+{
+	m_FirstLine = new HttpResponseFirstLine(this);
+}
+
+HttpResponseLayer& HttpResponseLayer::operator=(const HttpResponseLayer& other)
+{
+	HttpMessage::operator=(other);
+
+	if (m_FirstLine != NULL)
+		delete m_FirstLine;
+
+	m_FirstLine = new HttpResponseFirstLine(this);
+
+	return *this;
+}
+
+
+HeaderField* HttpResponseLayer::setContentLength(int contentLength, const std::string prevFieldName)
+{
+	char contentLengthAsString[20];
+	snprintf (contentLengthAsString, sizeof(contentLengthAsString), "%d",contentLength);
+	std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD);
+	HeaderField* contentLengthField = getFieldByName(contentLengthFieldName);
+	if (contentLengthField == NULL)
+	{
+		HeaderField* prevField = getFieldByName(prevFieldName);
+		contentLengthField = insertField(prevField, PCPP_HTTP_CONTENT_LENGTH_FIELD, contentLengthAsString);
+	}
+	else
+		contentLengthField->setFieldValue(std::string(contentLengthAsString));
+
+	return contentLengthField;
+}
+
+int HttpResponseLayer::getContentLength()
+{
+	std::string contentLengthFieldName(PCPP_HTTP_CONTENT_LENGTH_FIELD);
+	std::transform(contentLengthFieldName.begin(), contentLengthFieldName.end(), contentLengthFieldName.begin(), ::tolower);
+	HeaderField* contentLengthField = getFieldByName(contentLengthFieldName);
+	if (contentLengthField != NULL)
+		return atoi(contentLengthField->getFieldValue().c_str());
+	return 0;
+}
+
+std::string HttpResponseLayer::toString()
+{
+	static const int maxLengthToPrint = 120;
+	std::string result = "HTTP response, ";
+	int size = m_FirstLine->getSize() - 2; // the -2 is to remove \r\n at the end of the first line
+	if (size <= maxLengthToPrint)
+	{
+		char* firstLine = new char[size+1];
+		strncpy(firstLine, (char*)m_Data, size);
+		firstLine[size] = 0;
+		result += std::string(firstLine);
+		delete[] firstLine;
+	}
+	else
+	{
+		char firstLine[maxLengthToPrint+1];
+		strncpy(firstLine, (char*)m_Data, maxLengthToPrint-3);
+		firstLine[maxLengthToPrint-3] = '.';
+		firstLine[maxLengthToPrint-2] = '.';
+		firstLine[maxLengthToPrint-1] = '.';
+		firstLine[maxLengthToPrint] = 0;
+		result += std::string(firstLine);
+	}
+
+	return result;
+}
+
+
+
+
+
+
+
+
+// -------- Class HttpResponseFirstLine -----------------
+
+
+
+int HttpResponseFirstLine::getStatusCodeAsInt()
+{
+	return StatusCodeEnumToInt[m_StatusCode];
+}
+
+std::string HttpResponseFirstLine::getStatusCodeString()
+{
+	std::string result;
+	int statusStringOffset = 13;
+	if (m_StatusCode != HttpResponseLayer::HttpStatusCodeUnknown)
+	{
+		int statusStringEndOffset = m_FirstLineEndOffset - 2;
+		if ((*(m_HttpResponse->m_Data + statusStringEndOffset)) != '\r')
+			statusStringEndOffset++;
+		result.assign((char*)(m_HttpResponse->m_Data + statusStringOffset), statusStringEndOffset-statusStringOffset);
+	}
+
+	//else first line is illegal, return empty string
+
+	return result;
+}
+
+bool HttpResponseFirstLine::setStatusCode(HttpResponseLayer::HttpResponseStatusCode newStatusCode, std::string statusCodeString)
+{
+	if (newStatusCode == HttpResponseLayer::HttpStatusCodeUnknown)
+	{
+		LOG_ERROR("Requested status code is HttpStatusCodeUnknown");
+		return false;
+	}
+
+	//extend or shorten layer
+
+	size_t statusStringOffset = 13;
+	if (statusCodeString == "")
+		statusCodeString = StatusCodeEnumToString[newStatusCode];
+	int lengthDifference = statusCodeString.length() - getStatusCodeString().length();
+	if (lengthDifference > 0)
+	{
+		if (!m_HttpResponse->extendLayer(statusStringOffset, lengthDifference))
+		{
+			LOG_ERROR("Cannot change layer size");
+			return false;
+		}
+	}
+	else if (lengthDifference < 0)
+	{
+		if (!m_HttpResponse->shortenLayer(statusStringOffset, 0-lengthDifference))
+		{
+			LOG_ERROR("Cannot change layer size");
+			return false;
+
+		}
+	}
+
+	if (lengthDifference != 0)
+		m_HttpResponse->shiftFieldsOffset(m_HttpResponse->getFirstField(), lengthDifference);
+
+	// copy status string
+	memcpy(m_HttpResponse->m_Data+statusStringOffset, statusCodeString.c_str(), statusCodeString.length());
+
+	// change status code
+	char statusCodeAsString[4];
+	// convert code to string
+	snprintf (statusCodeAsString, sizeof(statusCodeAsString), "%d",StatusCodeEnumToInt[newStatusCode]);
+
+	memcpy(m_HttpResponse->m_Data+9, statusCodeAsString, 3);
+
+	m_StatusCode = newStatusCode;
+
+	m_FirstLineEndOffset += lengthDifference;
+
+	return true;
+
+}
+
+void HttpResponseFirstLine::setVersion(HttpVersion newVersion)
+{
+	if (newVersion == HttpVersionUnknown)
+		return;
+
+	char* verPos = (char*)(m_HttpResponse->m_Data + 5);
+	memcpy(verPos, VersionEnumToString[newVersion].c_str(), 3);
+}
+
+HttpResponseLayer::HttpResponseStatusCode HttpResponseFirstLine::validateStatusCode(char* data, size_t dataLen, HttpResponseLayer::HttpResponseStatusCode potentialCode)
+{
+	if (data[0] != ' ')
+		return HttpResponseLayer::HttpStatusCodeUnknown;
+
+	return potentialCode;
+}
+
+HttpResponseLayer::HttpResponseStatusCode HttpResponseFirstLine::parseStatusCode(char* data, size_t dataLen)
+{
+	if (parseVersion(data, dataLen) == HttpVersionUnknown)
+		return HttpResponseLayer::HttpStatusCodeUnknown;
+
+	// minimum data should be 12B long: "HTTP/x.y XXX"
+	if (dataLen < 12)
+		return HttpResponseLayer::HttpStatusCodeUnknown;
+
+	char* statusCodeData = data + 9;
+	size_t statusCodeDataLen = dataLen - 9;
+
+	switch (statusCodeData[0])
+	{
+	case '1':
+		switch (statusCodeData[1])
+		{
+		case '0':
+			switch (statusCodeData[2])
+			{
+			case '0':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http100Continue);
+			case '1':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http101SwitchingProtocols);
+			case '2':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http102Processing);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+			};
+
+			break;
+
+		default:
+			return HttpResponseLayer::HttpStatusCodeUnknown;
+		};
+
+		break;
+	case '2':
+		switch (statusCodeData[1])
+		{
+		case '0':
+			switch (statusCodeData[2])
+			{
+			case '0':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http200OK);
+			case '1':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http201Created);
+			case '2':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http202Accepted);
+			case '3':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http203NonAuthoritativeInformation);
+			case '4':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http204NoContent);
+			case '5':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::http205ResetContent);
+			case '6':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http206PartialContent);
+			case '7':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http207MultiStatus);
+			case '8':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http208AlreadyReported);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+
+			};
+
+			break;
+		case '2':
+			switch (statusCodeData[2])
+			{
+			case '6':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http226IMUsed);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+			};
+
+			break;
+
+		default:
+			return HttpResponseLayer::HttpStatusCodeUnknown;
+
+		};
+
+		break;
+
+	case '3':
+		switch (statusCodeData[1])
+		{
+		case '0':
+			switch (statusCodeData[2])
+			{
+			case '0':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http300MultipleChoices);
+			case '1':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http301MovedPermanently);
+			case '2':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http302);
+			case '3':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http303SeeOther);
+			case '4':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http304NotModified);
+			case '5':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http305UseProxy);
+			case '6':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http306SwitchProxy);
+			case '7':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http307TemporaryRedirect);
+			case '8':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http308PermanentRedirect);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+
+			};
+
+			break;
+
+		default:
+			return HttpResponseLayer::HttpStatusCodeUnknown;
+		};
+
+		break;
+
+	case '4':
+		switch (statusCodeData[1])
+		{
+		case '0':
+			switch (statusCodeData[2])
+			{
+			case '0':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http400BadRequest);
+			case '1':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http401Unauthorized);
+			case '2':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http402PaymentRequired);
+			case '3':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http403Forbidden);
+			case '4':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http404NotFound);
+			case '5':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http405MethodNotAllowed);
+			case '6':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http406NotAcceptable);
+			case '7':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http407ProxyAuthenticationRequired);
+			case '8':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http408RequestTimeout);
+			case '9':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http409Conflict);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+
+			};
+
+			break;
+
+		case '1':
+			switch (statusCodeData[2])
+			{
+			case '0':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http410Gone);
+			case '1':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http411LengthRequired);
+			case '2':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http412PreconditionFailed);
+			case '3':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http413RequestEntityTooLarge);
+			case '4':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http414RequestURITooLong);
+			case '5':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http415UnsupportedMediaType);
+			case '6':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http416RequestedRangeNotSatisfiable);
+			case '7':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http417ExpectationFailed);
+			case '8':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http418Imateapot);
+			case '9':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http419AuthenticationTimeout);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+
+			};
+
+			break;
+
+		case '2':
+			switch (statusCodeData[2])
+			{
+			case '0':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http420);
+			case '2':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http422UnprocessableEntity);
+			case '3':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http423Locked);
+			case '4':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http424FailedDependency);
+			case '6':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http426UpgradeRequired);
+			case '8':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http428PreconditionRequired);
+			case '9':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http429TooManyRequests);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+
+			};
+
+			break;
+
+		case '3':
+			return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http431RequestHeaderFieldsTooLarge);
+
+		case '4':
+			switch (statusCodeData[2])
+			{
+			case '0':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http440LoginTimeout);
+			case '4':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http444NoResponse);
+			case '9':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http449RetryWith);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+			};
+
+			break;
+
+		case '5':
+			switch (statusCodeData[2])
+			{
+			case '0':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http450BlockedByWindowsParentalControls);
+			case '1':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http451);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+			};
+
+			break;
+
+		case '9':
+			switch (statusCodeData[2])
+			{
+			case '4':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http494RequestHeaderTooLarge);
+			case '5':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http495CertError);
+			case '6':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http496NoCert);
+			case '7':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http497HTTPtoHTTPS);
+			case '8':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http498TokenExpiredInvalid);
+			case '9':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http499);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+			};
+
+			break;
+
+		default:
+			return HttpResponseLayer::HttpStatusCodeUnknown;
+		};
+
+		break;
+
+	case '5':
+		switch (statusCodeData[1])
+		{
+		case '0':
+			switch (statusCodeData[2])
+			{
+			case '0':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http500InternalServerError);
+			case '1':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http501NotImplemented);
+			case '2':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http502BadGateway);
+			case '3':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http503ServiceUnavailable);
+			case '4':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http504GatewayTimeout);
+			case '5':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http505HTTPVersionNotSupported);
+			case '6':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http506VariantAlsoNegotiates);
+			case '7':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http507InsufficientStorage);
+			case '8':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http508LoopDetected);
+			case '9':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http509BandwidthLimitExceeded);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+
+			};
+
+			break;
+
+		case '1':
+			switch (statusCodeData[2])
+			{
+			case '0':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http510NotExtended);
+			case '1':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http511NetworkAuthenticationRequired);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+			};
+
+			break;
+
+		case '2':
+			switch (statusCodeData[2])
+			{
+			case '0':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http520OriginError);
+			case '1':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http521WebServerIsDown);
+			case '2':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http522ConnectionTimedOut);
+			case '3':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http523ProxyDeclinedRequest);
+			case '4':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http524aTimeoutOccurred);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+			};
+
+			break;
+
+		case '9':
+			switch (statusCodeData[2])
+			{
+			case '8':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http598NetworkReadTimeoutError);
+			case '9':
+				return validateStatusCode(statusCodeData+3, statusCodeDataLen-3, HttpResponseLayer::Http599NetworkConnectTimeoutError);
+			default:
+				return HttpResponseLayer::HttpStatusCodeUnknown;
+			};
+
+			break;
+
+		default:
+			return HttpResponseLayer::HttpStatusCodeUnknown;
+		};
+
+		break;
+
+	default:
+		return HttpResponseLayer::HttpStatusCodeUnknown;
+	}
+
+	return HttpResponseLayer::HttpStatusCodeUnknown;
+}
+
+HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse) : m_HttpResponse(httpResponse)
+{
+	m_Version = parseVersion((char*)m_HttpResponse->m_Data, m_HttpResponse->getDataLen());
+	if (m_Version == HttpVersionUnknown)
+	{
+		m_StatusCode = HttpResponseLayer::HttpStatusCodeUnknown;
+	}
+	else
+	{
+		m_StatusCode = parseStatusCode((char*)m_HttpResponse->m_Data, m_HttpResponse->getDataLen());
+	}
+
+
+	char* endOfFirstLine;
+	if ((endOfFirstLine = (char *)memchr((char*)(m_HttpResponse->m_Data), '\n', m_HttpResponse->m_DataLen)) != NULL)
+	{
+		m_FirstLineEndOffset = endOfFirstLine - (char*)m_HttpResponse->m_Data + 1;
+		m_IsComplete = true;
+	}
+	else
+	{
+		m_FirstLineEndOffset = m_HttpResponse->getDataLen();
+		m_IsComplete = false;
+	}
+
+	LOG_DEBUG("Version='%s'; Status code=%d '%s'", VersionEnumToString[m_Version].c_str(), StatusCodeEnumToInt[m_StatusCode], getStatusCodeString().c_str());
+}
+
+
+HttpResponseFirstLine::HttpResponseFirstLine(HttpResponseLayer* httpResponse,  HttpVersion version, HttpResponseLayer::HttpResponseStatusCode statusCode, std::string statusCodeString)
+{
+	if (statusCode == HttpResponseLayer::HttpStatusCodeUnknown)
+	{
+		m_Exception.setMessage("Status code supplied was HttpStatusCodeUnknown");
+		throw m_Exception;
+	}
+
+	if (version == HttpVersionUnknown)
+	{
+		m_Exception.setMessage("Version supplied was HttpVersionUnknown");
+		throw m_Exception;
+	}
+
+	m_HttpResponse = httpResponse;
+
+	m_StatusCode = statusCode;
+	m_Version = version;
+
+	char statusCodeAsString[4];
+	snprintf (statusCodeAsString, sizeof(statusCodeAsString), "%d",StatusCodeEnumToInt[m_StatusCode]);
+	if (statusCodeString == "")
+		statusCodeString = StatusCodeEnumToString[m_StatusCode];
+	std::string firstLine = "HTTP/" + VersionEnumToString[m_Version] + " " + std::string(statusCodeAsString) + " " +  statusCodeString +  "\r\n";
+
+	m_FirstLineEndOffset = firstLine.length();
+
+	m_HttpResponse->m_DataLen = firstLine.length();
+	m_HttpResponse->m_Data = new uint8_t[m_HttpResponse->m_DataLen];
+	memcpy(m_HttpResponse->m_Data, firstLine.c_str(), m_HttpResponse->m_DataLen);
+
+	m_IsComplete = true;
+}
+
+HttpVersion HttpResponseFirstLine::parseVersion(char* data, size_t dataLen)
+{
+	if (dataLen < 8) // "HTTP/x.y"
+	{
+		LOG_DEBUG("HTTP response length < 8, cannot identify version");
+		return HttpVersionUnknown;
+	}
+
+	if (data[0] != 'H' || data[1] != 'T' || data[2] != 'T' || data[3] != 'P' || data[4] != '/')
+	{
+		LOG_DEBUG("HTTP response does not begin with 'HTTP/'");
+		return HttpVersionUnknown;
+	}
+
+	char* verPos = data + 5;
+	switch (verPos[0])
+	{
+	case '0':
+		if (verPos[1] == '.' && verPos[2] == '9')
+			return ZeroDotNine;
+		else
+			return HttpVersionUnknown;
+		break;
+
+	case '1':
+		if (verPos[1] == '.' && verPos[2] == '0')
+			return OneDotZero;
+		else if (verPos[1] == '.' && verPos[2] == '1')
+			return OneDotOne;
+		else
+			return HttpVersionUnknown;
+		break;
+
+	default:
+		return HttpVersionUnknown;
+	}
+}
+
+} // namespace pcpp

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/c0a788b3/thirdparty/pcap++/Packet++/src/IPv4Layer.cpp
----------------------------------------------------------------------
diff --git a/thirdparty/pcap++/Packet++/src/IPv4Layer.cpp b/thirdparty/pcap++/Packet++/src/IPv4Layer.cpp
new file mode 100644
index 0000000..94c050e
--- /dev/null
+++ b/thirdparty/pcap++/Packet++/src/IPv4Layer.cpp
@@ -0,0 +1,609 @@
+#define LOG_MODULE PacketLogModuleIPv4Layer
+
+#include <IPv4Layer.h>
+#include <IPv6Layer.h>
+#include <PayloadLayer.h>
+#include <UdpLayer.h>
+#include <TcpLayer.h>
+#include <IcmpLayer.h>
+#include <GreLayer.h>
+#include <IgmpLayer.h>
+#include <string.h>
+#include <sstream>
+#include <IpUtils.h>
+#include <Logger.h>
+
+namespace pcpp
+{
+
+#define IPV4OPT_DUMMMY 0xff
+#define IPV4_MAX_OPT_SIZE 40
+
+void IPv4Layer::initLayer()
+{
+	m_DataLen = sizeof(iphdr);
+	m_Data = new uint8_t[m_DataLen];
+	m_Protocol = IPv4;
+	memset(m_Data, 0, sizeof(iphdr));
+	iphdr* ipHdr = getIPv4Header();
+	ipHdr->internetHeaderLength = (5 & 0xf);
+	setOptionCount(-1);
+	m_NumOfTrailingBytes = 0;
+	m_TempHeaderExtension = 0;
+}
+
+void IPv4Layer::copyLayerData(const IPv4Layer& other)
+{
+	m_OptionCount = other.m_OptionCount;
+	m_NumOfTrailingBytes = other.m_NumOfTrailingBytes;
+	m_TempHeaderExtension = other.m_TempHeaderExtension;
+}
+
+IPv4OptionData* IPv4Layer::castPtrToOptionData(uint8_t* ptr)
+{
+	return (IPv4OptionData*)ptr;
+}
+
+IPv4Layer::IPv4Layer()
+{
+	initLayer();
+}
+
+IPv4Layer::IPv4Layer(const IPv4Address& srcIP, const IPv4Address& dstIP)
+{
+	initLayer();
+	iphdr* ipHdr = getIPv4Header();
+	ipHdr->ipSrc = srcIP.toInt();
+	ipHdr->ipDst = dstIP.toInt();
+}
+
+IPv4Layer::IPv4Layer(const IPv4Layer& other) : Layer(other)
+{
+	copyLayerData(other);
+}
+
+IPv4Layer& IPv4Layer::operator=(const IPv4Layer& other)
+{
+	Layer::operator=(other);
+
+	copyLayerData(other);
+
+	return *this;
+}
+
+void IPv4Layer::parseNextLayer()
+{
+	size_t hdrLen = getHeaderLen();
+	if (m_DataLen <= hdrLen)
+		return;
+
+	iphdr* ipHdr = getIPv4Header();
+
+	ProtocolType greVer = UnknownProtocol;
+	ProtocolType igmpVer = UnknownProtocol;
+	bool igmpQuery = false;
+
+	uint8_t ipVersion = 0;
+
+	// If it's a fragment don't parse upper layers, unless if it's the first fragment
+	// TODO: assuming first fragment contains at least L4 header, what if it's not true?
+	if (isFragment())
+	{
+		m_NextLayer = new PayloadLayer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		return;
+	}
+
+	switch (ipHdr->protocol)
+	{
+	case PACKETPP_IPPROTO_UDP:
+		if (m_DataLen - hdrLen >= sizeof(udphdr))
+			m_NextLayer = new UdpLayer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		break;
+	case PACKETPP_IPPROTO_TCP:
+		if (m_DataLen - hdrLen >= sizeof(tcphdr))
+			m_NextLayer = new TcpLayer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		break;
+	case PACKETPP_IPPROTO_ICMP:
+		m_NextLayer = new IcmpLayer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		break;
+	case PACKETPP_IPPROTO_IPIP:
+		ipVersion = *(m_Data + hdrLen);
+		if (ipVersion >> 4 == 4)
+			m_NextLayer = new IPv4Layer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		else if (ipVersion >> 4 == 6)
+			m_NextLayer = new IPv6Layer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		else
+			m_NextLayer = new PayloadLayer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		break;
+	case PACKETPP_IPPROTO_GRE:
+		greVer = GreLayer::getGREVersion(m_Data + hdrLen, m_DataLen - hdrLen);
+		if (greVer == GREv0)
+			m_NextLayer = new GREv0Layer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		else if (greVer == GREv1)
+			m_NextLayer = new GREv1Layer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		else
+			m_NextLayer = new PayloadLayer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		break;
+	case PACKETPP_IPPROTO_IGMP:
+		igmpVer = IgmpLayer::getIGMPVerFromData(m_Data + hdrLen, ntohs(getIPv4Header()->totalLength) - hdrLen, igmpQuery);
+		if (igmpVer == IGMPv1)
+			m_NextLayer = new IgmpV1Layer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		else if (igmpVer == IGMPv2)
+			m_NextLayer = new IgmpV2Layer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		else if (igmpVer == IGMPv3)
+		{
+			if (igmpQuery)
+				m_NextLayer = new IgmpV3QueryLayer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+			else
+				m_NextLayer = new IgmpV3ReportLayer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		}
+		else
+			m_NextLayer = new PayloadLayer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		break;
+	default:
+		m_NextLayer = new PayloadLayer(m_Data + hdrLen, m_DataLen - hdrLen, this, m_Packet);
+		return;
+	}
+}
+
+void IPv4Layer::computeCalculateFields()
+{
+	iphdr* ipHdr = getIPv4Header();
+	ipHdr->ipVersion = (4 & 0x0f);
+	ipHdr->totalLength = htons(m_DataLen);
+	ipHdr->headerChecksum = 0;
+
+	if (m_NextLayer != NULL)
+	{
+		switch (m_NextLayer->getProtocol())
+		{
+		case TCP:
+			ipHdr->protocol = PACKETPP_IPPROTO_TCP;
+			break;
+		case UDP:
+			ipHdr->protocol = PACKETPP_IPPROTO_UDP;
+			break;
+		case ICMP:
+			ipHdr->protocol = PACKETPP_IPPROTO_ICMP;
+			break;
+		case GREv0:
+		case GREv1:
+			ipHdr->protocol = PACKETPP_IPPROTO_GRE;
+			break;
+		case IGMPv1:
+		case IGMPv2:
+		case IGMPv3:
+			ipHdr->protocol = PACKETPP_IPPROTO_IGMP;
+			break;
+		default:
+			break;
+		}
+	}
+
+	ScalarBuffer<uint16_t> scalar = { (uint16_t*)ipHdr, (size_t)(ipHdr->internetHeaderLength*4) } ;
+	ipHdr->headerChecksum = htons(compute_checksum(&scalar, 1));
+}
+
+bool IPv4Layer::isFragment()
+{
+	return ((getFragmentFlags() & PCPP_IP_MORE_FRAGMENTS) != 0 || getFragmentOffset() != 0);
+}
+
+bool IPv4Layer::isFirstFragment()
+{
+	return isFragment() && (getFragmentOffset() == 0);
+}
+
+bool IPv4Layer::isLastFragment()
+{
+	return isFragment() && ((getFragmentFlags() & PCPP_IP_MORE_FRAGMENTS) == 0);
+}
+
+uint8_t IPv4Layer::getFragmentFlags()
+{
+	return getIPv4Header()->fragmentOffset & 0xE0;
+}
+
+uint16_t IPv4Layer::getFragmentOffset()
+{
+	return ntohs(getIPv4Header()->fragmentOffset & (uint16_t)0xFF1F) * 8;
+}
+
+std::string IPv4Layer::toString()
+{
+	std::string fragmet = "";
+	if (isFragment())
+	{
+		if (isFirstFragment())
+			fragmet = "First fragment";
+		else if (isLastFragment())
+			fragmet = "Last fragment";
+		else
+			fragmet = "Fragment";
+
+		std::stringstream sstm;
+		sstm << fragmet << " [offset= " << getFragmentOffset() << "], ";
+		fragmet = sstm.str();
+	}
+
+
+	return "IPv4 Layer, " + fragmet + "Src: " + getSrcIpAddress().toString() + ", Dst: " + getDstIpAddress().toString();
+}
+
+IPv4OptionData* IPv4Layer::getOptionData(IPv4OptionTypes option)
+{
+	// check if there are options at all
+	if (m_DataLen <= sizeof(iphdr))
+		return NULL;
+
+	IPv4OptionData* curOpt = getFirstOptionData();
+	while (curOpt != NULL)
+	{
+		if ((int)curOpt->opCode == option)
+			return curOpt;
+
+		curOpt = getNextOptionData(curOpt);
+	}
+
+	return NULL;
+}
+
+IPv4OptionData* IPv4Layer::getFirstOptionData()
+{
+	// check if there are IPv4 options at all
+	if (getHeaderLen() <= sizeof(iphdr))
+		return NULL;
+
+	uint8_t* curOptPtr = m_Data + sizeof(iphdr);
+	return castPtrToOptionData(curOptPtr);
+}
+
+IPv4OptionData* IPv4Layer::getNextOptionData(IPv4OptionData* option)
+{
+	if (option == NULL)
+		return NULL;
+
+	// prev opt was the last opt
+	if ((uint8_t*)option + option->getTotalSize() - m_Data >= (int)getHeaderLen())
+		return NULL;
+
+	IPv4OptionData* nextOption = castPtrToOptionData((uint8_t*)option + option->getTotalSize());
+
+	if (nextOption->opCode == IPV4OPT_DUMMMY)
+		return NULL;
+
+	return nextOption;
+}
+
+size_t IPv4Layer::getOptionsCount()
+{
+	if (m_OptionCount != (size_t)-1)
+		return m_OptionCount;
+
+	m_OptionCount = 0;
+	IPv4OptionData* curOpt = getFirstOptionData();
+	while (curOpt != NULL)
+	{
+		m_OptionCount++;
+		curOpt = getNextOptionData(curOpt);
+	}
+
+	return m_OptionCount;
+}
+
+void IPv4Layer::incOptionCount(int val)
+{
+	if (m_OptionCount == (size_t)-1)
+		getOptionsCount(); // this method already calculates the current number of options, no need to increment/decrement m_OptionCount again
+	else
+		m_OptionCount += val;
+}
+
+void IPv4Layer::setOptionCount(int val)
+{
+	m_OptionCount = val;
+}
+
+void IPv4Layer::adjustOptionsTrailer(size_t totalOptSize)
+{
+	size_t ipHdrSize = sizeof(iphdr);
+
+	int newNumberOfTrailingBytes = 0;
+	while ((totalOptSize + newNumberOfTrailingBytes) % 4 != 0)
+		newNumberOfTrailingBytes++;
+
+	if (newNumberOfTrailingBytes < m_NumOfTrailingBytes)
+		shortenLayer(ipHdrSize+totalOptSize, m_NumOfTrailingBytes - newNumberOfTrailingBytes);
+	else if (newNumberOfTrailingBytes > m_NumOfTrailingBytes)
+		extendLayer(ipHdrSize+totalOptSize, newNumberOfTrailingBytes - m_NumOfTrailingBytes);
+
+	m_NumOfTrailingBytes = newNumberOfTrailingBytes;
+
+	for (int i = 0; i < m_NumOfTrailingBytes; i++)
+		m_Data[ipHdrSize + totalOptSize + i] = IPV4OPT_DUMMMY;
+
+	m_TempHeaderExtension = 0;
+	getIPv4Header()->internetHeaderLength = ((ipHdrSize + totalOptSize + m_NumOfTrailingBytes)/4 & 0x0f);
+}
+
+IPv4OptionData* IPv4Layer::addOptionAt(IPv4OptionTypes optionType, uint8_t optionDataLength, const uint8_t* optionData, int offset)
+{
+	size_t sizeToExtend = optionDataLength + 2*sizeof(uint8_t);
+
+	if ((optionType == IPV4OPT_NOP || optionType == IPV4OPT_EndOfOtionsList))
+	{
+		if (optionDataLength != 0)
+		{
+			LOG_ERROR("Can't set IPv4 NOP option or IPv4 End-of-options option with size different than 0, tried to set size %d", optionDataLength);
+			return NULL;
+		}
+
+		sizeToExtend = sizeof(uint8_t);
+	}
+
+	size_t totalOptSize = getHeaderLen() - sizeof(iphdr) - m_NumOfTrailingBytes + sizeToExtend;
+
+	if (totalOptSize > IPV4_MAX_OPT_SIZE)
+	{
+		LOG_ERROR("Cannot add option - adding this option will exceed IPv4 total option size which is %d", IPV4_MAX_OPT_SIZE);
+		return NULL;
+	}
+
+	if (!extendLayer(offset, sizeToExtend))
+	{
+		LOG_ERROR("Could not extend IPv4Layer in [%d] bytes", (int)sizeToExtend);
+		return NULL;
+	}
+
+	uint8_t optionTypeVal = (uint8_t)optionType;
+	memcpy(m_Data + offset, &optionTypeVal, sizeof(uint8_t));
+
+	if (sizeToExtend > 1)
+	{
+		memcpy(m_Data + offset + sizeof(uint8_t), &sizeToExtend, sizeof(uint8_t));
+		if (sizeToExtend > 2 && optionData != NULL)
+			memcpy(m_Data + offset + 2*sizeof(uint8_t), optionData, optionDataLength);
+	}
+
+	// setting this m_TempHeaderExtension because adjustOptionsTrailer() may extend or shorten the layer and the extend or shorten methods need to know the accurate
+	// current size of the header. m_TempHeaderExtension will be added to the length extracted from getIPv4Header()->internetHeaderLength as the temp new size
+	m_TempHeaderExtension = sizeToExtend;
+	adjustOptionsTrailer(totalOptSize);
+	// the adjustOptionsTrailer() adds or removed the trailing bytes and sets getIPv4Header()->internetHeaderLength to the correct size, so the m_TempHeaderExtension
+	// isn't needed anymore
+	m_TempHeaderExtension = 0;
+
+	uint8_t* newOptPtr = m_Data + offset;
+
+	incOptionCount(1);
+
+	return castPtrToOptionData(newOptPtr);
+}
+
+void IPv4Layer::buildIPListOptionData(const std::vector<IPv4Address>& ipList, uint8_t** optionData, int& optionDataLength)
+{
+	optionDataLength = ipList.size()*sizeof(uint32_t) + sizeof(uint8_t);
+	(*optionData) = new uint8_t[optionDataLength];
+
+	size_t curOffset = 0;
+	(*optionData)[curOffset++] = 0; // init pointer value
+
+	bool firstZero = false;
+	for (std::vector<IPv4Address>::const_iterator iter = ipList.begin(); iter != ipList.end(); iter++)
+	{
+		uint32_t ipAddrAsInt = iter->toInt();
+
+		if (!firstZero)
+			(*optionData)[0] += (uint8_t)4;
+
+		if (!firstZero && ipAddrAsInt == 0)
+			firstZero = true;
+
+		memcpy((*optionData) + curOffset , &ipAddrAsInt, sizeof(uint32_t));
+		curOffset += sizeof(uint32_t);
+	}
+}
+
+void IPv4Layer::buildTimestampOptionData(const IPv4TimestampOptionValue& timestampVal, uint8_t** optionData, int& optionDataLength)
+{
+	optionDataLength = 0;
+	*optionData = NULL;
+
+	if (timestampVal.type == IPv4TimestampOptionValue::Unknown)
+	{
+		LOG_ERROR("Cannot build timestamp option of type IPv4TimestampOptionValue::Unknown");
+		return;
+	}
+
+	if (timestampVal.type == IPv4TimestampOptionValue::TimestampsForPrespecifiedIPs)
+	{
+		LOG_ERROR("Cannot build timestamp option of type IPv4TimestampOptionValue::TimestampsForPrespecifiedIPs - this type is not supported");
+		return;
+	}
+
+	if (timestampVal.type == IPv4TimestampOptionValue::TimestampAndIP && timestampVal.timestamps.size() != timestampVal.ipAddresses.size())
+	{
+		LOG_ERROR("Cannot build timestamp option of type IPv4TimestampOptionValue::TimestampAndIP because number of timestamps and IP addresses is not equal");
+		return;
+	}
+
+	optionDataLength = timestampVal.timestamps.size()*sizeof(uint32_t) + 2*sizeof(uint8_t);
+
+	if (timestampVal.type == IPv4TimestampOptionValue::TimestampAndIP)
+	{
+		optionDataLength += timestampVal.timestamps.size()*sizeof(uint32_t);
+	}
+
+	(*optionData) = new uint8_t[optionDataLength];
+
+	size_t curOffset = 0;
+	(*optionData)[curOffset++] = 1; //pointer default value is 1 - means there are no empty timestamps
+	(*optionData)[curOffset++] = (uint8_t)timestampVal.type; // timestamp type
+
+	int firstZero = -1;
+	for (int i = 0; i < (int)timestampVal.timestamps.size(); i++)
+	{
+		uint32_t timestamp = htonl(timestampVal.timestamps.at(i));
+
+		// for pointer calculation - find the first timestamp equals to 0
+		if (timestamp == 0 && firstZero == -1)
+			firstZero = i;
+
+		if (timestampVal.type == IPv4TimestampOptionValue::TimestampAndIP)
+		{
+			uint32_t ipAddrAsInt = timestampVal.ipAddresses.at(i).toInt();
+			memcpy((*optionData) + curOffset , &ipAddrAsInt, sizeof(uint32_t));
+			curOffset += sizeof(uint32_t);
+		}
+
+		memcpy((*optionData) + curOffset , &timestamp, sizeof(uint32_t));
+		curOffset += sizeof(uint32_t);
+	}
+
+	// calculate pointer field
+	if (firstZero > -1)
+	{
+		uint8_t pointerVal = (uint8_t)(4*sizeof(uint8_t) + firstZero*sizeof(uint32_t) + 1);
+		if (timestampVal.type == IPv4TimestampOptionValue::TimestampAndIP)
+			pointerVal += (uint8_t)(firstZero*sizeof(uint32_t));
+
+		(*optionData)[0] = pointerVal;
+	}
+}
+
+IPv4OptionData* IPv4Layer::addOption(IPv4OptionTypes optionType, uint8_t optionDataLength, const uint8_t* optionData)
+{
+	return addOptionAt(optionType, optionDataLength, optionData, getHeaderLen()-m_NumOfTrailingBytes);
+}
+
+IPv4OptionData* IPv4Layer::addOption(IPv4OptionTypes optionType, const std::vector<IPv4Address>& ipList)
+{
+	uint8_t* optionData = NULL;
+	int optionDataLength = 0;
+	buildIPListOptionData(ipList, &optionData, optionDataLength);
+
+	IPv4OptionData* res = addOption(optionType, optionDataLength, optionData);
+
+	delete [] optionData;
+
+	return res;
+}
+
+IPv4OptionData* IPv4Layer::addTimestampOption(const IPv4TimestampOptionValue& timestampValue)
+{
+	uint8_t* optionData = NULL;
+	int optionDataLength = 0;
+	buildTimestampOptionData(timestampValue, &optionData, optionDataLength);
+
+	if (optionData == NULL)
+		return NULL;
+
+	IPv4OptionData* res = addOption(IPV4OPT_Timestamp, optionDataLength, optionData);
+
+	delete [] optionData;
+
+	return res;
+}
+
+IPv4OptionData* IPv4Layer::addOptionAfter(IPv4OptionTypes optionType, uint8_t optionDataLength, const uint8_t* optionData, IPv4OptionTypes prevOption)
+{
+	int offset = 0;
+
+	IPv4OptionData* prevOpt = getOptionData(prevOption);
+
+	if (prevOpt == NULL)
+	{
+		offset = sizeof(iphdr);
+	}
+	else
+	{
+		offset = (uint8_t*)prevOpt + prevOpt->getTotalSize() - m_Data;
+	}
+
+	return addOptionAt(optionType, optionDataLength, optionData, offset);
+}
+
+IPv4OptionData* IPv4Layer::addOptionAfter(IPv4OptionTypes optionType, const std::vector<IPv4Address>& ipList, IPv4OptionTypes prevOption)
+{
+	uint8_t* optionData = NULL;
+	int optionDataLength = 0;
+	buildIPListOptionData(ipList, &optionData, optionDataLength);
+
+	IPv4OptionData* res = addOptionAfter(optionType, (uint8_t)optionDataLength, optionData, prevOption);
+
+	delete [] optionData;
+
+	return res;
+}
+
+IPv4OptionData* IPv4Layer::addTimestampOptionAfter(const IPv4TimestampOptionValue& timestampValue, IPv4OptionTypes prevOption)
+{
+	uint8_t* optionData = NULL;
+	int optionDataLength = 0;
+	buildTimestampOptionData(timestampValue, &optionData, optionDataLength);
+
+	if (optionData == NULL)
+		return NULL;
+
+	IPv4OptionData* res = addOptionAfter(IPV4OPT_Timestamp, (uint8_t)optionDataLength, optionData, prevOption);
+
+	delete [] optionData;
+
+	return res;
+}
+
+bool IPv4Layer::removeOption(IPv4OptionTypes option)
+{
+	IPv4OptionData* opt = getOptionData(option);
+	if (opt == NULL)
+	{
+		return false;
+	}
+
+	// calculate total option size
+	IPv4OptionData* curOpt = getFirstOptionData();
+	size_t totalOptSize = 0;
+	while (curOpt != NULL)
+	{
+		totalOptSize += curOpt->getTotalSize();
+		curOpt = getNextOptionData(curOpt);
+	}
+	totalOptSize -= opt->getTotalSize();
+
+
+	int offset = (uint8_t*)opt - m_Data;
+
+	size_t sizeToShorten = opt->getTotalSize();
+	if (!shortenLayer(offset, sizeToShorten))
+	{
+		LOG_ERROR("Failed to remove IPv4 option: cannot shorten layer");
+		return false;
+	}
+
+	// setting this m_TempHeaderExtension because adjustOptionsTrailer() may extend or shorten the layer and the extend or shorten methods need to know the accurate
+	// current size of the header. m_TempHeaderExtension will be added to the length extracted from getIPv4Header()->internetHeaderLength as the temp new size
+	m_TempHeaderExtension = 0-sizeToShorten;
+	adjustOptionsTrailer(totalOptSize);
+	// the adjustOptionsTrailer() adds or removed the trailing bytes and sets getIPv4Header()->internetHeaderLength to the correct size, so the m_TempHeaderExtension
+	// isn't needed anymore
+	m_TempHeaderExtension = 0;
+
+	incOptionCount(-1);
+
+	return true;
+}
+
+bool IPv4Layer::removeAllOptions()
+{
+	int offset = sizeof(iphdr);
+
+	if (!shortenLayer(offset, getHeaderLen()-offset))
+		return false;
+
+	getIPv4Header()->internetHeaderLength = (5 & 0xf);
+	m_NumOfTrailingBytes = 0;
+	setOptionCount(0);
+	return true;
+}
+
+} // namespace pcpp

http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/c0a788b3/thirdparty/pcap++/Packet++/src/IPv6Layer.cpp
----------------------------------------------------------------------
diff --git a/thirdparty/pcap++/Packet++/src/IPv6Layer.cpp b/thirdparty/pcap++/Packet++/src/IPv6Layer.cpp
new file mode 100644
index 0000000..b1a55ed
--- /dev/null
+++ b/thirdparty/pcap++/Packet++/src/IPv6Layer.cpp
@@ -0,0 +1,112 @@
+#define LOG_MODULE PacketLogModuleIPv6Layer
+
+#include <IPv6Layer.h>
+#include <IPv4Layer.h>
+#include <PayloadLayer.h>
+#include <UdpLayer.h>
+#include <TcpLayer.h>
+#include <GreLayer.h>
+#include <string.h>
+#include <IpUtils.h>
+
+namespace pcpp
+{
+
+void IPv6Layer::initLayer()
+{
+	m_DataLen = sizeof(ip6_hdr);
+	m_Data = new uint8_t[m_DataLen];
+	m_Protocol = IPv6;
+	memset(m_Data, 0, sizeof(ip6_hdr));
+}
+
+IPv6Layer::IPv6Layer()
+{
+	initLayer();
+}
+
+IPv6Layer::IPv6Layer(const IPv6Address& srcIP, const IPv6Address& dstIP)
+{
+	initLayer();
+	ip6_hdr* ipHdr = getIPv6Header();
+	srcIP.copyTo(ipHdr->ipSrc);
+	dstIP.copyTo(ipHdr->ipDst);
+}
+
+void IPv6Layer::parseNextLayer()
+{
+	if (m_DataLen <= sizeof(ip6_hdr))
+		return;
+
+	ip6_hdr* ipHdr = getIPv6Header();
+
+	ProtocolType greVer = UnknownProtocol;
+
+	uint8_t ipVersion = 0;
+
+	switch (ipHdr->nextHeader)
+	{
+	case PACKETPP_IPPROTO_UDP:
+		m_NextLayer = new UdpLayer(m_Data + sizeof(ip6_hdr), m_DataLen - sizeof(ip6_hdr), this, m_Packet);
+		break;
+	case PACKETPP_IPPROTO_TCP:
+		m_NextLayer = new TcpLayer(m_Data + sizeof(ip6_hdr), m_DataLen - sizeof(ip6_hdr), this, m_Packet);
+		break;
+	case PACKETPP_IPPROTO_IPIP:
+		ipVersion = *(m_Data + sizeof(ip6_hdr));
+		if (ipVersion >> 4 == 4)
+			m_NextLayer = new IPv4Layer(m_Data + sizeof(ip6_hdr), m_DataLen - sizeof(ip6_hdr), this, m_Packet);
+		else if (ipVersion >> 4 == 6)
+			m_NextLayer = new IPv6Layer(m_Data + sizeof(ip6_hdr), m_DataLen - sizeof(ip6_hdr), this, m_Packet);
+		else
+			m_NextLayer = new PayloadLayer(m_Data + sizeof(ip6_hdr), m_DataLen - sizeof(ip6_hdr), this, m_Packet);
+		break;
+	case PACKETPP_IPPROTO_GRE:
+		greVer = GreLayer::getGREVersion(m_Data + sizeof(ip6_hdr), m_DataLen - sizeof(ip6_hdr));
+		if (greVer == GREv0)
+			m_NextLayer = new GREv0Layer(m_Data + sizeof(ip6_hdr), m_DataLen - sizeof(ip6_hdr), this, m_Packet);
+		else if (greVer == GREv1)
+			m_NextLayer = new GREv1Layer(m_Data + sizeof(ip6_hdr), m_DataLen - sizeof(ip6_hdr), this, m_Packet);
+		else
+			m_NextLayer = new PayloadLayer(m_Data + sizeof(ip6_hdr), m_DataLen - sizeof(ip6_hdr), this, m_Packet);
+		break;
+	default:
+		m_NextLayer = new PayloadLayer(m_Data + sizeof(ip6_hdr), m_DataLen - sizeof(ip6_hdr), this, m_Packet);
+		return;
+	}
+}
+
+void IPv6Layer::computeCalculateFields()
+{
+	ip6_hdr* ipHdr = getIPv6Header();
+	ipHdr->payloadLength = htons(m_DataLen - sizeof(ip6_hdr));
+	ipHdr->ipVersion = (6 & 0x0f);
+
+	if (m_NextLayer != NULL)
+	{
+		switch (m_NextLayer->getProtocol())
+		{
+		case TCP:
+			ipHdr->nextHeader = PACKETPP_IPPROTO_TCP;
+			break;
+		case UDP:
+			ipHdr->nextHeader = PACKETPP_IPPROTO_UDP;
+			break;
+		case ICMP:
+			ipHdr->nextHeader = PACKETPP_IPPROTO_ICMP;
+			break;
+		case GRE:
+			ipHdr->nextHeader = PACKETPP_IPPROTO_GRE;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+std::string IPv6Layer::toString()
+{
+	return "IPv6 Layer, Src: " + getSrcIpAddress().toString() + ", Dst: " + getDstIpAddress().toString();
+}
+
+}// namespace pcpp