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