|  | #include "endian.hpp" | 
|  | #include "slp.hpp" | 
|  | #include "slp_meta.hpp" | 
|  |  | 
|  | #include <arpa/inet.h> | 
|  | #include <dirent.h> | 
|  | #include <ifaddrs.h> | 
|  | #include <net/if.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <bitset> | 
|  |  | 
|  | namespace slp | 
|  | { | 
|  | namespace handler | 
|  | { | 
|  |  | 
|  | namespace internal | 
|  | { | 
|  |  | 
|  | static constexpr auto SERVICE_DIR = "/etc/slp/services/"; | 
|  |  | 
|  | 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(); | 
|  | if (svcList.size() <= 0) | 
|  | { | 
|  | buff.resize(0); | 
|  | std::cerr << "SLP unable to read the service info\n"; | 
|  | 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"; | 
|  |  | 
|  | // See if total response size exceeds our max | 
|  | uint32_t totalLength = | 
|  | 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(); | 
|  | if (totalLength > slp::MAX_LEN) | 
|  | { | 
|  | std::cerr << "Message response size exceeds maximum allowed: " | 
|  | << totalLength << " / " << slp::MAX_LEN << std::endl; | 
|  | buff.resize(0); | 
|  | return std::make_tuple((int)slp::Error::PARSE_ERROR, buff); | 
|  | } | 
|  |  | 
|  | 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(); | 
|  | if (svcList.size() <= 0) | 
|  | { | 
|  | buff.resize(0); | 
|  | std::cerr << "SLP unable to read the service info\n"; | 
|  | 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); | 
|  | std::cerr << "SLP unable to find the service=" << svcName << "\n"; | 
|  | 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); | 
|  | std::cerr << "SLP unable to read the interface address\n"; | 
|  | 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 Entries | 
|  | 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; | 
|  |  | 
|  | // See if total response size exceeds our max | 
|  | uint32_t totalLength = buff.size() + slp::response::SIZE_URL_ENTRY + | 
|  | url.length(); | 
|  | if (totalLength > slp::MAX_LEN) | 
|  | { | 
|  | std::cerr << "Message response size exceeds maximum allowed: " | 
|  | << totalLength << " / " << slp::MAX_LEN << std::endl; | 
|  | buff.resize(0); | 
|  | return std::make_tuple((int)slp::Error::PARSE_ERROR, buff); | 
|  | } | 
|  |  | 
|  | 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() | 
|  | { | 
|  | using namespace std::string_literals; | 
|  | slp::handler::internal::ServiceList svcLst; | 
|  | slp::ConfigData service; | 
|  | struct dirent* dent = nullptr; | 
|  |  | 
|  | // Open the services dir and get the service info | 
|  | // from service files. | 
|  | // Service File format would be "ServiceName serviceType Port" | 
|  | DIR* dir = opendir(SERVICE_DIR); | 
|  | // wrap the pointer into smart pointer. | 
|  | slp::deleted_unique_ptr<DIR> dirPtr(dir, [](DIR* dir) { | 
|  | if (!dir) | 
|  | { | 
|  | closedir(dir); | 
|  | } | 
|  | }); | 
|  | dir = nullptr; | 
|  |  | 
|  | if (dirPtr.get()) | 
|  | { | 
|  | while ((dent = readdir(dirPtr.get())) != NULL) | 
|  | { | 
|  | if (dent->d_type == DT_REG) // regular file | 
|  | { | 
|  | auto absFileName = std::string(SERVICE_DIR) + dent->d_name; | 
|  | std::ifstream readFile(absFileName); | 
|  | readFile >> service; | 
|  | service.name = "service:"s + service.name; | 
|  | svcLst.emplace(service.name, service); | 
|  | } | 
|  | } | 
|  | } | 
|  | return svcLst; | 
|  | } | 
|  | } // namespace internal | 
|  |  | 
|  | std::tuple<int, buffer> processRequest(const Message& msg) | 
|  | { | 
|  | int rc = slp::SUCCESS; | 
|  | buffer resp(0); | 
|  | std::cout << "SLP Processing Request=" | 
|  | << std::bitset<8>(msg.header.functionID) << "\n"; | 
|  |  | 
|  | 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) | 
|  | { | 
|  | if (req.header.functionID != 0) | 
|  | { | 
|  | std::cout << "Processing Error for function: " | 
|  | << std::bitset<8>(req.header.functionID) << std::endl; | 
|  | } | 
|  |  | 
|  | /*  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>          \ | 
|  | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*/ | 
|  |  | 
|  | // There has been some sort of issue processing the request from | 
|  | // the client. Can not assume the input request buffer is valid | 
|  | // so just create an empty buffer with the non-variable size | 
|  | // fields set and the error code | 
|  | uint8_t length = slp::header::MIN_LEN +     /* 14 bytes for header     */ | 
|  | slp::response::SIZE_ERROR; /*  2 bytes for error code */ | 
|  |  | 
|  | buffer buff(length, 0); | 
|  |  | 
|  | static_assert(sizeof(err) == 1, "Errors should be 1 byte."); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | // This is an invalid header from user so just fill in 0 for langtag | 
|  | uint16_t langtagLen = 0; | 
|  | std::copy_n((uint8_t*)&langtagLen, slp::header::SIZE_LANG, | 
|  | buff.data() + slp::header::OFFSET_LANG_LEN); | 
|  |  | 
|  | // Since this is network order, the err should go in the 2nd byte of the | 
|  | // error field. | 
|  | buff[slp::header::MIN_LEN + 1] = err; | 
|  |  | 
|  | return buff; | 
|  | } | 
|  | } // namespace handler | 
|  | } // namespace slp |