]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/arguments.cc
Limit the number of queries sent out to get NS addresses per query.
[thirdparty/pdns.git] / pdns / arguments.cc
CommitLineData
12c86877 1/*
12471842
PL
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 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
12c86877 25#include "arguments.hh"
5b2cb3be 26#include <boost/algorithm/string.hpp>
2dfec518 27#include <boost/algorithm/string/compare.hpp>
4f6ef75f 28#include <boost/algorithm/string/predicate.hpp>
fa8fd4d2 29
61b26744 30#include "namespaces.hh"
4f6ef75f 31#include "logger.hh"
8a16d778
AT
32#include <sys/types.h>
33#include <dirent.h>
34#include <sys/stat.h>
35#include <unistd.h>
64f3490e 36#include <limits.h>
12c86877 37
12c86877
BH
38const ArgvMap::param_t::const_iterator ArgvMap::begin()
39{
40 return params.begin();
41}
42
43const ArgvMap::param_t::const_iterator ArgvMap::end()
44{
45 return params.end();
46}
47
48string & ArgvMap::set(const string &var)
49{
50 return params[var];
51}
52
8864bdf6
KM
53void ArgvMap::setDefault(const string &var, const string &value)
54{
55 if(! defaultmap.count(var))
56 defaultmap.insert(pair<string, string>(var, value));
57}
58
59void ArgvMap::setDefaults()
60{
61 for(map<string,string>::const_iterator i=params.begin();i!=params.end();++i)
62 if(! defaultmap.count(i->first))
63 defaultmap.insert(*i);
64}
65
12c86877 66bool ArgvMap::mustDo(const string &var)
bd11bd1d 67{
12c86877
BH
68 return ((*this)[var]!="no") && ((*this)[var]!="off");
69}
70
71vector<string>ArgvMap::list()
72{
73 vector<string> ret;
74 for(map<string,string>::const_iterator i=params.begin();i!=params.end();++i)
75 ret.push_back(i->first);
76 return ret;
77}
78
79string ArgvMap::getHelp(const string &item)
80{
81 return helpmap[item];
82}
83
84string & ArgvMap::set(const string &var, const string &help)
85{
86 helpmap[var]=help;
87 d_typeMap[var]="Parameter";
88 return set(var);
89}
90
91void ArgvMap::setCmd(const string &var, const string &help)
92{
93 helpmap[var]=help;
94 d_typeMap[var]="Command";
95 set(var)="no";
96}
97
98string & ArgvMap::setSwitch(const string &var, const string &help)
99{
100 helpmap[var]=help;
101 d_typeMap[var]="Switch";
102 return set(var);
103}
104
105
12c86877
BH
106bool ArgvMap::contains(const string &var, const string &val)
107{
102eb646
BH
108 params_t::const_iterator param = params.find(var);
109 if(param == params.end() || param->second.empty()) {
110 return false;
111 }
562588a3 112 vector<string> parts;
562588a3 113
102eb646 114 stringtok( parts, param->second, ", \t" );
93a471a3
CHB
115 for (const auto& part: parts) {
116 if (part == val) {
562588a3
BH
117 return true;
118 }
119 }
120
121 return false;
12c86877
BH
122}
123
12c86877
BH
124string ArgvMap::helpstring(string prefix)
125{
126 if(prefix=="no")
127 prefix="";
128
129 string help;
93a471a3
CHB
130
131 for (const auto& i: helpmap) {
132 if(!prefix.empty() && i.first.find(prefix) != 0) // only print items with prefix
133 continue;
12c86877
BH
134
135 help+=" --";
93a471a3 136 help+=i.first;
12c86877 137
93a471a3 138 string type=d_typeMap[i.first];
12c86877
BH
139
140 if(type=="Parameter")
4957a608 141 help+="=...";
12c86877 142 else if(type=="Switch")
4957a608 143 {
93a471a3
CHB
144 help+=" | --"+i.first+"=yes";
145 help+=" | --"+i.first+"=no";
4957a608 146 }
12c86877
BH
147
148
149 help+="\n\t";
93a471a3 150 help+=i.second;
12c86877
BH
151 help+="\n";
152
153 }
154 return help;
155}
156
8864bdf6 157string ArgvMap::configstring(bool running, bool full)
12c86877
BH
158{
159 string help;
6bef3d91 160
8864bdf6
KM
161 if (running)
162 help="# Autogenerated configuration file based on running instance ("+nowTime()+")\n\n";
6bef3d91 163 else
8864bdf6
KM
164 help="# Autogenerated configuration file template\n\n";
165
93a471a3
CHB
166 for(const auto& i: helpmap) {
167 if(d_typeMap[i.first]=="Command")
c7efa8ff
RA
168 continue;
169
8864bdf6
KM
170 if (! defaultmap.count(i.first)) {
171 throw ArgException(string("Default for parameter '")+i.first+"' not set");
172 }
173
174 if (!running || full) {
175 help+="#################################\n";
176 help+="# ";
177 help+=i.first;
178 help+="\t";
179 help+=i.second;
180 help+="\n#\n";
181 } else {
182 if (defaultmap[i.first] == params[i.first]) {
183 continue;
184 }
185 }
186
187 if (! running || defaultmap[i.first] == params[i.first]) {
188 help+="# ";
189 }
190
191 if (running) {
192 help+=i.first+"="+params[i.first]+"\n";
193 if (full) {
194 help+="\n";
195 }
c7efa8ff 196 } else {
8864bdf6 197 help+=i.first+"="+defaultmap[i.first]+"\n\n";
12c86877 198 }
c7efa8ff 199 }
12c86877
BH
200 return help;
201}
202
12c86877
BH
203const string & ArgvMap::operator[](const string &arg)
204{
205 if(!parmIsset(arg))
206 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
207
12c86877
BH
208 return params[arg];
209}
210
cc2978dc
BH
211mode_t ArgvMap::asMode(const string &arg)
212{
213 mode_t mode;
214 const char *cptr_orig;
215 char *cptr_ret = NULL;
216
217 if(!parmIsset(arg))
218 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
219
220 cptr_orig = params[arg].c_str();
221 mode = static_cast<mode_t>(strtol(cptr_orig, &cptr_ret, 8));
222 if (mode == 0 && cptr_ret == cptr_orig)
223 throw ArgException("'" + arg + string("' contains invalid octal mode"));
224 return mode;
225}
226
227gid_t ArgvMap::asGid(const string &arg)
228{
229 gid_t gid;
230 const char *cptr_orig;
231 char *cptr_ret = NULL;
232
233 if(!parmIsset(arg))
234 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
235
236 cptr_orig = params[arg].c_str();
237 gid = static_cast<gid_t>(strtol(cptr_orig, &cptr_ret, 0));
238 if (gid == 0 && cptr_ret == cptr_orig) {
239 // try to resolve
240 struct group *group = getgrnam(params[arg].c_str());
241 if (group == NULL)
242 throw ArgException("'" + arg + string("' contains invalid group"));
243 gid = group->gr_gid;
244 }
245 return gid;
246}
247
248uid_t ArgvMap::asUid(const string &arg)
249{
250 uid_t uid;
251 const char *cptr_orig;
252 char *cptr_ret = NULL;
253
254 if(!parmIsset(arg))
255 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
256
257 cptr_orig = params[arg].c_str();
258 uid = static_cast<uid_t>(strtol(cptr_orig, &cptr_ret, 0));
259 if (uid == 0 && cptr_ret == cptr_orig) {
260 // try to resolve
261 struct passwd *pwent = getpwnam(params[arg].c_str());
262 if (pwent == NULL)
263 throw ArgException("'" + arg + string("' contains invalid group"));
264 uid = pwent->pw_uid;
265 }
266 return uid;
267}
cc2978dc 268
ff99a74b 269int ArgvMap::asNum(const string &arg, int def)
12c86877 270{
cc2978dc
BH
271 int retval;
272 const char *cptr_orig;
273 char *cptr_ret = NULL;
274
12c86877
BH
275 if(!parmIsset(arg))
276 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
277
ff99a74b 278 // use default for empty values
cc2978dc 279 if (params[arg].empty())
ff99a74b 280 return def;
cc2978dc
BH
281
282 cptr_orig = params[arg].c_str();
283 retval = static_cast<int>(strtol(cptr_orig, &cptr_ret, 0));
284 if (!retval && cptr_ret == cptr_orig)
fd378ba9 285 throw ArgException("'"+arg+"' value '"+string(cptr_orig) + string( "' is not a valid number"));
cc2978dc
BH
286
287 return retval;
288}
289
290bool ArgvMap::isEmpty(const string &arg)
291{
292 if(!parmIsset(arg))
293 return true;
294 return params[arg].empty();
12c86877
BH
295}
296
297double ArgvMap::asDouble(const string &arg)
298{
cc2978dc
BH
299 double retval;
300 const char *cptr_orig;
301 char *cptr_ret = NULL;
302
12c86877
BH
303 if(!parmIsset(arg))
304 throw ArgException(string("Undefined but needed argument: '")+arg+"'");
305
cc2978dc
BH
306 if (params[arg].empty())
307 return 0.0;
308
309 cptr_orig = params[arg].c_str();
310 retval = strtod(cptr_orig, &cptr_ret);
311
312 if (retval == 0 && cptr_ret == cptr_orig)
313 throw ArgException("'"+arg+string("' is not valid double"));
314
315 return retval;
12c86877
BH
316}
317
318ArgvMap::ArgvMap()
319{
320
321}
322
323bool ArgvMap::parmIsset(const string &var)
324{
325 return (params.find(var)!=params.end());
326}
327
328void ArgvMap::parseOne(const string &arg, const string &parseOnly, bool lax)
329{
330 string var, val;
bdf40704 331 string::size_type pos;
5a177409 332 bool incremental = false;
4d752b05 333
a350fc7a 334 if(arg.find("--") == 0 && (pos=arg.find("+="))!=string::npos) // this is a --port+=25 case
4d752b05
KM
335 {
336 var=arg.substr(2,pos-2);
337 val=arg.substr(pos+2);
338 incremental = true;
339 }
a350fc7a 340 else if(arg.find("--") == 0 && (pos=arg.find("="))!=string::npos) // this is a --port=25 case
4d752b05
KM
341 {
342 var=arg.substr(2,pos-2);
343 val=arg.substr(pos+1);
344 }
a350fc7a 345 else if(arg.find("--") == 0 && (arg.find("=")==string::npos)) // this is a --daemon case
4d752b05
KM
346 {
347 var=arg.substr(2);
348 val="";
349 }
12c86877 350 else if(arg[0]=='-')
4d752b05
KM
351 {
352 var=arg.substr(1);
353 val="";
12c86877 354 }
4d752b05
KM
355 else // command
356 d_cmds.push_back(arg);
c63f0951
AT
357
358 boost::trim(var);
12c86877
BH
359
360 if(var!="" && (parseOnly.empty() || var==parseOnly)) {
bd11bd1d 361 pos=val.find_first_not_of(" \t"); // strip leading whitespace
3752660e 362 if(pos && pos!=string::npos)
bd11bd1d 363 val=val.substr(pos);
4d752b05
KM
364 if(parmIsset(var))
365 {
366 if(incremental)
367 {
368 if(params[var].empty())
369 {
370 if(!d_cleared.count(var))
371 throw ArgException("Incremental parameter '"+var+"' without a parent");
372 params[var]=val;
373 }
374 else
375 params[var]+=", " + val;
376 }
377 else
378 {
379 params[var]=val;
380 d_cleared.insert(var);
5a177409 381 }
3702b428 382 }
4d752b05 383 else if(!lax)
a67dd0cf 384 throw ArgException("Trying to set unknown parameter '"+var+"'");
12c86877
BH
385 }
386}
387
388const vector<string>&ArgvMap::getCommands()
389{
390 return d_cmds;
391}
392
393void ArgvMap::parse(int &argc, char **argv, bool lax)
394{
116cda91 395 d_cmds.clear();
58a57699 396 d_cleared.clear();
12c86877
BH
397 for(int n=1;n<argc;n++) {
398 parseOne(argv[n],"",lax);
399 }
400}
401
402void ArgvMap::preParse(int &argc, char **argv, const string &arg)
403{
404 for(int n=1;n<argc;n++) {
405 string varval=argv[n];
a350fc7a 406 if(varval.find("--"+arg) == 0)
12c86877
BH
407 parseOne(argv[n]);
408 }
409}
410
d3002a04
AT
411bool ArgvMap::parseFile(const char *fname, const string& arg, bool lax) {
412 string line;
07b7e0b5
AT
413 string pline;
414 string::size_type pos;
415
d3002a04
AT
416 ifstream f(fname);
417 if(!f)
418 return false;
07b7e0b5 419
d3002a04
AT
420 while(getline(f,pline)) {
421 trim_right(pline);
422
83281a74 423 if(!pline.empty() && pline[pline.size()-1]=='\\') {
d3002a04
AT
424 line+=pline.substr(0,pline.length()-1);
425 continue;
426 }
427 else
428 line+=pline;
429
430 // strip everything after a #
431 if((pos=line.find("#"))!=string::npos) {
432 // make sure it's either first char or has whitespace before
433 // fixes issue #354
434 if (pos == 0 || std::isspace(line[pos-1]))
435 line=line.substr(0,pos);
436 }
437
438 // strip trailing spaces
439 trim_right(line);
07b7e0b5 440
d3002a04
AT
441 // strip leading spaces
442 if((pos=line.find_first_not_of(" \t\r\n"))!=string::npos)
443 line=line.substr(pos);
07b7e0b5 444
d3002a04
AT
445 // gpgsql-basic-query=sdfsdfs dfsdfsdf sdfsdfsfd
446
447 parseOne( string("--") + line, arg, lax );
448 line="";
449 }
07b7e0b5 450
07b7e0b5
AT
451 return true;
452}
453
454
f64de501 455bool ArgvMap::preParseFile(const char *fname, const string &arg, const string& theDefault)
12c86877 456{
f64de501 457 params[arg]=theDefault;
b56ac6fd 458
d3002a04 459 return parseFile(fname, arg, false);
12c86877
BH
460}
461
12c86877 462bool ArgvMap::file(const char *fname, bool lax)
4f6ef75f
AT
463{
464 return file(fname,lax,false);
465}
466
467bool ArgvMap::file(const char *fname, bool lax, bool included)
12c86877 468{
8a16d778 469 if (!parmIsset("include-dir")) // inject include-dir
4f6ef75f
AT
470 set("include-dir","Directory to include configuration files from");
471
d3002a04 472 if(!parseFile(fname, "", lax)) {
e6a9dde5 473 g_log << Logger::Warning << "Unable to open " << fname << std::endl;
d3002a04
AT
474 return false;
475 }
eefd15f9 476
4f6ef75f 477 // handle include here (avoid re-include)
8a16d778 478 if (!included && !params["include-dir"].empty()) {
93d7e233
AT
479 std::vector<std::string> extraConfigs;
480 gatherIncludes(extraConfigs);
ef7cd021 481 for(const std::string& fn : extraConfigs) {
93d7e233 482 if (!file(fn.c_str(), lax, true)) {
e6a9dde5 483 g_log << Logger::Error << fn << " could not be parsed" << std::endl;
93d7e233 484 throw ArgException(fn + " could not be parsed");
4f6ef75f 485 }
93d7e233 486 }
93d7e233 487 }
8a16d778 488
93d7e233
AT
489 return true;
490}
8a16d778 491
93d7e233
AT
492void ArgvMap::gatherIncludes(std::vector<std::string> &extraConfigs) {
493 extraConfigs.clear();
a73da04b
OM
494 if (params["include-dir"].empty())
495 return; // nothing to do
496
497 DIR *dir;
498 if (!(dir = opendir(params["include-dir"].c_str()))) {
499 int err = errno;
500 string msg = params["include-dir"] + " is not accessible: " + strerror(err);
501 g_log << Logger::Error << msg << std::endl;
502 throw ArgException(msg);
503 }
93d7e233 504
a73da04b
OM
505 struct dirent *ent;
506 while ((ent = readdir(dir)) != NULL) {
507 if (ent->d_name[0] == '.')
508 continue; // skip any dots
509 if (boost::ends_with(ent->d_name, ".conf")) {
510 // build name
511 string name = params["include-dir"] + "/" + ent->d_name; // FIXME: Use some path separator
512 // ensure it's readable file
513 struct stat st;
514 if (stat(name.c_str(), &st) || !S_ISREG(st.st_mode)) {
515 string msg = name + " is not a regular file";
516 g_log << Logger::Error << msg << std::endl;
517 closedir(dir);
518 throw ArgException(msg);
93d7e233 519 }
a73da04b 520 extraConfigs.push_back(name);
93d7e233 521 }
a73da04b
OM
522 }
523 std::sort(extraConfigs.begin(), extraConfigs.end(), CIStringComparePOSIX());
524 closedir(dir);
12c86877 525}