SLP Parser

Contains SLP Parser and SLP Message Handler for
the following messages:
 -findsrvtypes.
 -findsrvs.

Change-Id: I24234b63a8a1226b5c4bb3f6ac0c9aa799987ffc
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/slp_message_handler.cpp b/slp_message_handler.cpp
new file mode 100644
index 0000000..fd6670a
--- /dev/null
+++ b/slp_message_handler.cpp
@@ -0,0 +1,372 @@
+#include "slp.hpp"
+
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "endian.hpp"
+#include "slp_meta.hpp"
+
+namespace slp
+{
+namespace handler
+{
+
+namespace internal
+{
+
+buffer prepareHeader(const Message& req)
+{
+    uint8_t length =  slp::header::MIN_LEN +   /* 14 bytes for header     */
+                      req.header.langtag.length() + /* Actual length of lang tag */
+                      slp::response::SIZE_ERROR; /*  2 bytes for error code */
+
+    buffer buff(length, 0);
+
+    buff[slp::header::OFFSET_VERSION] = req.header.version;
+
+    //will increment the function id from 1 as reply
+    buff[slp::header::OFFSET_FUNCTION] = req.header.functionID + 1;
+
+    std::copy_n(&length, slp::header::SIZE_LENGTH,
+                buff.data() +
+                slp::header::OFFSET_LENGTH);
+
+    auto flags = endian::to_network(req.header.flags);
+
+    std::copy_n((uint8_t*)&flags, slp::header::SIZE_FLAGS,
+                buff.data() +
+                slp::header::OFFSET_FLAGS);
+
+    std::copy_n(req.header.extOffset.data(), slp::header::SIZE_EXT,
+                buff.data() +
+                slp::header::OFFSET_EXT);
+
+    auto xid = endian::to_network(req.header.xid);
+
+    std::copy_n((uint8_t*)&xid, slp::header::SIZE_XID,
+                buff.data() +
+                slp::header::OFFSET_XID);
+
+    uint16_t langtagLen = req.header.langtag.length();
+    langtagLen = endian::to_network(langtagLen);
+    std::copy_n((uint8_t*)&langtagLen, slp::header::SIZE_LANG,
+                buff.data() +
+                slp::header::OFFSET_LANG_LEN);
+
+    std::copy_n((uint8_t*)req.header.langtag.c_str(), req.header.langtag.length(),
+                buff.data() +
+                slp::header::OFFSET_LANG);
+    return buff;
+
+}
+
+std::tuple<int, buffer> processSrvTypeRequest(
+    const Message& req)
+{
+
+    /*
+       0                   1                   2                   3
+       0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |      Service Location header (function = SrvTypeRply = 10)    |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |           Error Code          |    length of <srvType-list>   |
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+      |                       <srvtype--list>                         \
+      +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    */
+
+    buffer buff;
+
+    //read the slp service info from conf and create the service type string
+    slp::handler::internal::ServiceList svcList =
+        slp::handler::internal::readSLPServiceInfo(slp::CONF_FILE);
+    if (svcList.size() <= 0)
+    {
+        buff.resize(0);
+        return std::make_tuple((int)slp::Error::INTERNAL_ERROR, buff);
+    }
+
+    std::string service;
+    bool firstIteration = true;
+    for_each(svcList.cbegin(), svcList.cend(),
+             [&service, &firstIteration](const auto& svc)
+    {
+        if (firstIteration == true)
+        {
+            service = svc.first;
+            firstIteration = false;
+        }
+        else
+        {
+            service += ",";
+            service += svc.first;
+        }
+    });
+
+    buff = prepareHeader(req);
+
+    /* Need to modify the length and the function type field of the header
+     * as it is dependent on the handler of the service */
+
+    std::cout << "service=" << service.c_str() << "\n";
+
+    uint8_t length =  buff.size() +   /* 14 bytes header + length of langtag */
+                      slp::response::SIZE_ERROR +    /* 2 byte err code */
+                      slp::response::SIZE_SERVICE +  /* 2 byte srvtype len */
+                      service.length();
+
+
+    buff.resize(length);
+
+    std::copy_n(&length, slp::header::SIZE_LENGTH,
+                buff.data() +
+                slp::header::OFFSET_LENGTH);
+
+    /* error code is already set to 0 moving to service type len */
+
+    uint16_t serviceTypeLen = service.length();
+    serviceTypeLen = endian::to_network(serviceTypeLen);
+
+    std::copy_n((uint8_t*)&serviceTypeLen, slp::response::SIZE_SERVICE,
+                buff.data() +
+                slp::response::OFFSET_SERVICE_LEN);
+
+    /* service type data */
+    std::copy_n((uint8_t*)service.c_str(), service.length(),
+                (buff.data() +
+                 slp::response::OFFSET_SERVICE));
+
+    return std::make_tuple(slp::SUCCESS, buff);
+}
+
+std::tuple<int, buffer> processSrvRequest(
+    const Message& req)
+{
+
+    /*
+          Service Reply
+          0                   1                   2                   3
+          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         |        Service Location header (function = SrvRply = 2)       |
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         |        Error Code             |        URL Entry count        |
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         |       <URL Entry 1>          ...       <URL Entry N>          \
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+         URL Entry
+          0                   1                   2                   3
+          0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         |   Reserved    |          Lifetime             |   URL Length  |
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         |URL len, contd.|            URL (variable length)              \
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+         |# of URL auths |            Auth. blocks (if any)              \
+         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    */
+
+    buffer buff;
+    //Get all the services which are registered
+    slp::handler::internal::ServiceList svcList =
+        slp::handler::internal::readSLPServiceInfo(slp::CONF_FILE);
+    if (svcList.size() <= 0)
+    {
+        buff.resize(0);
+        return std::make_tuple((int)slp::Error::INTERNAL_ERROR, buff);
+    }
+
+    //return error if serice type doesn't match
+    auto& svcName = req.body.srvrqst.srvType;
+    auto svcIt = svcList.find(svcName);
+    if (svcIt == svcList.end())
+    {
+        buff.resize(0);
+        return std::make_tuple((int)slp::Error::INTERNAL_ERROR, buff);
+    }
+    //Get all the interface address
+    auto ifaddrList = slp::handler::internal::getIntfAddrs();
+    if (ifaddrList.size() <= 0)
+    {
+        buff.resize(0);
+        return std::make_tuple((int)slp::Error::INTERNAL_ERROR, buff);
+    }
+
+    buff = prepareHeader(req);
+    //Calculate the length and resize the buffer
+    uint8_t length =  buff.size() + /* 14 bytes header + length of langtag */
+                      slp::response::SIZE_ERROR +    /* 2 bytes error code */
+                      slp::response::SIZE_URL_COUNT; /* 2 bytes srvtype len */
+
+    buff.resize(length);
+
+    //Populate the url count
+    uint16_t urlCount = ifaddrList.size();
+    urlCount = endian::to_network(urlCount);
+
+    std::copy_n((uint8_t*)&urlCount, slp::response::SIZE_URL_COUNT,
+                buff.data() +
+                slp::response::OFFSET_URL_ENTRY);
+
+    //Find the service
+    const slp::ConfigData& svc = svcIt->second;
+    //Populate the URL Entrys
+    auto pos = slp::response::OFFSET_URL_ENTRY + slp::response::SIZE_URL_COUNT;
+    for (const auto& addr : ifaddrList)
+    {
+        std::string url = svc.name + ':' + svc.type +
+                          "//" + addr + ',' + svc.port;
+
+
+        buff.resize(buff.size() +
+                    slp::response::SIZE_URL_ENTRY +
+                    url.length());
+
+        uint8_t reserved = 0;
+        uint16_t auth = 0;
+        uint16_t lifetime = endian::to_network<uint16_t>(slp::LIFETIME);
+        uint16_t urlLength = url.length();
+
+        std::copy_n((uint8_t*)&reserved, slp::response::SIZE_RESERVED,
+                    buff.data() + pos);
+
+        pos += slp::response::SIZE_RESERVED;
+
+
+        std::copy_n((uint8_t*)&lifetime, slp::response::SIZE_LIFETIME,
+                    buff.data() + pos);
+
+        pos += slp::response::SIZE_LIFETIME;
+
+        urlLength = endian::to_network(urlLength);
+        std::copy_n((uint8_t*)&urlLength, slp::response::SIZE_URLLENGTH,
+                    buff.data() + pos);
+        pos += slp::response::SIZE_URLLENGTH;
+
+        std::copy_n((uint8_t*)url.c_str(), url.length(),
+                    buff.data() + pos);
+        pos += url.length();
+
+        std::copy_n((uint8_t*)&auth, slp::response::SIZE_AUTH,
+                    buff.data() + pos);
+        pos += slp::response::SIZE_AUTH;
+    }
+    uint8_t packetLength = buff.size();
+    std::copy_n((uint8_t*)&packetLength, slp::header::SIZE_VERSION,
+                buff.data() +
+                slp::header::OFFSET_LENGTH);
+
+    return std::make_tuple((int)slp::SUCCESS, buff);
+}
+
+std::list<std::string> getIntfAddrs()
+{
+    std::list<std::string> addrList;
+
+    struct ifaddrs* ifaddr;
+    // attempt to fill struct with ifaddrs
+    if (getifaddrs(&ifaddr) == -1)
+    {
+        return addrList;
+    }
+
+    slp::deleted_unique_ptr<ifaddrs> ifaddrPtr(ifaddr, [](ifaddrs * addr)
+    {
+        freeifaddrs(addr);
+    });
+
+    ifaddr = nullptr;
+
+    for (ifaddrs* ifa = ifaddrPtr.get(); ifa != nullptr; ifa = ifa->ifa_next)
+    {
+        // walk interfaces
+        if (ifa->ifa_addr == nullptr)
+        {
+            continue;
+        }
+
+        // get only INET interfaces not ipv6
+        if (ifa->ifa_addr->sa_family == AF_INET)
+        {
+            // if loopback, or not running ignore
+            if ((ifa->ifa_flags & IFF_LOOPBACK) ||
+                !(ifa->ifa_flags & IFF_RUNNING))
+            {
+                continue;
+            }
+
+            char tmp[INET_ADDRSTRLEN] = { 0 };
+
+            inet_ntop(AF_INET,
+                      &(((struct sockaddr_in*)(ifa->ifa_addr))->sin_addr),
+                      tmp,
+                      sizeof(tmp));
+            addrList.emplace_back(tmp);
+        }
+    }
+
+    return addrList;
+}
+
+slp::handler::internal::ServiceList  readSLPServiceInfo(
+    const std::string& filename)
+{
+    using namespace std::string_literals;
+    /*Conf File format would be
+      ServiceName serviceType Port */
+
+    slp::handler::internal::ServiceList svcLst;
+
+    std::ifstream readFile(filename);
+    slp::ConfigData service;
+    //Read all the service from the file
+    while (readFile >> service)
+    {
+        std::string tmp = "service:"s + service.name;
+        service.name = tmp;
+        svcLst.emplace(service.name, service);
+    }
+
+    return svcLst;
+}
+}//namespace internal
+
+std::tuple<int, buffer> processRequest(
+    const Message& msg)
+{
+    int rc = slp::SUCCESS;
+    buffer resp(0);
+    switch (msg.header.functionID)
+    {
+        case (uint8_t)slp::FunctionType::SRVTYPERQST:
+            std::tie(rc, resp) = slp::handler::internal::processSrvTypeRequest(msg);
+            break;
+        case (uint8_t)slp::FunctionType::SRVRQST:
+            std::tie(rc, resp) = slp::handler::internal::processSrvRequest(msg);
+            break;
+        default:
+            rc = (uint8_t)slp::Error::MSG_NOT_SUPPORTED;
+    }
+    return std::make_tuple(rc, resp);
+}
+
+buffer processError(const Message& req,
+                    uint8_t err)
+{
+    buffer buff;
+    buff = slp::handler::internal::prepareHeader(req);
+
+    std::copy_n(&err, slp::response::SIZE_ERROR,
+                buff.data() +
+                slp::response::OFFSET_ERROR);
+    return buff;
+
+}
+}//namespace handler
+}//namespace slp
diff --git a/slp_parser.cpp b/slp_parser.cpp
new file mode 100644
index 0000000..28726b0
--- /dev/null
+++ b/slp_parser.cpp
@@ -0,0 +1,285 @@
+#include "slp.hpp"
+
+#include <string.h>
+
+#include <string>
+#include <algorithm>
+
+#include "endian.hpp"
+#include "slp_meta.hpp"
+
+namespace slp
+{
+
+namespace parser
+{
+
+namespace internal
+{
+
+std::tuple<int, Message> parseHeader(const buffer& buff)
+{
+    /*  0                   1                   2                   3
+        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |    Version    |  Function-ID  |            Length             |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       | Length, contd.|O|F|R|       reserved          |Next Ext Offset|
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |  Next Extension Offset, contd.|              XID              |
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |      Language Tag Length      |         Language Tag          \
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
+
+    Message req {0};
+    int rc = slp::SUCCESS;
+
+    std::copy_n(buff.data(),
+                slp::header::SIZE_VERSION,
+                &req.header.version);
+
+    std::copy_n(buff.data() + slp::header::OFFSET_FUNCTION,
+                slp::header::SIZE_VERSION,
+                &req.header.functionID);
+
+    std::copy_n(buff.data() + slp::header::OFFSET_LENGTH,
+                slp::header::SIZE_LENGTH,
+                req.header.length.data());
+
+    std::copy_n(buff.data() + slp::header::OFFSET_FLAGS,
+                slp::header::SIZE_FLAGS,
+                (uint8_t*)&req.header.flags);
+
+    req.header.flags = endian::from_network(req.header.flags);
+    std::copy_n(buff.data() + slp::header::OFFSET_EXT,
+                slp::header::SIZE_EXT,
+                req.header.extOffset.data());
+
+    std::copy_n(buff.data() + slp::header::OFFSET_XID,
+                slp::header::SIZE_XID,
+                (uint8_t*)&req.header.xid);
+
+    req.header.xid = endian::from_network(req.header.xid);
+
+    uint16_t langtagLen;
+
+    std::copy_n(buff.data() + slp::header::OFFSET_LANG_LEN,
+                slp::header::SIZE_LANG,
+                (uint8_t*)&langtagLen);
+
+
+    langtagLen = endian::from_network(langtagLen);
+
+
+    req.header.langtag.insert(0, (const char*)buff.data() +
+                              slp::header::OFFSET_LANG,
+                              langtagLen);
+
+    /* check for the validity of the function */
+    if (req.header.functionID < static_cast<uint8_t>
+        (slp::FunctionType::SRVRQST)
+        || req.header.functionID > static_cast<uint8_t>(slp::FunctionType::SAADV))
+    {
+        rc = static_cast<int>(slp::Error::PARSE_ERROR);
+    }
+
+    return std::make_tuple(rc, std::move(req));
+}
+
+
+int parseSrvTypeRqst(const buffer& buff, Message& req)
+{
+
+
+    /*  0                   1                   2                   3
+        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |        length of PRList       |        <PRList> String        \
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |   length of Naming Authority  |   <Naming Authority String>   \
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |     length of <scope-list>    |      <scope-list> String      \
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
+
+    /* Enforce SLPv2 service type request size limits. */
+    if (buff.size() < slp::request::MIN_SRVTYPE_LEN)
+    {
+        return (int)slp::Error::PARSE_ERROR;
+    }
+
+    /* Parse the PRList. */
+    uint16_t prListLen;
+    std::copy_n(buff.data() + slp::request::OFFSET_PR_LEN,
+                slp::request::SIZE_PRLIST,
+                (uint8_t*)&prListLen);
+
+    prListLen = endian::from_network(prListLen);
+
+    req.body.srvtyperqst.prList.insert(0, (const char*)buff.data() +
+                                       slp::request::OFFSET_PR,
+                                       prListLen);
+
+    uint8_t pos = slp::request::OFFSET_PR + prListLen;
+
+    /* Parse the Naming Authority. */
+    uint16_t namingAuthLen;
+    std::copy_n(buff.data() + pos,
+                slp::request::SIZE_NAMING,
+                (uint8_t*)&namingAuthLen);
+
+    pos += slp::request::SIZE_NAMING;
+
+    namingAuthLen = endian::from_network(namingAuthLen);
+
+    if (namingAuthLen == 0 || namingAuthLen == 0xffff)
+    {
+        req.body.srvtyperqst.namingAuth = "";
+    }
+    else
+    {
+        req.body.srvtyperqst.namingAuth.insert(0, (const char*)buff.data() +
+                                               pos,
+                                               namingAuthLen);
+    }
+
+    pos += namingAuthLen;
+
+    /* Parse the <scope-list>. */
+    uint16_t scopeListLen;
+    std::copy_n(buff.data() + pos,
+                slp::request::SIZE_SCOPE,
+                (uint8_t*)&scopeListLen);
+
+    pos += slp::request::SIZE_SCOPE;
+
+    scopeListLen = endian::from_network(scopeListLen);
+
+    req.body.srvtyperqst.scopeList.insert(0, (const char*)buff.data() + pos,
+                                          scopeListLen);
+
+    return slp::SUCCESS;
+}
+
+int parseSrvRqst(const buffer& buff, Message& req)
+{
+    /*  0                   1                   2                   3
+        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |      length of <PRList>       |        <PRList> String        \
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |   length of <service-type>    |    <service-type> String      \
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |    length of <scope-list>     |     <scope-list> String       \
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |  length of predicate string   |  Service Request <predicate>  \
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+       |  length of <SLP SPI> string   |       <SLP SPI> String        \
+       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */
+
+    /* Enforce v2 service request size limits. */
+    if (buff.size() < slp::request::MIN_SRV_LEN)
+    {
+        return (int)slp::Error::PARSE_ERROR;
+    }
+
+    /* 1) Parse the PRList. */
+    uint16_t prListLen;
+    std::copy_n(buff.data() + slp::request::OFFSET_PR_LEN,
+                slp::request::SIZE_PRLIST,
+                (uint8_t*)&prListLen);
+
+    auto pos = slp::request::OFFSET_PR_LEN + slp::request::SIZE_PRLIST;
+
+    prListLen = endian::from_network(prListLen);
+
+    req.body.srvrqst.prList.insert(0, (const char*)buff.data() + pos,
+                                   prListLen);
+
+    pos += prListLen;
+
+
+    /* 2) Parse the <service-type> string. */
+    uint16_t srvTypeLen;
+    std::copy_n(buff.data() + pos,
+                slp::request::SIZE_SERVICE_TYPE,
+                (uint8_t*)&srvTypeLen);
+
+    srvTypeLen = endian::from_network(srvTypeLen);
+
+    pos += slp::request::SIZE_SERVICE_TYPE;
+
+    req.body.srvrqst.srvType.insert(0, (const char*)buff.data() + pos,
+                                    srvTypeLen);
+
+    pos += srvTypeLen;
+
+    /* 3) Parse the <scope-list> string. */
+    uint16_t scopeListLen;
+    std::copy_n(buff.data() + pos,
+                slp::request::SIZE_SCOPE,
+                (uint8_t*)&scopeListLen);
+
+    scopeListLen = endian::from_network(scopeListLen);
+
+    pos += slp::request::SIZE_SCOPE;
+
+    req.body.srvrqst.scopeList.insert(0, (const char*)buff.data() + pos,
+                                      scopeListLen);
+
+    pos += scopeListLen;
+
+    /* 4) Parse the <predicate> string. */
+    uint16_t predicateLen;
+    std::copy_n(buff.data() + pos,
+                slp::request::SIZE_PREDICATE,
+                (uint8_t*)&predicateLen);
+
+    predicateLen = endian::from_network(predicateLen);
+    pos += slp::request::SIZE_PREDICATE;
+
+    req.body.srvrqst.predicate.insert(0, (const char*)buff.data() + pos,
+                                      predicateLen);
+
+    pos += predicateLen;
+
+    /* 5) Parse the <SLP SPI> string. */
+    uint16_t spistrLen;
+    std::copy_n(buff.data() + pos,
+                slp::request::SIZE_SLPI,
+                (uint8_t*)&spistrLen);
+
+    spistrLen = endian::from_network(spistrLen);
+    pos += slp::request::SIZE_SLPI;
+
+    req.body.srvrqst.spistr.insert(0, (const char*)buff.data() + pos,
+                                   spistrLen);
+
+    return slp::SUCCESS;
+}
+}//namespace internal
+
+std::tuple<int, Message> parseBuffer(const buffer& buff)
+{
+    Message req;
+    int rc = slp::SUCCESS;
+    /* parse the header first */
+    std::tie(rc, req) = internal::parseHeader(buff);
+    if (!rc)
+    {
+        /* switch on the function id to parse the body */
+        switch (req.header.functionID)
+        {
+            case (uint8_t)slp::FunctionType::SRVTYPERQST:
+                rc = internal::parseSrvTypeRqst(buff, req);
+                break;
+            case (uint8_t)slp::FunctionType::SRVRQST:
+                rc = internal::parseSrvRqst(buff, req);
+                break;
+            default:
+                rc = (int)slp::Error::MSG_NOT_SUPPORTED;
+        }
+    }
+    return std::make_tuple(rc, std::move(req));
+}
+}//namespace parser
+}//namespace slp