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