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
prepareHeader(const Message & req)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
processSrvTypeRequest(const Message & req)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
processSrvRequest(const Message & req)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 =
230 svc.name + ':' + svc.type + "//" + addr + ',' + svc.port;
231
232 // See if total response size exceeds our max
233 uint32_t totalLength =
234 buff.size() + slp::response::SIZE_URL_ENTRY + 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
getIntfAddrs()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(ifaddr, [](ifaddrs* addr) {
291 freeifaddrs(addr);
292 });
293
294 ifaddr = nullptr;
295
296 for (ifaddrs* ifa = ifaddrPtr.get(); ifa != nullptr; ifa = ifa->ifa_next)
297 {
298 // walk interfaces
299 if (ifa->ifa_addr == nullptr)
300 {
301 continue;
302 }
303
304 // get only INET interfaces not ipv6
305 if (ifa->ifa_addr->sa_family == AF_INET)
306 {
307 // if loopback, or not running ignore
308 if ((ifa->ifa_flags & IFF_LOOPBACK) ||
309 !(ifa->ifa_flags & IFF_RUNNING))
310 {
311 continue;
312 }
313
314 char tmp[INET_ADDRSTRLEN] = {0};
315
316 inet_ntop(AF_INET,
317 &(((struct sockaddr_in*)(ifa->ifa_addr))->sin_addr), tmp,
318 sizeof(tmp));
319 addrList.emplace_back(tmp);
320 }
321 }
322
323 return addrList;
324 }
325
readSLPServiceInfo()326 slp::handler::internal::ServiceList readSLPServiceInfo()
327 {
328 using namespace std::string_literals;
329 slp::handler::internal::ServiceList svcLst;
330 slp::ConfigData service;
331 struct dirent* dent = nullptr;
332
333 // Open the services dir and get the service info
334 // from service files.
335 // Service File format would be "ServiceName serviceType Port"
336 DIR* dir = opendir(SERVICE_DIR);
337 // wrap the pointer into smart pointer.
338 slp::deleted_unique_ptr<DIR> dirPtr(dir, [](DIR* dir) {
339 if (!dir)
340 {
341 closedir(dir);
342 }
343 });
344 dir = nullptr;
345
346 if (dirPtr.get())
347 {
348 while ((dent = readdir(dirPtr.get())) != NULL)
349 {
350 if (dent->d_type == DT_REG) // regular file
351 {
352 auto absFileName = std::string(SERVICE_DIR) + dent->d_name;
353 std::ifstream readFile(absFileName);
354 readFile >> service;
355 service.name = "service:"s + service.name;
356 svcLst.emplace(service.name, service);
357 }
358 }
359 }
360 return svcLst;
361 }
362 } // namespace internal
363
processRequest(const Message & msg)364 std::tuple<int, buffer> processRequest(const Message& msg)
365 {
366 int rc = slp::SUCCESS;
367 buffer resp;
368 std::cout << "SLP Processing Request="
369 << std::bitset<8>(msg.header.functionID) << "\n";
370
371 switch (msg.header.functionID)
372 {
373 case (uint8_t)slp::FunctionType::SRVTYPERQST:
374 std::tie(rc, resp) =
375 slp::handler::internal::processSrvTypeRequest(msg);
376 break;
377 case (uint8_t)slp::FunctionType::SRVRQST:
378 std::tie(rc, resp) = slp::handler::internal::processSrvRequest(msg);
379 break;
380 default:
381 rc = (uint8_t)slp::Error::MSG_NOT_SUPPORTED;
382 }
383 return std::make_tuple(rc, resp);
384 }
385
processError(const Message & req,uint8_t err)386 buffer processError(const Message& req, uint8_t err)
387 {
388 if (req.header.functionID != 0)
389 {
390 std::cout << "Processing Error for function: "
391 << std::bitset<8>(req.header.functionID) << std::endl;
392 }
393
394 /* 0 1 2 3
395 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
396 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
397 | Service Location header (function = SrvRply = 2) |
398 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
399 | Error Code | URL Entry count |
400 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
401 | <URL Entry 1> ... <URL Entry N> \
402 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+*/
403
404 // There has been some sort of issue processing the request from
405 // the client. Can not assume the input request buffer is valid
406 // so just create an empty buffer with the non-variable size
407 // fields set and the error code
408 uint8_t length = slp::header::MIN_LEN + /* 14 bytes for header */
409 slp::response::SIZE_ERROR; /* 2 bytes for error code */
410
411 buffer buff(length, 0);
412
413 static_assert(sizeof(err) == 1, "Errors should be 1 byte.");
414
415 buff[slp::header::OFFSET_VERSION] = req.header.version;
416
417 // will increment the function id from 1 as reply
418 buff[slp::header::OFFSET_FUNCTION] = req.header.functionID + 1;
419
420 std::copy_n(&length, slp::header::SIZE_LENGTH,
421 buff.data() + slp::header::OFFSET_LENGTH);
422
423 auto flags = endian::to_network(req.header.flags);
424
425 std::copy_n((uint8_t*)&flags, slp::header::SIZE_FLAGS,
426 buff.data() + slp::header::OFFSET_FLAGS);
427
428 std::copy_n(req.header.extOffset.data(), slp::header::SIZE_EXT,
429 buff.data() + slp::header::OFFSET_EXT);
430
431 auto xid = endian::to_network(req.header.xid);
432
433 std::copy_n((uint8_t*)&xid, slp::header::SIZE_XID,
434 buff.data() + slp::header::OFFSET_XID);
435
436 // This is an invalid header from user so just fill in 0 for langtag
437 uint16_t langtagLen = 0;
438 std::copy_n((uint8_t*)&langtagLen, slp::header::SIZE_LANG,
439 buff.data() + slp::header::OFFSET_LANG_LEN);
440
441 // Since this is network order, the err should go in the 2nd byte of the
442 // error field.
443 buff[slp::header::MIN_LEN + 1] = err;
444
445 return buff;
446 }
447 } // namespace handler
448 } // namespace slp
449