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