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