1 #include "config.h" 2 3 #include "endian.hpp" 4 #include "slp.hpp" 5 #include "slp_meta.hpp" 6 7 #include <arpa/inet.h> 8 #include <dirent.h> 9 #include <ifaddrs.h> 10 #include <net/if.h> 11 #include <string.h> 12 13 #include <algorithm> 14 15 namespace slp 16 { 17 namespace handler 18 { 19 20 namespace internal 21 { 22 23 buffer prepareHeader(const Message& req) 24 { 25 uint8_t length = 26 slp::header::MIN_LEN + /* 14 bytes for header */ 27 req.header.langtag.length() + /* Actual length of lang tag */ 28 slp::response::SIZE_ERROR; /* 2 bytes for error code */ 29 30 buffer buff(length, 0); 31 32 buff[slp::header::OFFSET_VERSION] = req.header.version; 33 34 // will increment the function id from 1 as reply 35 buff[slp::header::OFFSET_FUNCTION] = req.header.functionID + 1; 36 37 std::copy_n(&length, slp::header::SIZE_LENGTH, 38 buff.data() + slp::header::OFFSET_LENGTH); 39 40 auto flags = endian::to_network(req.header.flags); 41 42 std::copy_n((uint8_t*)&flags, slp::header::SIZE_FLAGS, 43 buff.data() + slp::header::OFFSET_FLAGS); 44 45 std::copy_n(req.header.extOffset.data(), slp::header::SIZE_EXT, 46 buff.data() + slp::header::OFFSET_EXT); 47 48 auto xid = endian::to_network(req.header.xid); 49 50 std::copy_n((uint8_t*)&xid, slp::header::SIZE_XID, 51 buff.data() + slp::header::OFFSET_XID); 52 53 uint16_t langtagLen = req.header.langtag.length(); 54 langtagLen = endian::to_network(langtagLen); 55 std::copy_n((uint8_t*)&langtagLen, slp::header::SIZE_LANG, 56 buff.data() + slp::header::OFFSET_LANG_LEN); 57 58 std::copy_n((uint8_t*)req.header.langtag.c_str(), 59 req.header.langtag.length(), 60 buff.data() + slp::header::OFFSET_LANG); 61 return buff; 62 } 63 64 std::tuple<int, buffer> processSrvTypeRequest(const Message& req) 65 { 66 /* 67 0 1 2 3 68 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 69 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 70 | Service Location header (function = SrvTypeRply = 10) | 71 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 72 | Error Code | length of <srvType-list> | 73 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 74 | <srvtype--list> \ 75 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 76 */ 77 78 buffer buff; 79 80 // read the slp service info from conf and create the service type string 81 slp::handler::internal::ServiceList svcList = 82 slp::handler::internal::readSLPServiceInfo(); 83 if (svcList.size() <= 0) 84 { 85 buff.resize(0); 86 std::cerr << "SLP unable to read the service info\n"; 87 return std::make_tuple((int)slp::Error::INTERNAL_ERROR, buff); 88 } 89 90 std::string service; 91 bool firstIteration = true; 92 for_each(svcList.cbegin(), svcList.cend(), 93 [&service, &firstIteration](const auto& svc) { 94 if (firstIteration == true) 95 { 96 service = svc.first; 97 firstIteration = false; 98 } 99 else 100 { 101 service += ","; 102 service += svc.first; 103 } 104 }); 105 106 buff = prepareHeader(req); 107 108 /* Need to modify the length and the function type field of the header 109 * as it is dependent on the handler of the service */ 110 111 std::cout << "service=" << service.c_str() << "\n"; 112 113 uint8_t length = buff.size() + /* 14 bytes header + length of langtag */ 114 slp::response::SIZE_ERROR + /* 2 byte err code */ 115 slp::response::SIZE_SERVICE + /* 2 byte srvtype len */ 116 service.length(); 117 118 buff.resize(length); 119 120 std::copy_n(&length, slp::header::SIZE_LENGTH, 121 buff.data() + slp::header::OFFSET_LENGTH); 122 123 /* error code is already set to 0 moving to service type len */ 124 125 uint16_t serviceTypeLen = service.length(); 126 serviceTypeLen = endian::to_network(serviceTypeLen); 127 128 std::copy_n((uint8_t*)&serviceTypeLen, slp::response::SIZE_SERVICE, 129 buff.data() + slp::response::OFFSET_SERVICE_LEN); 130 131 /* service type data */ 132 std::copy_n((uint8_t*)service.c_str(), service.length(), 133 (buff.data() + slp::response::OFFSET_SERVICE)); 134 135 return std::make_tuple(slp::SUCCESS, buff); 136 } 137 138 std::tuple<int, buffer> processSrvRequest(const Message& req) 139 { 140 /* 141 Service Reply 142 0 1 2 3 143 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 144 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 145 | Service Location header (function = SrvRply = 2) | 146 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 147 | Error Code | URL Entry count | 148 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 149 | <URL Entry 1> ... <URL Entry N> \ 150 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 151 152 URL Entry 153 0 1 2 3 154 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 155 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 156 | Reserved | Lifetime | URL Length | 157 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 158 |URL len, contd.| URL (variable length) \ 159 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 160 |# of URL auths | Auth. blocks (if any) \ 161 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 162 */ 163 164 buffer buff; 165 // Get all the services which are registered 166 slp::handler::internal::ServiceList svcList = 167 slp::handler::internal::readSLPServiceInfo(); 168 if (svcList.size() <= 0) 169 { 170 buff.resize(0); 171 std::cerr << "SLP unable to read the service info\n"; 172 return std::make_tuple((int)slp::Error::INTERNAL_ERROR, buff); 173 } 174 175 // return error if serice type doesn't match 176 auto& svcName = req.body.srvrqst.srvType; 177 auto svcIt = svcList.find(svcName); 178 if (svcIt == svcList.end()) 179 { 180 buff.resize(0); 181 std::cerr << "SLP unable to find the service=" << svcName << "\n"; 182 return std::make_tuple((int)slp::Error::INTERNAL_ERROR, buff); 183 } 184 // Get all the interface address 185 auto ifaddrList = slp::handler::internal::getIntfAddrs(); 186 if (ifaddrList.size() <= 0) 187 { 188 buff.resize(0); 189 std::cerr << "SLP unable to read the interface address\n"; 190 return std::make_tuple((int)slp::Error::INTERNAL_ERROR, buff); 191 } 192 193 buff = prepareHeader(req); 194 // Calculate the length and resize the buffer 195 uint8_t length = buff.size() + /* 14 bytes header + length of langtag */ 196 slp::response::SIZE_ERROR + /* 2 bytes error code */ 197 slp::response::SIZE_URL_COUNT; /* 2 bytes srvtype len */ 198 199 buff.resize(length); 200 201 // Populate the url count 202 uint16_t urlCount = ifaddrList.size(); 203 urlCount = endian::to_network(urlCount); 204 205 std::copy_n((uint8_t*)&urlCount, slp::response::SIZE_URL_COUNT, 206 buff.data() + slp::response::OFFSET_URL_ENTRY); 207 208 // Find the service 209 const slp::ConfigData& svc = svcIt->second; 210 // Populate the URL Entries 211 auto pos = slp::response::OFFSET_URL_ENTRY + slp::response::SIZE_URL_COUNT; 212 for (const auto& addr : ifaddrList) 213 { 214 std::string url = svc.name + ':' + svc.type + "//" + addr + ',' + 215 svc.port; 216 217 buff.resize(buff.size() + slp::response::SIZE_URL_ENTRY + url.length()); 218 219 uint8_t reserved = 0; 220 uint16_t auth = 0; 221 uint16_t lifetime = endian::to_network<uint16_t>(slp::LIFETIME); 222 uint16_t urlLength = url.length(); 223 224 std::copy_n((uint8_t*)&reserved, slp::response::SIZE_RESERVED, 225 buff.data() + pos); 226 227 pos += slp::response::SIZE_RESERVED; 228 229 std::copy_n((uint8_t*)&lifetime, slp::response::SIZE_LIFETIME, 230 buff.data() + pos); 231 232 pos += slp::response::SIZE_LIFETIME; 233 234 urlLength = endian::to_network(urlLength); 235 std::copy_n((uint8_t*)&urlLength, slp::response::SIZE_URLLENGTH, 236 buff.data() + pos); 237 pos += slp::response::SIZE_URLLENGTH; 238 239 std::copy_n((uint8_t*)url.c_str(), url.length(), buff.data() + pos); 240 pos += url.length(); 241 242 std::copy_n((uint8_t*)&auth, slp::response::SIZE_AUTH, 243 buff.data() + pos); 244 pos += slp::response::SIZE_AUTH; 245 } 246 uint8_t packetLength = buff.size(); 247 std::copy_n((uint8_t*)&packetLength, slp::header::SIZE_VERSION, 248 buff.data() + slp::header::OFFSET_LENGTH); 249 250 return std::make_tuple((int)slp::SUCCESS, buff); 251 } 252 253 std::list<std::string> getIntfAddrs() 254 { 255 std::list<std::string> addrList; 256 257 struct ifaddrs* ifaddr; 258 // attempt to fill struct with ifaddrs 259 if (getifaddrs(&ifaddr) == -1) 260 { 261 return addrList; 262 } 263 264 slp::deleted_unique_ptr<ifaddrs> ifaddrPtr( 265 ifaddr, [](ifaddrs* addr) { freeifaddrs(addr); }); 266 267 ifaddr = nullptr; 268 269 for (ifaddrs* ifa = ifaddrPtr.get(); ifa != nullptr; ifa = ifa->ifa_next) 270 { 271 // walk interfaces 272 if (ifa->ifa_addr == nullptr) 273 { 274 continue; 275 } 276 277 // get only INET interfaces not ipv6 278 if (ifa->ifa_addr->sa_family == AF_INET) 279 { 280 // if loopback, or not running ignore 281 if ((ifa->ifa_flags & IFF_LOOPBACK) || 282 !(ifa->ifa_flags & IFF_RUNNING)) 283 { 284 continue; 285 } 286 287 char tmp[INET_ADDRSTRLEN] = {0}; 288 289 inet_ntop(AF_INET, 290 &(((struct sockaddr_in*)(ifa->ifa_addr))->sin_addr), tmp, 291 sizeof(tmp)); 292 addrList.emplace_back(tmp); 293 } 294 } 295 296 return addrList; 297 } 298 299 slp::handler::internal::ServiceList readSLPServiceInfo() 300 { 301 using namespace std::string_literals; 302 slp::handler::internal::ServiceList svcLst; 303 slp::ConfigData service; 304 struct dirent* dent = nullptr; 305 306 // Open the services dir and get the service info 307 // from service files. 308 // Service File format would be "ServiceName serviceType Port" 309 DIR* dir = opendir(SERVICE_DIR); 310 // wrap the pointer into smart pointer. 311 slp::deleted_unique_ptr<DIR> dirPtr(dir, [](DIR* dir) { 312 if (!dir) 313 { 314 closedir(dir); 315 } 316 }); 317 dir = nullptr; 318 319 if (dirPtr.get()) 320 { 321 while ((dent = readdir(dirPtr.get())) != NULL) 322 { 323 if (dent->d_type == DT_REG) // regular file 324 { 325 auto absFileName = std::string(SERVICE_DIR) + dent->d_name; 326 std::ifstream readFile(absFileName); 327 readFile >> service; 328 service.name = "service:"s + service.name; 329 svcLst.emplace(service.name, service); 330 } 331 } 332 } 333 return svcLst; 334 } 335 } // namespace internal 336 337 std::tuple<int, buffer> processRequest(const Message& msg) 338 { 339 int rc = slp::SUCCESS; 340 buffer resp(0); 341 std::cout << "SLP Processing Request=" << msg.header.functionID << "\n"; 342 343 switch (msg.header.functionID) 344 { 345 case (uint8_t)slp::FunctionType::SRVTYPERQST: 346 std::tie(rc, 347 resp) = slp::handler::internal::processSrvTypeRequest(msg); 348 break; 349 case (uint8_t)slp::FunctionType::SRVRQST: 350 std::tie(rc, resp) = slp::handler::internal::processSrvRequest(msg); 351 break; 352 default: 353 rc = (uint8_t)slp::Error::MSG_NOT_SUPPORTED; 354 } 355 return std::make_tuple(rc, resp); 356 } 357 358 buffer processError(const Message& req, uint8_t err) 359 { 360 buffer buff; 361 buff = slp::handler::internal::prepareHeader(req); 362 363 std::copy_n(&err, slp::response::SIZE_ERROR, 364 buff.data() + slp::response::OFFSET_ERROR); 365 return buff; 366 } 367 } // namespace handler 368 } // namespace slp 369