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