blob: fd6670a187217810776aee28fd78450782dea67e [file] [log] [blame]
Ratan Gupta07c462a2016-12-14 00:40:30 +05301#include "slp.hpp"
2
3#include <arpa/inet.h>
4#include <ifaddrs.h>
5#include <net/if.h>
6#include <string.h>
7
8#include <algorithm>
9
10#include "endian.hpp"
11#include "slp_meta.hpp"
12
13namespace slp
14{
15namespace handler
16{
17
18namespace internal
19{
20
21buffer prepareHeader(const Message& req)
22{
23 uint8_t length = slp::header::MIN_LEN + /* 14 bytes for header */
24 req.header.langtag.length() + /* Actual length of lang tag */
25 slp::response::SIZE_ERROR; /* 2 bytes for error code */
26
27 buffer buff(length, 0);
28
29 buff[slp::header::OFFSET_VERSION] = req.header.version;
30
31 //will increment the function id from 1 as reply
32 buff[slp::header::OFFSET_FUNCTION] = req.header.functionID + 1;
33
34 std::copy_n(&length, slp::header::SIZE_LENGTH,
35 buff.data() +
36 slp::header::OFFSET_LENGTH);
37
38 auto flags = endian::to_network(req.header.flags);
39
40 std::copy_n((uint8_t*)&flags, slp::header::SIZE_FLAGS,
41 buff.data() +
42 slp::header::OFFSET_FLAGS);
43
44 std::copy_n(req.header.extOffset.data(), slp::header::SIZE_EXT,
45 buff.data() +
46 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() +
52 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() +
58 slp::header::OFFSET_LANG_LEN);
59
60 std::copy_n((uint8_t*)req.header.langtag.c_str(), req.header.langtag.length(),
61 buff.data() +
62 slp::header::OFFSET_LANG);
63 return buff;
64
65}
66
67std::tuple<int, buffer> processSrvTypeRequest(
68 const Message& req)
69{
70
71 /*
72 0 1 2 3
73 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
74 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
75 | Service Location header (function = SrvTypeRply = 10) |
76 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
77 | Error Code | length of <srvType-list> |
78 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
79 | <srvtype--list> \
80 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
81 */
82
83 buffer buff;
84
85 //read the slp service info from conf and create the service type string
86 slp::handler::internal::ServiceList svcList =
87 slp::handler::internal::readSLPServiceInfo(slp::CONF_FILE);
88 if (svcList.size() <= 0)
89 {
90 buff.resize(0);
91 return std::make_tuple((int)slp::Error::INTERNAL_ERROR, buff);
92 }
93
94 std::string service;
95 bool firstIteration = true;
96 for_each(svcList.cbegin(), svcList.cend(),
97 [&service, &firstIteration](const auto& svc)
98 {
99 if (firstIteration == true)
100 {
101 service = svc.first;
102 firstIteration = false;
103 }
104 else
105 {
106 service += ",";
107 service += svc.first;
108 }
109 });
110
111 buff = prepareHeader(req);
112
113 /* Need to modify the length and the function type field of the header
114 * as it is dependent on the handler of the service */
115
116 std::cout << "service=" << service.c_str() << "\n";
117
118 uint8_t length = buff.size() + /* 14 bytes header + length of langtag */
119 slp::response::SIZE_ERROR + /* 2 byte err code */
120 slp::response::SIZE_SERVICE + /* 2 byte srvtype len */
121 service.length();
122
123
124 buff.resize(length);
125
126 std::copy_n(&length, slp::header::SIZE_LENGTH,
127 buff.data() +
128 slp::header::OFFSET_LENGTH);
129
130 /* error code is already set to 0 moving to service type len */
131
132 uint16_t serviceTypeLen = service.length();
133 serviceTypeLen = endian::to_network(serviceTypeLen);
134
135 std::copy_n((uint8_t*)&serviceTypeLen, slp::response::SIZE_SERVICE,
136 buff.data() +
137 slp::response::OFFSET_SERVICE_LEN);
138
139 /* service type data */
140 std::copy_n((uint8_t*)service.c_str(), service.length(),
141 (buff.data() +
142 slp::response::OFFSET_SERVICE));
143
144 return std::make_tuple(slp::SUCCESS, buff);
145}
146
147std::tuple<int, buffer> processSrvRequest(
148 const Message& req)
149{
150
151 /*
152 Service Reply
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 | Service Location header (function = SrvRply = 2) |
157 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
158 | Error Code | URL Entry count |
159 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
160 | <URL Entry 1> ... <URL Entry N> \
161 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
162
163 URL Entry
164 0 1 2 3
165 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
166 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
167 | Reserved | Lifetime | URL Length |
168 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
169 |URL len, contd.| URL (variable length) \
170 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
171 |# of URL auths | Auth. blocks (if any) \
172 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
173 */
174
175 buffer buff;
176 //Get all the services which are registered
177 slp::handler::internal::ServiceList svcList =
178 slp::handler::internal::readSLPServiceInfo(slp::CONF_FILE);
179 if (svcList.size() <= 0)
180 {
181 buff.resize(0);
182 return std::make_tuple((int)slp::Error::INTERNAL_ERROR, buff);
183 }
184
185 //return error if serice type doesn't match
186 auto& svcName = req.body.srvrqst.srvType;
187 auto svcIt = svcList.find(svcName);
188 if (svcIt == svcList.end())
189 {
190 buff.resize(0);
191 return std::make_tuple((int)slp::Error::INTERNAL_ERROR, buff);
192 }
193 //Get all the interface address
194 auto ifaddrList = slp::handler::internal::getIntfAddrs();
195 if (ifaddrList.size() <= 0)
196 {
197 buff.resize(0);
198 return std::make_tuple((int)slp::Error::INTERNAL_ERROR, buff);
199 }
200
201 buff = prepareHeader(req);
202 //Calculate the length and resize the buffer
203 uint8_t length = buff.size() + /* 14 bytes header + length of langtag */
204 slp::response::SIZE_ERROR + /* 2 bytes error code */
205 slp::response::SIZE_URL_COUNT; /* 2 bytes srvtype len */
206
207 buff.resize(length);
208
209 //Populate the url count
210 uint16_t urlCount = ifaddrList.size();
211 urlCount = endian::to_network(urlCount);
212
213 std::copy_n((uint8_t*)&urlCount, slp::response::SIZE_URL_COUNT,
214 buff.data() +
215 slp::response::OFFSET_URL_ENTRY);
216
217 //Find the service
218 const slp::ConfigData& svc = svcIt->second;
219 //Populate the URL Entrys
220 auto pos = slp::response::OFFSET_URL_ENTRY + slp::response::SIZE_URL_COUNT;
221 for (const auto& addr : ifaddrList)
222 {
223 std::string url = svc.name + ':' + svc.type +
224 "//" + addr + ',' + svc.port;
225
226
227 buff.resize(buff.size() +
228 slp::response::SIZE_URL_ENTRY +
229 url.length());
230
231 uint8_t reserved = 0;
232 uint16_t auth = 0;
233 uint16_t lifetime = endian::to_network<uint16_t>(slp::LIFETIME);
234 uint16_t urlLength = url.length();
235
236 std::copy_n((uint8_t*)&reserved, slp::response::SIZE_RESERVED,
237 buff.data() + pos);
238
239 pos += slp::response::SIZE_RESERVED;
240
241
242 std::copy_n((uint8_t*)&lifetime, slp::response::SIZE_LIFETIME,
243 buff.data() + pos);
244
245 pos += slp::response::SIZE_LIFETIME;
246
247 urlLength = endian::to_network(urlLength);
248 std::copy_n((uint8_t*)&urlLength, slp::response::SIZE_URLLENGTH,
249 buff.data() + pos);
250 pos += slp::response::SIZE_URLLENGTH;
251
252 std::copy_n((uint8_t*)url.c_str(), url.length(),
253 buff.data() + pos);
254 pos += url.length();
255
256 std::copy_n((uint8_t*)&auth, slp::response::SIZE_AUTH,
257 buff.data() + pos);
258 pos += slp::response::SIZE_AUTH;
259 }
260 uint8_t packetLength = buff.size();
261 std::copy_n((uint8_t*)&packetLength, slp::header::SIZE_VERSION,
262 buff.data() +
263 slp::header::OFFSET_LENGTH);
264
265 return std::make_tuple((int)slp::SUCCESS, buff);
266}
267
268std::list<std::string> getIntfAddrs()
269{
270 std::list<std::string> addrList;
271
272 struct ifaddrs* ifaddr;
273 // attempt to fill struct with ifaddrs
274 if (getifaddrs(&ifaddr) == -1)
275 {
276 return addrList;
277 }
278
279 slp::deleted_unique_ptr<ifaddrs> ifaddrPtr(ifaddr, [](ifaddrs * addr)
280 {
281 freeifaddrs(addr);
282 });
283
284 ifaddr = nullptr;
285
286 for (ifaddrs* ifa = ifaddrPtr.get(); ifa != nullptr; ifa = ifa->ifa_next)
287 {
288 // walk interfaces
289 if (ifa->ifa_addr == nullptr)
290 {
291 continue;
292 }
293
294 // get only INET interfaces not ipv6
295 if (ifa->ifa_addr->sa_family == AF_INET)
296 {
297 // if loopback, or not running ignore
298 if ((ifa->ifa_flags & IFF_LOOPBACK) ||
299 !(ifa->ifa_flags & IFF_RUNNING))
300 {
301 continue;
302 }
303
304 char tmp[INET_ADDRSTRLEN] = { 0 };
305
306 inet_ntop(AF_INET,
307 &(((struct sockaddr_in*)(ifa->ifa_addr))->sin_addr),
308 tmp,
309 sizeof(tmp));
310 addrList.emplace_back(tmp);
311 }
312 }
313
314 return addrList;
315}
316
317slp::handler::internal::ServiceList readSLPServiceInfo(
318 const std::string& filename)
319{
320 using namespace std::string_literals;
321 /*Conf File format would be
322 ServiceName serviceType Port */
323
324 slp::handler::internal::ServiceList svcLst;
325
326 std::ifstream readFile(filename);
327 slp::ConfigData service;
328 //Read all the service from the file
329 while (readFile >> service)
330 {
331 std::string tmp = "service:"s + service.name;
332 service.name = tmp;
333 svcLst.emplace(service.name, service);
334 }
335
336 return svcLst;
337}
338}//namespace internal
339
340std::tuple<int, buffer> processRequest(
341 const Message& msg)
342{
343 int rc = slp::SUCCESS;
344 buffer resp(0);
345 switch (msg.header.functionID)
346 {
347 case (uint8_t)slp::FunctionType::SRVTYPERQST:
348 std::tie(rc, resp) = slp::handler::internal::processSrvTypeRequest(msg);
349 break;
350 case (uint8_t)slp::FunctionType::SRVRQST:
351 std::tie(rc, resp) = slp::handler::internal::processSrvRequest(msg);
352 break;
353 default:
354 rc = (uint8_t)slp::Error::MSG_NOT_SUPPORTED;
355 }
356 return std::make_tuple(rc, resp);
357}
358
359buffer processError(const Message& req,
360 uint8_t err)
361{
362 buffer buff;
363 buff = slp::handler::internal::prepareHeader(req);
364
365 std::copy_n(&err, slp::response::SIZE_ERROR,
366 buff.data() +
367 slp::response::OFFSET_ERROR);
368 return buff;
369
370}
371}//namespace handler
372}//namespace slp