SLP Server
This contains the entry point for the SLP
and starts the SLP Server.
Change-Id: I5976c8168a1af2703143c9bead61583197949115
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
diff --git a/Makefile.am b/Makefile.am
new file mode 100755
index 0000000..1b1e804
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,12 @@
+sbin_PROGRAMS = \
+ slpd
+
+slpd_SOURCES = \
+ main.cpp \
+ sock_channel.cpp \
+ slp_server.cpp \
+ slp_parser.cpp \
+ slp.cpp \
+ slp_message_handler.cpp
+
+slpd_LDFLAGS = $(SYSTEMD_LIBS)
diff --git a/README.md b/README.md
new file mode 100755
index 0000000..083274a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+## To Build
+```
+To build this package, do the following steps:
+
+ 1. ./bootstrap.sh
+ 2. ./configure ${CONFIGURE_FLAGS}
+ 3. make
+
+To full clean the repository again run `./bootstrap.sh clean`.
+
+SLPD:-This is a unicast SLP UDP server which serves the following
+two messages
+1) finsrvs
+2) findsrvtypes
+
+NOTE:- Multicast support is not there and this server neither
+listen to any advertisement messages nor it advertises it's
+services with DA.
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..50b75b7
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+AUTOCONF_FILES="Makefile.in aclocal.m4 ar-lib autom4te.cache compile \
+ config.guess config.h.in config.sub configure depcomp install-sh \
+ ltmain.sh missing *libtool test-driver"
+
+case $1 in
+ clean)
+ test -f Makefile && make maintainer-clean
+ for file in ${AUTOCONF_FILES}; do
+ find -name "$file" | xargs -r rm -rf
+ done
+ exit 0
+ ;;
+esac
+
+autoreconf -i
+echo 'Run "./configure ${CONFIGURE_FLAGS} && make"'
diff --git a/configure.ac b/configure.ac
new file mode 100755
index 0000000..d20b4be
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,22 @@
+# Initialization
+AC_PREREQ([2.69])
+AC_INIT([slpd-lite], [1.0], [https://github.com/openbmc/slpd-lite/issues])
+AC_CONFIG_HEADERS([config.h])
+AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz])
+
+# Checks for programs.
+AC_PROG_CXX
+AX_CXX_COMPILE_STDCXX_14([noext])
+AM_PROG_AR
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+
+# Checks for libraries.
+PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221])
+
+# Checks for header files.
+AC_CHECK_HEADER(systemd/sd-bus.h, ,[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd developement package required])])
+# Checks for typedefs, structures, and compiler characteristics.
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..13da385
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,82 @@
+#include <algorithm>
+#include <iomanip>
+
+#include "slp.hpp"
+#include "slp_meta.hpp"
+#include "slp_server.hpp"
+#include "sock_channel.hpp"
+
+
+/* Call Back for the sd event loop */
+static int requestHandler(sd_event_source* es, int fd, uint32_t revents,
+ void* userdata)
+{
+ int rc = slp::SUCCESS;
+ timeval tv{slp::TIMEOUT, 0};
+ udpsocket::Channel channel(fd, tv);
+ std::vector<uint8_t> recvBuff;
+ slp::Message req;
+ std::vector<uint8_t> resp;
+ // Read the packet
+ std::tie(rc, recvBuff) = channel.read();
+
+ if (rc < 0)
+ {
+ std::cerr << "E> SLP Error in Read : " << std::hex << rc << "\n";
+ return rc;
+ }
+
+ switch (recvBuff[0])
+ {
+ case slp::VERSION_2:
+ {
+ std::cout << "SLP Request" << "\n";
+ //print the buffer
+ std::for_each(recvBuff.begin(), recvBuff.end(),
+ [](uint8_t & ch)
+ {
+ std::cout << std::hex << std::setfill('0')
+ << std::setw(2) << (int)ch << ' ' ;
+ });
+ std::cout << "\n";
+ //Parse the buffer and construct the req object
+ std::tie(rc, req) = slp::parser::parseBuffer(recvBuff);
+ if (!rc)
+ {
+ //Passing the req object to handler to serve it
+ std::tie(rc, resp) = slp::handler::processRequest(req);
+ }
+ break;
+ }
+ default:
+ rc = static_cast<uint8_t>(slp::Error::VER_NOT_SUPPORTED);
+ break;
+ }
+
+ //if there was error during Parsing of request
+ //or processing of request then handle the error.
+ if (rc)
+ {
+ std::cerr << "E> SLP Error rc=" << rc << "\n";
+ resp = slp::handler::processError(req, rc);
+ }
+
+ //print and send the response
+ std::cout << "SLP Response" << "\n";
+ std::for_each(resp.begin(), resp.end(),
+ [](uint8_t & ch)
+ {
+ std::cout << std::hex << std::setfill('0')
+ << std::setw(2) << (int)ch << ' ' ;
+ });
+
+ channel.write(resp);
+ return slp::SUCCESS;
+}
+
+
+int main(int argc, char* argv[])
+{
+ slp::udp::Server svr(slp::PORT, requestHandler);
+ return svr.run();
+}
diff --git a/slp.cpp b/slp.cpp
new file mode 100644
index 0000000..6338b05
--- /dev/null
+++ b/slp.cpp
@@ -0,0 +1,31 @@
+#include "slp.hpp"
+
+slp::Message::Message(const slp::Message& rhs)
+{
+ *this = rhs;
+}
+
+slp::Message& slp::Message::operator=(const slp::Message& rhs)
+{
+
+ if (this != &rhs)
+ {
+ header = rhs.header;
+ body = rhs.body;
+ }
+}
+
+
+slp::Payload::Payload(const slp::Payload& rhs)
+{
+ *this = rhs;
+}
+
+slp::Payload& slp::Payload::operator=(const slp::Payload& rhs)
+{
+ if (this != &rhs)
+ {
+ this->srvtyperqst = rhs.srvtyperqst ;
+ this->srvrqst = rhs.srvrqst;
+ }
+}
diff --git a/slp.hpp b/slp.hpp
new file mode 100644
index 0000000..5f9703c
--- /dev/null
+++ b/slp.hpp
@@ -0,0 +1,282 @@
+#pragma once
+
+#include <stdio.h>
+
+#include <array>
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "slp_service_info.hpp"
+
+namespace slp
+{
+
+using buffer = std::vector<uint8_t>;
+
+template<typename T>
+using deleted_unique_ptr = std::unique_ptr<T, std::function<void(T*)>>;
+
+namespace request
+{
+
+/*
+ * @struct ServiceType
+ *
+ * SLP Message structure for ServiceType Request.
+ */
+struct ServiceType
+{
+ std::string prList;
+ std::string namingAuth;
+ std::string scopeList;
+};
+
+/*
+ * @struct Service
+ *
+ * SLP Message structure for Service Request.
+ */
+struct Service
+{
+ std::string prList;
+ std::string srvType;
+ std::string scopeList;
+ std::string predicate;
+ std::string spistr;
+};
+}//namespace request
+
+/*
+ * @enum FunctionType
+ *
+ * SLP Protocol supported Message types.
+ */
+enum class FunctionType : uint8_t
+{
+ SRVRQST = 0x01,
+ SRVRPLY = 0x02,
+ ATTRRQST = 0x06,
+ ATTRRPLY = 0x07,
+ SRVTYPERQST = 0x09,
+ SRVTYPERPLY = 0x0A,
+ SAADV = 0x0B,
+};
+
+/*
+ * @enum Error
+ *
+ * SLP Protocol defined Error Codes.
+ */
+enum class Error : uint8_t
+{
+ LANGUAGE_NOT_SUPPORTED = 0x01,
+ PARSE_ERROR = 0x02,
+ INVALID_REGISTRATION = 0x03,
+ SCOPE_NOT_SUPPORTED = 0x04,
+ AUTHENTICATION_UNKNOWN = 0x05,
+ AUTHENTICATION_ABSENT = 0x06,
+ AUTHENTICATION_FAILED = 0x07,
+ VER_NOT_SUPPORTED = 0x09,
+ INTERNAL_ERROR = 0x0A,
+ DA_BUSY_NOW = 0x0B,
+ OPTION_NOT_UNDERSTOOD = 0x0C,
+ INVALID_UPDATE = 0x0D,
+ MSG_NOT_SUPPORTED = 0x0E,
+};
+
+/*
+ * @struct Header
+ *
+ * SLP Protocol Header
+ */
+struct Header
+{
+ uint8_t version = 0;
+ uint8_t functionID = 0;
+ std::array<uint8_t, 3> length;
+ uint16_t flags = 0;
+ std::array<uint8_t, 3> extOffset;
+ uint16_t xid = 0;
+ uint16_t langtagLen = 0;
+ std::string langtag;
+
+};
+
+/*
+ * @struct Payload
+ * This is a payload of the SLP Message currently
+ * we are supporting two request.
+ *
+ */
+struct Payload
+{
+ request::ServiceType srvtyperqst;
+ request::Service srvrqst;
+};
+
+
+/*
+ * @struct Messsage
+ *
+ * This will denote the slp Message.
+ */
+struct Message
+{
+ Header header;
+ Payload body;
+};
+
+
+namespace parser
+{
+
+/** Parse a buffer and fill the header and the body of the message.
+ *
+ * @param[in] buffer - The buffer from which data should be parsed.
+ *
+ * @return Zero on success and parsed msg object,
+ * non-zero on failure and empty msg object.
+ *
+ */
+
+std::tuple<int, Message> parseBuffer(const buffer& buf);
+
+namespace internal
+{
+
+/** Parse header data from the buffer.
+ *
+ * @param[in] buffer - The buffer from which data should be parsed.
+ *
+ * @return Zero on success and fills header object inside message,
+ * non-zero on failure and empty msg object.
+ *
+ * @internal
+ */
+
+std::tuple<int, Message> parseHeader(const buffer& buf);
+
+/** Parse a srvType request
+ *
+ * @param[in] buffer - The buffer from which data should be parsed.
+ *
+ * @return Zero on success,and fills the body object inside message.
+ * non-zero on failure and empty msg object.
+ *
+ * @internal
+ */
+
+int parseSrvTypeRqst(const buffer& buf, Message& req);
+
+/** Parse a service request.
+ *
+ * @param[in] buffer - The buffer from which data should be parsed.
+ *
+ * @return Zero on success,and fills the body object inside message.
+ * non-zero on failure and empty msg object.
+ *
+ * @internal
+ */
+
+int parseSrvRqst(const buffer& buf, Message& req);
+
+}//namespace internal
+}//namespce parser
+
+
+namespace handler
+{
+
+/** Handle the request message.
+ *
+ * @param[in] msg - The message to process.
+ *
+ * @return In case of success, the vector is populated with the data
+ * available on the socket and return code is 0.
+ * In case of error, nonzero code and vector is set to size 0.
+ *
+ */
+
+std::tuple<int, buffer> processRequest(const Message& msg);
+
+/** Handle the error
+ *
+ * @param[in] msg - Req message.
+ * @param[in] err - Error code.
+ *
+ * @return the vector populated with the error data
+ */
+
+buffer processError(const Message& req,
+ const uint8_t err);
+namespace internal
+{
+
+using ServiceList = std::map<std::string, slp::ConfigData>;
+/** Handle the SrvRequest message.
+ *
+ * @param[in] msg - The message to process
+ *
+ * @return In case of success, the vector is populated with the data
+ * available on the socket and return code is 0.
+ * In case of error, nonzero code and vector is set to size 0.
+ *
+ * @internal
+ */
+
+std::tuple<int, buffer> processSrvRequest(const Message& msg);
+
+
+/** Handle the SrvTypeRequest message.
+ *
+ * @param[in] msg - The message to process
+ *
+ * @return In case of success, the vector is populated with the data
+ * available on the socket and return code is 0.
+ * In case of error, nonzero code and vector is set to size 0.
+ *
+ * @internal
+ *
+ */
+
+std::tuple<int, buffer> processSrvTypeRequest(const Message& msg);
+
+/** Read the SLPinfo from the configuration.
+ *
+ * @param[in] filename - Name of the conf file
+ *
+ * @return the list of the services
+ *
+ * @internal
+ *
+ */
+ServiceList readSLPServiceInfo(const std::string& filename);
+
+/** Get all the interface address
+ *
+ * @return the list of the interface address.
+ *
+ * @internal
+ *
+ */
+
+std::list<std::string> getIntfAddrs();
+
+/** Fill the buffer with the header data from the request object
+ *
+ * @param[in] req - Header data will be copied from
+ *
+ * @return the vector is populated with the data
+ *
+ * @internal
+ */
+buffer prepareHeader(const Message& req);
+
+
+}//namespace internal
+}//namespce handler
+}//namespce slp
diff --git a/slp_meta.hpp b/slp_meta.hpp
new file mode 100644
index 0000000..791d3d6
--- /dev/null
+++ b/slp_meta.hpp
@@ -0,0 +1,86 @@
+#pragma once
+
+namespace slp
+{
+/** @brief SLP configuartion file which is having the service information */
+constexpr auto CONF_FILE = "/etc/slp_lite.conf";
+/** @brief SLP Version */
+constexpr size_t VERSION_2 = 2;
+constexpr auto SUCCESS = 0;
+/** @brief SLP Port */
+constexpr auto PORT = 427;
+
+constexpr auto TIMEOUT = 30;
+/** @brief SLP service lifetime */
+constexpr auto LIFETIME = 5;
+
+/** @brief Defines the constants for slp header.
+ * Size and the offsets.
+ */
+namespace header
+{
+
+constexpr size_t SIZE_VERSION = 1;
+constexpr size_t SIZE_LENGTH = 1;
+constexpr size_t SIZE_FLAGS = 2;
+constexpr size_t SIZE_EXT = 3;
+constexpr size_t SIZE_XID = 2;
+constexpr size_t SIZE_LANG = 2;
+
+constexpr size_t OFFSET_VERSION = 0;
+constexpr size_t OFFSET_FUNCTION = 1;
+constexpr size_t OFFSET_LENGTH = 4;
+constexpr size_t OFFSET_FLAGS = 5;
+constexpr size_t OFFSET_EXT = 7;
+constexpr size_t OFFSET_XID = 10;
+constexpr size_t OFFSET_LANG_LEN = 12;
+constexpr size_t OFFSET_LANG = 14;
+
+constexpr size_t MIN_LEN = 14;
+}//namespace header
+
+/** @brief Defines the constants for slp response.
+ * Size and the offsets.
+ */
+
+namespace response
+{
+
+constexpr size_t SIZE_ERROR = 2;
+constexpr size_t SIZE_SERVICE = 2;
+constexpr size_t SIZE_URL_COUNT = 2;
+constexpr size_t SIZE_URL_ENTRY = 6;
+constexpr size_t SIZE_RESERVED = 1;
+constexpr size_t SIZE_LIFETIME = 2;
+constexpr size_t SIZE_URLLENGTH = 2;
+constexpr size_t SIZE_AUTH = 1;
+
+constexpr size_t OFFSET_ERROR = 16;
+constexpr size_t OFFSET_SERVICE_LEN = 18;
+constexpr size_t OFFSET_SERVICE = 20;
+constexpr size_t OFFSET_URL_ENTRY = 18;
+
+}//namespace response
+
+/** @brief Defines the constants for slp request.
+ * Size and the offsets.
+ */
+namespace request
+{
+
+constexpr size_t MIN_SRVTYPE_LEN = 22;
+constexpr size_t MIN_SRV_LEN = 26;
+
+constexpr size_t SIZE_PRLIST = 2;
+constexpr size_t SIZE_NAMING = 2;
+constexpr size_t SIZE_SCOPE = 2;
+constexpr size_t SIZE_SERVICE_TYPE = 2;
+constexpr size_t SIZE_PREDICATE = 2;
+constexpr size_t SIZE_SLPI = 2;
+
+constexpr size_t OFFSET_PR_LEN = 16;
+constexpr size_t OFFSET_PR = 18;
+constexpr size_t OFFSET_SERVICE = 20;
+
+}//namespace request
+}//namespace slp
diff --git a/slp_server.cpp b/slp_server.cpp
new file mode 100644
index 0000000..e307dab
--- /dev/null
+++ b/slp_server.cpp
@@ -0,0 +1,110 @@
+#include "slp_server.hpp"
+
+#include <memory>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sock_channel.hpp"
+
+/** General udp server which waits for the POLLIN event
+ on the port and calls the call back once it gets the event.
+ usage would be create the server with the port and the call back
+ and call the run method.
+ */
+int slp::udp::Server::run()
+{
+ struct sockaddr_in in {};
+
+ sd_event* event = nullptr;
+
+ slp::deleted_unique_ptr<sd_event> eventPtr(event, [](sd_event * event)
+ {
+ if (!event)
+ {
+ event = sd_event_unref(event);
+ }
+ });
+
+ int fd = -1, r;
+ sigset_t ss;
+
+ r = sd_event_default(&event);
+ if (r < 0)
+ {
+ goto finish;
+ }
+
+ eventPtr.reset(event);
+ event = nullptr;
+
+ if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
+ sigaddset(&ss, SIGINT) < 0)
+ {
+ r = -errno;
+ goto finish;
+ }
+ /* Block SIGTERM first, so that the event loop can handle it */
+ if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
+ {
+ r = -errno;
+ goto finish;
+ }
+
+ /* Let's make use of the default handler and "floating"
+ reference features of sd_event_add_signal() */
+
+ r = sd_event_add_signal(eventPtr.get(), NULL, SIGTERM, NULL, NULL);
+ if (r < 0)
+ {
+ goto finish;
+ }
+
+ r = sd_event_add_signal(eventPtr.get(), NULL, SIGINT, NULL, NULL);
+ if (r < 0)
+ {
+ goto finish;
+ }
+
+ fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+ if (fd < 0)
+ {
+ r = -errno;
+ goto finish;
+ }
+
+ in.sin_family = AF_INET;
+ in.sin_port = htons(this->port);
+ in.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind(fd, (struct sockaddr*)&in, sizeof(in)) < 0)
+ {
+ r = -errno;
+ goto finish;
+ }
+
+ r = sd_event_add_io(eventPtr.get(), nullptr, fd, EPOLLIN, this->callme,
+ nullptr);
+ if (r < 0)
+ {
+ goto finish;
+ }
+
+ r = sd_event_loop(eventPtr.get());
+
+finish:
+
+ if (fd >= 0)
+ {
+ (void) close(fd);
+ }
+
+ if (r < 0)
+ {
+ fprintf(stderr, "Failure: %s\n", strerror(-r));
+ }
+
+ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/slp_server.hpp b/slp_server.hpp
new file mode 100644
index 0000000..0ab12c9
--- /dev/null
+++ b/slp_server.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <iostream>
+#include <string>
+#include <sys/types.h>
+#include <systemd/sd-bus.h>
+#include <systemd/sd-daemon.h>
+#include <systemd/sd-event.h>
+
+#include "slp_meta.hpp"
+#include "slp.hpp"
+
+namespace slp
+{
+
+namespace udp
+{
+/** General udp server which waits for the POLLIN event
+ on the port and calls the call back once it gets the event.
+ usage would be create the server with the port and the call back
+ and call the run method.
+ */
+class Server
+{
+
+ public:
+
+ Server(): Server(slp::PORT, nullptr) {};
+
+ Server(uint16_t port, sd_event_io_handler_t cb):
+ port(port),
+ callme(cb) {};
+
+ Server(const Server&) = delete;
+ Server& operator=(const Server&) = delete;
+ Server(Server&&) = default;
+ Server& operator=(Server &&) = default;
+
+ uint16_t port;
+ sd_event_io_handler_t callme;
+
+ int run();
+
+};
+}//namespce udp
+}//namespace slp
diff --git a/slp_service_info.hpp b/slp_service_info.hpp
new file mode 100644
index 0000000..29fe1c4
--- /dev/null
+++ b/slp_service_info.hpp
@@ -0,0 +1,52 @@
+#pragma once
+#include <iostream>
+#include <fstream>
+#include <sstream>
+namespace slp
+{
+struct ConfigData
+{
+ std::string name;
+ std::string type;
+ std::string port;
+
+ friend std::istream& operator>>(std::istream& str, ConfigData& data)
+ {
+ std::string line;
+ constexpr auto DELIMITER = " ";
+ size_t delimtrPos = 0;
+ size_t delimtrPrevPos = 0;
+ std::array<std::string, 3>tokens;
+ std::getline(str, line);
+ size_t count = 0;
+
+ delimtrPos = line.find(DELIMITER, delimtrPrevPos);
+ while (delimtrPos != std::string::npos)
+ {
+ tokens[count] = line.substr(delimtrPrevPos, (delimtrPos - delimtrPrevPos));
+ delimtrPrevPos = delimtrPos + 1;
+
+ delimtrPos = line.find(DELIMITER, delimtrPrevPos);
+ if (delimtrPos == std::string::npos && delimtrPrevPos < line.length())
+ {
+ delimtrPos = line.length();
+ }
+
+ count++;
+ }
+
+ if (count > 2)
+ {
+ data.name = tokens[0];
+ data.type = tokens[1];
+ data.port = tokens[2];
+ }
+ else
+ {
+ str.setstate(std::ios::failbit);
+ }
+ return str;
+ }
+
+};
+}