| #include "slp.hpp" |
| |
| #include <arpa/inet.h> |
| #include <dirent.h> |
| #include <ifaddrs.h> |
| #include <net/if.h> |
| #include <string.h> |
| |
| #include <algorithm> |
| |
| #include "config.h" |
| #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(); |
| 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"; |
| |
| 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 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() |
| { |
| 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); |
| switch (msg.header.functionID) |
| { |
| std::cout << "SLP Processing Request=" |
| << msg.header.functionID <<"\n"; |
| |
| 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 |