1 /*
2  * This file is part of PowerDNS or dnsdist.
3  * Copyright -- PowerDNS.COM B.V. and its contributors
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of version 2 of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * In addition, for the avoidance of any doubt, permission is granted to
10  * link this program with OpenSSL and to (re)distribute the binaries
11  * produced as the result of such linking.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #include <fstream>
24 
25 #include "dnsdist-rings.hh"
26 
numDistinctRequestors()27 size_t Rings::numDistinctRequestors()
28 {
29   std::set<ComboAddress, ComboAddress::addressOnlyLessThan> s;
30   for (const auto& shard : d_shards) {
31     std::lock_guard<std::mutex> rl(shard->queryLock);
32     for(const auto& q : shard->queryRing) {
33       s.insert(q.requestor);
34     }
35   }
36   return s.size();
37 }
38 
getTopBandwidth(unsigned int numentries)39 std::unordered_map<int, vector<boost::variant<string,double>>> Rings::getTopBandwidth(unsigned int numentries)
40 {
41   map<ComboAddress, unsigned int, ComboAddress::addressOnlyLessThan> counts;
42   uint64_t total=0;
43   for (const auto& shard : d_shards) {
44     {
45       std::lock_guard<std::mutex> rl(shard->queryLock);
46       for(const auto& q : shard->queryRing) {
47         counts[q.requestor]+=q.size;
48         total+=q.size;
49       }
50     }
51     {
52       std::lock_guard<std::mutex> rl(shard->respLock);
53       for(const auto& r : shard->respRing) {
54         counts[r.requestor]+=r.size;
55         total+=r.size;
56       }
57     }
58   }
59 
60   typedef vector<pair<unsigned int, ComboAddress>> ret_t;
61   ret_t rcounts;
62   rcounts.reserve(counts.size());
63   for(const auto& p : counts)
64     rcounts.push_back({p.second, p.first});
65   numentries = rcounts.size() < numentries ? rcounts.size() : numentries;
66   partial_sort(rcounts.begin(), rcounts.begin()+numentries, rcounts.end(), [](const ret_t::value_type&a, const ret_t::value_type&b)
67 	       {
68 		 return(b.first < a.first);
69 	       });
70   std::unordered_map<int, vector<boost::variant<string,double>>> ret;
71   uint64_t rest = 0;
72   unsigned int count = 1;
73   for(const auto& rc : rcounts) {
74     if(count==numentries+1) {
75       rest+=rc.first;
76     }
77     else {
78       ret.insert({count++, {rc.second.toString(), rc.first, 100.0*rc.first/total}});
79     }
80   }
81 
82   if (total > 0) {
83     ret.insert({count, {"Rest", rest, 100.0*rest/total}});
84   }
85   else {
86     ret.insert({count, {"Rest", rest, 100.0 }});
87   }
88 
89   return ret;
90 }
91 
loadFromFile(const std::string & filepath,const struct timespec & now)92 size_t Rings::loadFromFile(const std::string& filepath, const struct timespec& now)
93 {
94   ifstream ifs(filepath);
95   if (!ifs) {
96     throw std::runtime_error("unable to open the file at " + filepath);
97   }
98 
99   size_t inserted = 0;
100   string line;
101   dnsheader dh;
102   memset(&dh, 0, sizeof(dh));
103 
104   while (std::getline(ifs, line)) {
105     boost::trim_right_if(line, boost::is_any_of(" \r\n\x1a"));
106     boost::trim_left(line);
107     bool isResponse = false;
108     vector<string> parts;
109     stringtok(parts, line, " \t,");
110 
111     if (parts.size() == 7) {
112     }
113     else if (parts.size() >= 10 && parts.size() <= 12) {
114       isResponse = true;
115     }
116     else {
117       cerr<<"skipping line with "<<parts.size()<<"parts: "<<line<<endl;
118       continue;
119     }
120 
121     size_t idx = 0;
122     vector<string> timeStr;
123     stringtok(timeStr, parts.at(idx++), ".");
124     if (timeStr.size() != 2) {
125       cerr<<"skipping invalid time "<<parts.at(0)<<endl;
126       continue;
127     }
128 
129     struct timespec when;
130     try {
131       when.tv_sec = now.tv_sec + std::stoi(timeStr.at(0));
132       when.tv_nsec = now.tv_nsec + std::stoi(timeStr.at(1)) * 100 * 1000 * 1000;
133     }
134     catch (const std::exception& e) {
135       cerr<<"error parsing time "<<parts.at(idx-1)<<" from line "<<line<<endl;
136       continue;
137     }
138 
139     ComboAddress from(parts.at(idx++));
140     ComboAddress to;
141 
142     if (isResponse) {
143       to = ComboAddress(parts.at(idx++));
144     }
145     /* skip ID */
146     idx++;
147     DNSName qname(parts.at(idx++));
148     QType qtype(QType::chartocode(parts.at(idx++).c_str()));
149 
150     if (isResponse) {
151       insertResponse(when, from, qname, qtype.getCode(), 0, 0, dh, to);
152     }
153     else {
154       insertQuery(when, from, qname, qtype.getCode(), 0, dh);
155     }
156     ++inserted;
157   }
158 
159   return inserted;
160 }
161