#include "endian.hpp" #include "slp.hpp" #include "slp_meta.hpp" #include #include #include #include #include #include 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 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 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | \ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ 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 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 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(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 getIntfAddrs() { std::list addrList; struct ifaddrs* ifaddr; // attempt to fill struct with ifaddrs if (getifaddrs(&ifaddr) == -1) { return addrList; } slp::deleted_unique_ptr 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 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 processRequest(const Message& msg) { int rc = slp::SUCCESS; buffer resp(0); std::cout << "SLP Processing Request=" << 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) { buffer buff; buff = slp::handler::internal::prepareHeader(req); static_assert(sizeof(err) == 1, "Errors should be 1 byte."); // Since this is network order, the err should go in the 2nd byte of the // error field. This is immediately after the langtag. buff[slp::header::MIN_LEN + req.header.langtag.length() + 1] = err; return buff; } } // namespace handler } // namespace slp