]> git.llucax.com Git - z.facultad/75.74/practicos.git/blob - practicas/pipi/src/nameserver.cpp
f04b4ab3a33a862233cc4fb7e3ac7d10c97234b4
[z.facultad/75.74/practicos.git] / practicas / pipi / src / nameserver.cpp
1 #include "nameserver.h"
2 #include <sstream>
3 #include <algorithm>
4 #include <iterator>
5 #include <sstream>
6 #include <functional>
7 #ifdef DEBUG
8 #include <iostream>
9 #endif
10
11 NameServer::Name::Name(const std::string& s)
12 {
13     std::istringstream iss(s);
14     std::string tok;
15     while (std::getline(iss, tok, '.'))
16         push_back(tok);
17 }
18
19 std::ostream& operator<< (std::ostream& os, const NameServer::Name& name)
20 {
21     if (name.empty())
22         return os;
23     std::copy(name.begin(), name.end() - 1,
24             std::ostream_iterator< std::string >(os, "."));
25     return os << name.back();
26 }
27
28 NameServer::Name::operator std::string () const
29 {
30     std::stringstream ss;
31     ss << *this;
32     return ss.str();
33 }
34
35 /// Parsea una zona
36 static std::istream& parsezone(std::istream& is, NameServer::Zone& z)
37     throw (std::runtime_error)
38 {
39     std::string line, sname, ip;
40     // [dominio] [ttl] [parent ip]
41     while (std::getline(is, line) && (line == "")); // Salteo líneas en blanco
42     if (!is)
43         return is;
44     std::istringstream iss(line);
45     if (!(iss >> sname >> z.ttl >> ip))
46         throw std::runtime_error("Error al parsear");
47     z.name = sname;
48 #ifdef DEBUG_PARSER
49     std::cerr << "parsezone: IP = " << ip << "\n\n";
50 #endif
51     z.parent = IPAddr(ip);
52     // un record por linea, sin líneas vacías
53     // [name] [type] [ip]
54     while (std::getline(is, line) && (line != ""))
55     {
56         iss.clear();
57         iss.str(line);
58         std::string key, type;
59         if (!(iss >> key >> type >> ip))
60             throw std::runtime_error("Error al parsear");
61         typedef NameServer::Record Rec;
62 #ifdef DEBUG_PARSER
63         std::cerr << "parsezone: IP = " << ip << "\n\n";
64 #endif
65         Rec r((type == "NS") ? Rec::NS : Rec::A, IPAddr(ip));
66         z.records.insert(NameServer::Zone::records_t::value_type(key, r));
67     }
68 #ifdef DEBUG_PARSER
69     std::cerr << "parsezone: " << z << "\n\n";
70 #endif
71     return is;
72 }
73
74 /// Constructor
75 NameServer::Zone::Zone(std::string sname, size_t ttl, const IPAddr& parent):
76     name(sname), ttl(ttl), parent(parent)
77 {
78 }
79
80 /// Constructor
81 NameServer::Zone::Zone(std::istream& is)
82     throw (std::runtime_error)
83 {
84     // Parsea la zona
85     if (!parsezone(is, *this))
86         throw std::runtime_error("Error de parser, no hay zona");
87 }
88
89 /// Limpia una zona
90 void NameServer::Zone::clear()
91 {
92     name.clear();
93     records.clear();
94 }
95
96 /// Constructor
97 NameServer::NameServer(std::istream& is, IPIn& ipin, IPOut& ipout,
98         DevQue& req_que, DevQue& res_que, DevQue& snd_que)
99     throw (std::runtime_error):
100     ipin(ipin), ipout(ipout), req_que(req_que), res_que(res_que),
101         snd_que(snd_que)
102 {
103     Zone z;
104     while (parsezone(is, z))
105     {
106         zones.push_back(z);
107         z.clear();
108 #ifdef DEBUG_PARSER
109         std::cerr << "NameServer: " << z << "\n\n";
110 #endif
111     }
112
113 #ifdef DEBUG_RESOLV
114     std::cout << "NameServer: req_que_id = " << req_que.que_id
115         << ", res_que_id = " << res_que.que_id
116         << ", snd_que_id = " << snd_que.que_id << "\n";
117 #endif
118 }
119
120 /// Devuelve la parte izquierda de n, si la parte derecha coincide exactamente
121 /// con m, si no devuelve un vector vacío.
122 /// Elemplo: name_split("mi.domi.nio", "domi.nio") == ["mi"]
123 ///          name_split("dos.mi.domi.nio", "domi.nio") == ["dos", "mi"]
124 ///          name_split("domi.nio", "domi.nio") == []
125 ///          name_split("papeli.nio", "domi.nio") == []
126 static NameServer::Name
127 name_split(NameServer::Name n, NameServer::Name m)
128 {
129     NameServer::Name r;
130     std::reverse(n.begin(), n.end());
131     std::reverse(m.begin(), m.end());
132     // Si m es más grande o igual que n, no hay nada que hacer
133     if (n.size() <= m.size())
134         return r;
135     // Si no coincide la parte derecha, no hay nada que hacer
136     NameServer::Name::size_type i;
137     for (i = 0; i < m.size(); ++i)
138         if (n[i] != m[i])
139             return r;
140     // Si era todo igual y sobran cosas, devolvemos lo que "sobra"
141     while (i < n.size())
142         r.push_back(n[i++]);
143     std::reverse(r.begin(), r.end());
144 #ifdef DEBUG_NAME
145     std::cerr << "name_split(" << n << ", " << m << ") -> " << r << "\n";
146 #endif
147     return r;
148 }
149
150 /// Devuelve -1 si es un nombre "hijo" (la parte derecha de n está contenida
151 /// completamente en m, pero m y n no son iguales), 0 si m == n y 1 si es
152 /// "padre" (m no coincide con la parte derecha de n).
153 /// Elemplo: namecmp("mi.domi.nio", "domi.nio") == -1
154 ///          namecmp("otra.cosa", "domi.nio") == 1
155 ///          namecmp("papeli.nio", "domi.nio") == 1
156 ///          namecmp("domi.nio", "domi.nio") == 0
157 /*enum name_cmp_t
158 {
159     NC_DIRECT_CHILD,  ///> Hijo directo, es decir, está en la zona m
160     NC_CHILD,         ///> Hijo indirecto, está en una zona que cuelga de m
161     NC_EQUAL,         ///> Es el host de la zona m
162     NC_DIRECT_PARENT, ///> Padre directo, es decir, coincide en parte con m
163     NC_PARENT         ///> Padre indirecto, es completamente distinto a m
164 };*/
165 #if 0
166 static int name_cmp(const NameServer::Name& n, const NameServer::Name& m)
167 {
168     // Si m es más grande que n, seguro es padre
169     if (n.size() < m.size())
170         return 1;
171     // Si no coincide la parte derecha, seguro es padre
172     NameServer::Name::size_type i;
173     for (i = m.size(); i > 0; --i)
174         if (n[i-1] != m[i-1])
175             return 1;
176     // Si era todo igual y sobran cosas, es hijo
177     if (i)
178         return -1;
179     // Si no, son iguales.
180     return 0;
181 }
182 #endif
183
184 struct search_zone: std::unary_function< NameServer::Zone, bool >
185 {
186     bool local;
187     ResolvProtoResponse resp;
188     const NameServer::Name& name;
189     search_zone(const NameServer::Name& n): local(false), name(n) {}
190     bool operator() (const NameServer::Zone& z)
191     {
192         bool found = false;
193         NameServer::Name local_part = name_split(name, z.name);
194         if (!local_part.empty()) // Está en esta zona
195         {
196             local = true;
197             std::string n = local_part.back(); // Obtengo última parte
198             // busco
199             typedef NameServer::Zone::records_t::const_iterator itt;
200             std::pair<itt, itt> p = z.records.equal_range(n);
201             for (; p.first != p.second; ++p.first)
202             {
203                 const NameServer::Record& r = (*p.first).second;
204                 // Tiene que buscar solo A porque era un nombre
205                 if ((local_part.size() == 1) && (r.type != NameServer::Record::A))
206                     continue;
207                 // Tiene que seguir para abajo, solo busca NS
208                 if ((local_part.size() > 1) && r.type != NameServer::Record::NS)
209                     continue;
210                 found = true;
211                 resp.ret = (r.type == NameServer::Record::NS) ? RP_RES_NS
212                     : RP_RES_A;
213                 resp.ttl = z.ttl;
214                 resp.ips.push_back(r.ip);
215             }
216         }
217         return found;
218     }
219 };
220
221 /// Resuelve un nombre de forma directa (no recursiva)
222 ResolvProtoResponse NameServer::resolv_direct(const Name& n)
223 {
224 #ifdef DEBUG_TRACE
225     std::cout << "NameServer::resolv_direct()\n";
226 #endif
227 #ifdef DEBUG_RESOLV
228     std::cerr << "resolv_direct -> tratando de resolver: " << n << "\n";
229 #endif
230     search_zone zs(n);
231     bool found;
232     for (zones_t::const_iterator i = zones.begin(); i!= zones.end(); ++i)
233         if ((found = zs(*i)))
234             break;
235     if (found)
236     {
237 #ifdef DEBUG_RESOLV
238         std::cerr << "resolv_direct found (local/hijo): " << zs.resp << "\n";
239 #endif
240         return zs.resp;
241     }
242     if (zs.local)
243     {
244 #ifdef DEBUG_RESOLV
245         std::cerr << "resolv_direct NOT FOUND (es local pero no existe)\n";
246 #endif
247         return ResolvProtoResponse(RP_RES_NOTFOUND);
248     }
249     cache_t::const_iterator i = cache.find(n);
250     // TODO TTL!?!
251     if (i != cache.end())
252     {
253 #ifdef DEBUG_RESOLV
254         std::cerr << "resolv_direct found (en cache): " << i->second << "\n";
255 #endif
256         const CacheRecord& cr = i->second;
257         return ResolvProtoResponse(RP_RES_A, cr.ttl, cr.ips);
258     }
259     if (zones.size())
260     {
261         // Busco una zona con padre para ver si puedo "trepar"
262         for (zones_t::const_iterator i = zones.begin(); i != zones.end(); ++i)
263         {
264 #ifdef DEBUG_RESOLV
265             std::cerr << "resolv_direct -> evaluando padre " << i->parent
266                 << "\n";
267 #endif
268             if (i->parent != IPAddr(0))
269             {
270 #ifdef DEBUG_RESOLV
271                 std::cerr << "resolv_direct found (al padre): "
272                     << i->parent << "\n";
273 #endif
274                 ResolvProtoResponse rpr(RP_RES_NS, i->ttl);
275                 rpr.ips.push_back(i->parent);
276                 return rpr;
277             }
278         }
279     }
280 #ifdef DEBUG_RESOLV
281     std::cerr << "resolv_direct NOT FOUND (no hay padre)\n";
282 #endif
283     // No hay padre, no puedo hacer nada más
284     return ResolvProtoResponse(RP_RES_NOTFOUND);
285 }
286
287 /// Resuelve un nombre de forma recursiva
288 ResolvProtoResponse NameServer::resolv_recursive(const Name& n)
289 {
290 #ifdef DEBUG_TRACE
291     std::cout << "NameServer::resolv_recursive()\n";
292 #endif
293     ResolvProtoResponse rpr = resolv_direct(n);
294     switch (rpr.ret)
295     {
296         case RP_RES_NS:
297 #ifdef DEBUG_RESOLV
298             std::cerr << "resolv_recursive -> redirect a " << rpr << "\n";
299 #endif
300             return resolv_recursive_r(n, rpr); // Sigo "bajando"
301         case RP_RES_A:
302 #ifdef DEBUG_RESOLV
303             std::cerr << "resolv_recursive -> gotcha! " << rpr << "\n";
304 #endif
305             // TODO agregar a cache
306             break;
307     }
308     return rpr; // Devuelvo el A o NOTFOUND
309 }
310
311 /// Resuelve un nombre de forma recursiva entrando a otros ns
312 ResolvProtoResponse NameServer::resolv_recursive_r(const Name& n,
313         ResolvProtoResponse rpr)
314 {
315 #ifdef DEBUG_TRACE
316     std::cout << "NameServer::resolv_recursive_r()\n";
317 #endif
318     ResolvProtoResponse r;
319     for (ResolvProtoResponse::ipvec_t::const_iterator ip = rpr.ips.begin();
320             ip != rpr.ips.end(); ++ip)
321     {
322         r = query(n, *ip);
323         switch (r.ret)
324         {
325             case RP_RES_NS:
326 #ifdef DEBUG_RESOLV
327                 std::cerr << "resolv_recursive_r -> redirect a " << r << "\n";
328 #endif
329                 return resolv_recursive_r(n, r); // Sigo "bajando"
330             case RP_RES_NOTFOUND:
331 #ifdef DEBUG_RESOLV
332                 std::cerr << "resolv_recursive_r -> NOT FOUND en " << *ip
333                     << ", sigo probando\n";
334 #endif
335                 break; // Sigo probando del mismo nivel
336             case RP_RES_A:
337 #ifdef DEBUG_RESOLV
338                 std::cerr << "resolv_recursive_r -> gotcha! " << r << "\n";
339 #endif
340                 // TODO agregar a cache
341                 return r; // Gotcha!
342         }
343     }
344 #ifdef DEBUG_RESOLV
345     std::cerr << "resolv_recursive_r -> NOT FOUND, no hay más por hacer\n";
346 #endif
347     return r; // NOTFOUND
348 }
349
350 /// Consulta a otro name server sobre un nombre
351 ResolvProtoResponse NameServer::query(const Name& n, const IPAddr& ip)
352 {
353 #ifdef DEBUG_TRACE
354     std::cout << "NameServer::query()\n";
355 #endif
356     ResolvProtoRequest r(std::string(n), RP_REQ_DIRECT);
357 #ifdef DEBUG_RESOLV
358     std::cerr << "query -> pidiendo " << r << " a " << ip << "\n";
359 #endif
360     // Envía a través de la cola de envío
361     snd_que.transmit(std::string(r), ip);
362     Dev::mac_type mac = ip;
363     std::string buf = res_que.receive(mac);
364     ResolvProtoResponse resp(buf);
365 #ifdef DEBUG_RESOLV
366     std::cerr << "query -> recibido " << resp << " de " << ip << "\n";
367 #endif
368     return resp;
369 }
370
371 void NameServer::recv_loop()
372 {
373 #ifdef DEBUG_TRACE
374     std::cout << "NameServer::recv_loop()\n";
375 #endif
376     while (true)
377     {
378         IPAddr src, dst;
379         uint8_t proto;
380         std::string s = ipin.recv(proto, src, dst);
381 #ifdef DEBUG_RESOLV
382             std::cout << "NameServer::recv_loop() -> recibido len=" << s.size()
383                 << " de " << src << " para " << dst << " (proto = "
384                 << unsigned(proto) << ")\n";
385 #endif
386         if (proto == RESOLV_PROTO) // Si es para nosotros
387         {
388             rp_pkt_type_t type;
389             memcpy(&type, s.data(), sizeof(uint8_t));
390             switch (type)
391             {
392                 // Request
393                 case RP_REQ_DIRECT:
394                 case RP_REQ_RECURSIVE:
395 #ifdef DEBUG_RESOLV
396                     std::cout << "---> " << ResolvProtoRequest(s) << "\n";
397 #endif
398                     req_que.transmit(s, src); // Encolo
399                     break;
400                 // Response
401                 default:
402 #ifdef DEBUG_RESOLV
403                     std::cout << "---> " << ResolvProtoResponse(s) << "\n";
404 #endif
405                     res_que.transmit(s, src); // Encolo
406             }
407         }
408     }
409 }
410
411 void NameServer::send_loop()
412 {
413 #ifdef DEBUG_TRACE
414     std::cout << "NameServer::send_loop()\n";
415 #endif
416     while (true)
417     {
418         Dev::mac_type mac = 0;
419         std::string buf = snd_que.receive(mac);
420 #ifdef DEBUG_RESOLV
421         std::cout << "NameServer::send_loop() -> envío request "
422             << ResolvProtoRequest(buf) << "\n";
423 #endif
424         ipout.send(buf, RESOLV_PROTO, IPAddr(mac));
425     }
426 }
427
428 void NameServer::req_loop()
429 {
430 #ifdef DEBUG_TRACE
431     std::cout << "NameServer::req_loop()\n";
432 #endif
433     while (true)
434     {
435         Dev::mac_type mac = 0;
436         ResolvProtoRequest req(req_que.receive(mac));
437 #ifdef DEBUG_RESOLV
438         std::cout << "NameServer::req_loop() -> recibido " << req << "\n";
439 #endif
440         ResolvProtoResponse res
441             = (req.query_type == RP_REQ_DIRECT)
442             ? resolv_direct(req.name)
443             : resolv_recursive(req.name);
444 #ifdef DEBUG_RESOLV
445         std::cout << "NameServer::req_loop() -> respondo " << res << "\n";
446 #endif
447         snd_que.transmit(std::string(res), IPAddr(mac));
448     }
449 }
450
451 std::ostream& operator<< (std::ostream& os, const NameServer::Record::type_t& t)
452 {
453     if (t == NameServer::Record::NS)
454         return os << "NS";
455     else
456         return os << "A";
457 }
458
459 std::ostream& operator<< (std::ostream& os, const NameServer::Record& r)
460 {
461     return os << r.type << " " << r.ip;
462 }
463
464 std::ostream& operator<< (std::ostream& os,
465         const NameServer::Zone::records_t::value_type& p)
466 {
467     return os << p.first << ": " << p.second;
468 }
469
470 std::ostream& operator<< (std::ostream& os, const NameServer::Zone& z)
471 {
472     os << "Zone " << z.name << " " << z.ttl << " " << z.parent << "\n";
473     std::copy(z.records.begin(), z.records.end(), std::ostream_iterator<
474             NameServer::Zone::records_t::value_type >(os, "\n"));
475     return os;
476 }
477
478 std::ostream& operator<< (std::ostream& os, const NameServer::CacheRecord& cr)
479 {
480     os << "CacheRecord(ttl=" << cr.ttl << ", records=";
481     std::copy(cr.ips.begin(), cr.ips.end(),
482             std::ostream_iterator< IPAddr >(os, ","));
483     return os << ")";
484 }
485
486 std::ostream& operator<< (std::ostream& os, const NameServer& ns)
487 {
488     os << "NameServer: zones[" << ns.zones.size() << "] (\n\n";
489     std::copy(ns.zones.begin(), ns.zones.end(),
490             std::ostream_iterator< NameServer::Zone >(os, "\n"));
491     return os << ")";
492 }
493
494 // vim: set et sw=4 sts=4 :