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