anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

misc.cpp (17220B)

      1 /* Miscellaneous routines.
      2  *
      3  * (C) 2003-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  *
      8  * Based on the original code of Epona by Lara.
      9  * Based on the original code of Services by Andy Church.
     10  */
     11 
     12 #include "services.h"
     13 #include "build.h"
     14 #include "modules.h"
     15 #include "lists.h"
     16 #include "config.h"
     17 #include "bots.h"
     18 #include "language.h"
     19 #include "regexpr.h"
     20 #include "sockets.h"
     21 
     22 #include <errno.h>
     23 #include <sys/types.h>
     24 #include <sys/stat.h>
     25 #ifndef _WIN32
     26 #include <sys/socket.h>
     27 #include <netdb.h>
     28 #endif
     29 
     30 NumberList::NumberList(const Anope::string &list, bool descending) : is_valid(true), desc(descending)
     31 {
     32 	Anope::string error;
     33 	commasepstream sep(list);
     34 	Anope::string token;
     35 
     36 	sep.GetToken(token);
     37 	if (token.empty())
     38 		token = list;
     39 	do
     40 	{
     41 		size_t t = token.find('-');
     42 
     43 		if (t == Anope::string::npos)
     44 		{
     45 			try
     46 			{
     47 				unsigned num = convertTo<unsigned>(token, error, false);
     48 				if (error.empty())
     49 					numbers.insert(num);
     50 			}
     51 			catch (const ConvertException &)
     52 			{
     53 				error = "1";
     54 			}
     55 
     56 			if (!error.empty())
     57 			{
     58 				if (!this->InvalidRange(list))
     59 				{
     60 					is_valid = false;
     61 					return;
     62 				}
     63 			}
     64 		}
     65 		else
     66 		{
     67 			Anope::string error2;
     68 			try
     69 			{
     70 				unsigned num1 = convertTo<unsigned>(token.substr(0, t), error, false);
     71 				unsigned num2 = convertTo<unsigned>(token.substr(t + 1), error2, false);
     72 				if (error.empty() && error2.empty())
     73 					for (unsigned i = num1; i <= num2; ++i)
     74 						numbers.insert(i);
     75 			}
     76 			catch (const ConvertException &)
     77 			{
     78 				error = "1";
     79 			}
     80 
     81 			if (!error.empty() || !error2.empty())
     82 			{
     83 				if (!this->InvalidRange(list))
     84 				{
     85 					is_valid = false;
     86 					return;
     87 				}
     88 			}
     89 		}
     90 	} while (sep.GetToken(token));
     91 }
     92 
     93 NumberList::~NumberList()
     94 {
     95 }
     96 
     97 void NumberList::Process()
     98 {
     99 	if (!is_valid)
    100 		return;
    101 
    102 	if (this->desc)
    103 	{
    104 		for (std::set<unsigned>::reverse_iterator it = numbers.rbegin(), it_end = numbers.rend(); it != it_end; ++it)
    105 			this->HandleNumber(*it);
    106 	}
    107 	else
    108 	{
    109 		for (std::set<unsigned>::iterator it = numbers.begin(), it_end = numbers.end(); it != it_end; ++it)
    110 			this->HandleNumber(*it);
    111 	}
    112 }
    113 
    114 void NumberList::HandleNumber(unsigned)
    115 {
    116 }
    117 
    118 bool NumberList::InvalidRange(const Anope::string &)
    119 {
    120 	return true;
    121 }
    122 
    123 ListFormatter::ListFormatter(NickCore *acc) : nc(acc)
    124 {
    125 }
    126 
    127 ListFormatter &ListFormatter::AddColumn(const Anope::string &name)
    128 {
    129 	this->columns.push_back(name);
    130 	return *this;
    131 }
    132 
    133 void ListFormatter::AddEntry(const ListEntry &entry)
    134 {
    135 	this->entries.push_back(entry);
    136 }
    137 
    138 bool ListFormatter::IsEmpty() const
    139 {
    140 	return this->entries.empty();
    141 }
    142 
    143 void ListFormatter::Process(std::vector<Anope::string> &buffer)
    144 {
    145 	std::vector<Anope::string> tcolumns;
    146 	std::map<Anope::string, size_t> lengths;
    147 	std::set<Anope::string> breaks;
    148 	for (unsigned i = 0; i < this->columns.size(); ++i)
    149 	{
    150 		tcolumns.push_back(Language::Translate(this->nc, this->columns[i].c_str()));
    151 		lengths[this->columns[i]] = tcolumns[i].length();
    152 	}
    153 	for (unsigned i = 0; i < this->entries.size(); ++i)
    154 	{
    155 		ListEntry &e = this->entries[i];
    156 		for (unsigned j = 0; j < this->columns.size(); ++j)
    157 			if (e[this->columns[j]].length() > lengths[this->columns[j]])
    158 				lengths[this->columns[j]] = e[this->columns[j]].length();
    159 	}
    160 	unsigned length = 0;
    161 	for (std::map<Anope::string, size_t>::iterator it = lengths.begin(), it_end = lengths.end(); it != it_end; ++it)
    162 	{
    163 		/* Break lines at 80 chars */
    164 		if (length > 80)
    165 		{
    166 			breaks.insert(it->first);
    167 			length = 0;
    168 		}
    169 		else
    170 			length += it->second;
    171 	}
    172 
    173 	/* Only put a list header if more than 1 column */
    174 	if (this->columns.size() > 1)
    175 	{
    176 		Anope::string s;
    177 		for (unsigned i = 0; i < this->columns.size(); ++i)
    178 		{
    179 			if (breaks.count(this->columns[i]))
    180 			{
    181 				buffer.push_back(s);
    182 				s = "  ";
    183 			}
    184 			else if (!s.empty())
    185 				s += "  ";
    186 			s += tcolumns[i];
    187 			if (i + 1 != this->columns.size())
    188 				for (unsigned j = tcolumns[i].length(); j < lengths[this->columns[i]]; ++j)
    189 					s += " ";
    190 		}
    191 		buffer.push_back(s);
    192 	}
    193 
    194 	for (unsigned i = 0; i < this->entries.size(); ++i)
    195 	{
    196 		ListEntry &e = this->entries[i];
    197 
    198 		Anope::string s;
    199 		for (unsigned j = 0; j < this->columns.size(); ++j)
    200 		{
    201 			if (breaks.count(this->columns[j]))
    202 			{
    203 				buffer.push_back(s);
    204 				s = "  ";
    205 			}
    206 			else if (!s.empty())
    207 				s += "  ";
    208 			s += e[this->columns[j]];
    209 			if (j + 1 != this->columns.size())
    210 				for (unsigned k = e[this->columns[j]].length(); k < lengths[this->columns[j]]; ++k)
    211 					s += " ";
    212 		}
    213 		buffer.push_back(s);
    214 	}
    215 }
    216 
    217 InfoFormatter::InfoFormatter(NickCore *acc) : nc(acc), longest(0)
    218 {
    219 }
    220 
    221 void InfoFormatter::Process(std::vector<Anope::string> &buffer)
    222 {
    223 	buffer.clear();
    224 
    225 	for (std::vector<std::pair<Anope::string, Anope::string> >::iterator it = this->replies.begin(), it_end = this->replies.end(); it != it_end; ++it)
    226 	{
    227 		Anope::string s;
    228 		for (unsigned i = it->first.length(); i < this->longest; ++i)
    229 			s += " ";
    230 		s += it->first + ": " + Language::Translate(this->nc, it->second.c_str());
    231 
    232 		buffer.push_back(s);
    233 	}
    234 }
    235 
    236 Anope::string& InfoFormatter::operator[](const Anope::string &key)
    237 {
    238 	Anope::string tkey = Language::Translate(this->nc, key.c_str());
    239 	if (tkey.length() > this->longest)
    240 		this->longest = tkey.length();
    241 	this->replies.push_back(std::make_pair(tkey, ""));
    242 	return this->replies.back().second;
    243 }
    244 
    245 void InfoFormatter::AddOption(const Anope::string &opt)
    246 {
    247 	Anope::string options = Language::Translate(this->nc, "Options");
    248 	Anope::string *optstr = NULL;
    249 	for (std::vector<std::pair<Anope::string, Anope::string> >::iterator it = this->replies.begin(), it_end = this->replies.end(); it != it_end; ++it)
    250 	{
    251 		if (it->first == options)
    252 		{
    253 			optstr = &it->second;
    254 			break;
    255 		}
    256 	}
    257 	if (!optstr)
    258 		optstr = &(*this)[_("Options")];
    259 
    260 	if (!optstr->empty())
    261 		*optstr += ", ";
    262 	*optstr += Language::Translate(nc, opt.c_str());
    263 }
    264 
    265 bool Anope::IsFile(const Anope::string &filename)
    266 {
    267 	struct stat fileinfo;
    268 	if (!stat(filename.c_str(), &fileinfo))
    269 		return true;
    270 
    271 	return false;
    272 }
    273 
    274 time_t Anope::DoTime(const Anope::string &s)
    275 {
    276 	if (s.empty())
    277 		return 0;
    278 
    279 	int amount = 0;
    280 	Anope::string end;
    281 
    282 	try
    283 	{
    284 		amount = convertTo<int>(s, end, false);
    285 		if (!end.empty())
    286 		{
    287 			switch (end[0])
    288 			{
    289 				case 's':
    290 					return amount;
    291 				case 'm':
    292 					return amount * 60;
    293 				case 'h':
    294 					return amount * 3600;
    295 				case 'd':
    296 					return amount * 86400;
    297 				case 'w':
    298 					return amount * 86400 * 7;
    299 				case 'y':
    300 					return amount * 86400 * 365;
    301 				default:
    302 					break;
    303 			}
    304 		}
    305 	}
    306 	catch (const ConvertException &)
    307 	{
    308 		amount = -1;
    309 	}
    310 
    311 	return amount;
    312 }
    313 
    314 Anope::string Anope::Duration(time_t t, const NickCore *nc)
    315 {
    316 	/* We first calculate everything */
    317 	time_t years = t / 31536000;
    318 	time_t days = (t / 86400) % 365;
    319 	time_t hours = (t / 3600) % 24;
    320 	time_t minutes = (t / 60) % 60;
    321 	time_t seconds = (t) % 60;
    322 
    323 	if (!years && !days && !hours && !minutes)
    324 		return stringify(seconds) + " " + (seconds != 1 ? Language::Translate(nc, _("seconds")) : Language::Translate(nc, _("second")));
    325 	else
    326 	{
    327 		bool need_comma = false;
    328 		Anope::string buffer;
    329 		if (years)
    330 		{
    331 			buffer = stringify(years) + " " + (years != 1 ? Language::Translate(nc, _("years")) : Language::Translate(nc, _("year")));
    332 			need_comma = true;
    333 		}
    334 		if (days)
    335 		{
    336 			buffer += need_comma ? ", " : "";
    337 			buffer += stringify(days) + " " + (days != 1 ? Language::Translate(nc, _("days")) : Language::Translate(nc, _("day")));
    338 			need_comma = true;
    339 		}
    340 		if (hours)
    341 		{
    342 			buffer += need_comma ? ", " : "";
    343 			buffer += stringify(hours) + " " + (hours != 1 ? Language::Translate(nc, _("hours")) : Language::Translate(nc, _("hour")));
    344 			need_comma = true;
    345 		}
    346 		if (minutes)
    347 		{
    348 			buffer += need_comma ? ", " : "";
    349 			buffer += stringify(minutes) + " " + (minutes != 1 ? Language::Translate(nc, _("minutes")) : Language::Translate(nc, _("minute")));
    350 		}
    351 		return buffer;
    352 	}
    353 }
    354 
    355 Anope::string Anope::strftime(time_t t, const NickCore *nc, bool short_output)
    356 {
    357 	tm tm = *localtime(&t);
    358 	char buf[BUFSIZE];
    359 	strftime(buf, sizeof(buf), Language::Translate(nc, _("%b %d %H:%M:%S %Y %Z")), &tm);
    360 	if (short_output)
    361 		return buf;
    362 	if (t < Anope::CurTime)
    363 		return Anope::string(buf) + " " + Anope::printf(Language::Translate(nc, _("(%s ago)")), Duration(Anope::CurTime - t, nc).c_str(), nc);
    364 	else if (t > Anope::CurTime)
    365 		return Anope::string(buf) + " " + Anope::printf(Language::Translate(nc, _("(%s from now)")), Duration(t - Anope::CurTime, nc).c_str(), nc);
    366 	else
    367 		return Anope::string(buf) + " " + Language::Translate(nc, _("(now)"));
    368 }
    369 
    370 Anope::string Anope::Expires(time_t expires, const NickCore *nc)
    371 {
    372 	if (!expires)
    373 		return Language::Translate(nc, NO_EXPIRE);
    374 	else if (expires <= Anope::CurTime)
    375 		return Language::Translate(nc, _("expires momentarily"));
    376 	else
    377 	{
    378 		char buf[256];
    379 		time_t diff = expires - Anope::CurTime + 59;
    380 
    381 		if (diff >= 86400)
    382 		{
    383 			int days = diff / 86400;
    384 			snprintf(buf, sizeof(buf), Language::Translate(nc, days == 1 ? _("expires in %d day") : _("expires in %d days")), days);
    385 		}
    386 		else
    387 		{
    388 			if (diff <= 3600)
    389 			{
    390 				int minutes = diff / 60;
    391 				snprintf(buf, sizeof(buf), Language::Translate(nc, minutes == 1 ? _("expires in %d minute") : _("expires in %d minutes")), minutes);
    392 			}
    393 			else
    394 			{
    395 				int hours = diff / 3600, minutes;
    396 				diff -= hours * 3600;
    397 				minutes = diff / 60;
    398 				snprintf(buf, sizeof(buf), Language::Translate(nc, hours == 1 && minutes == 1 ? _("expires in %d hour, %d minute") : (hours == 1 && minutes != 1 ? _("expires in %d hour, %d minutes") : (hours != 1 && minutes == 1 ? _("expires in %d hours, %d minute") : _("expires in %d hours, %d minutes")))), hours, minutes);
    399 			}
    400 		}
    401 
    402 		return buf;
    403 	}
    404 }
    405 
    406 bool Anope::Match(const Anope::string &str, const Anope::string &mask, bool case_sensitive, bool use_regex)
    407 {
    408 	size_t s = 0, m = 0, str_len = str.length(), mask_len = mask.length();
    409 
    410 	if (use_regex && mask_len >= 2 && mask[0] == '/' && mask[mask.length() - 1] == '/')
    411 	{
    412 		Anope::string stripped_mask = mask.substr(1, mask_len - 2);
    413 		// This is often called with the same mask multiple times in a row, so cache it
    414 		static Regex *r = NULL;
    415 
    416 		if (r == NULL || r->GetExpression() != stripped_mask)
    417 		{
    418 			ServiceReference<RegexProvider> provider("Regex", Config->GetBlock("options")->Get<const Anope::string>("regexengine"));
    419 			if (provider)
    420 			{
    421 				try
    422 				{
    423 					delete r;
    424 					r = NULL;
    425 					// This may throw
    426 					r = provider->Compile(stripped_mask);
    427 				}
    428 				catch (const RegexException &ex)
    429 				{
    430 					Log(LOG_DEBUG) << ex.GetReason();
    431 				}
    432 			}
    433 			else
    434 			{
    435 				delete r;
    436 				r = NULL;
    437 			}
    438 		}
    439 
    440 		if (r != NULL && r->Matches(str))
    441 			return true;
    442 
    443 		// Fall through to non regex match
    444 	}
    445 
    446 	while (s < str_len && m < mask_len && mask[m] != '*')
    447 	{
    448 		char string = str[s], wild = mask[m];
    449 		if (case_sensitive)
    450 		{
    451 			if (wild != string && wild != '?')
    452 				return false;
    453 		}
    454 		else
    455 		{
    456 			if (Anope::tolower(wild) != Anope::tolower(string) && wild != '?')
    457 				return false;
    458 		}
    459 
    460 		++m;
    461 		++s;
    462 	}
    463 
    464 	size_t sp = Anope::string::npos, mp = Anope::string::npos;
    465 	while (s < str_len)
    466 	{
    467 		char string = str[s], wild = mask[m];
    468 		if (wild == '*')
    469 		{
    470 			if (++m == mask_len)
    471 				return 1;
    472 
    473 			mp = m;
    474 			sp = s + 1;
    475 		}
    476 		else if (case_sensitive)
    477 		{
    478 			if (wild == string || wild == '?')
    479 			{
    480 				++m;
    481 				++s;
    482 			}
    483 			else
    484 			{
    485 				m = mp;
    486 				s = sp++;
    487 			}
    488 		}
    489 		else
    490 		{
    491 			if (Anope::tolower(wild) == Anope::tolower(string) || wild == '?')
    492 			{
    493 				++m;
    494 				++s;
    495 			}
    496 			else
    497 			{
    498 				m = mp;
    499 				s = sp++;
    500 			}
    501 		}
    502 	}
    503 
    504 	if (m < mask_len && mask[m] == '*')
    505 		++m;
    506 
    507 	return m == mask_len;
    508 }
    509 
    510 void Anope::Encrypt(const Anope::string &src, Anope::string &dest)
    511 {
    512 	EventReturn MOD_RESULT;
    513 	FOREACH_RESULT(OnEncrypt, MOD_RESULT, (src, dest));
    514 	static_cast<void>(MOD_RESULT);
    515 }
    516 
    517 bool Anope::Decrypt(const Anope::string &src, Anope::string &dest)
    518 {
    519 	size_t pos = src.find(':');
    520 	if (pos == Anope::string::npos)
    521 	{
    522 		Log() << "Error: Anope::Decrypt() called with invalid password string (" << src << ")";
    523 		return false;
    524 	}
    525 	Anope::string hashm(src.begin(), src.begin() + pos);
    526 
    527 	EventReturn MOD_RESULT;
    528 	FOREACH_RESULT(OnDecrypt, MOD_RESULT, (hashm, src, dest));
    529 	if (MOD_RESULT == EVENT_ALLOW)
    530 		return true;
    531 
    532 	return false;
    533 }
    534 
    535 Anope::string Anope::printf(const char *fmt, ...)
    536 {
    537 	va_list args;
    538 	char buf[1024];
    539 	va_start(args, fmt);
    540 	vsnprintf(buf, sizeof(buf), fmt, args);
    541 	va_end(args);
    542 	return buf;
    543 }
    544 
    545 Anope::string Anope::Hex(const Anope::string &data)
    546 {
    547 	const char hextable[] = "0123456789abcdef";
    548 
    549 	size_t l = data.length();
    550 	Anope::string rv;
    551 	for (size_t i = 0; i < l; ++i)
    552 	{
    553 		unsigned char c = data[i];
    554 		rv += hextable[c >> 4];
    555 		rv += hextable[c & 0xF];
    556 	}
    557 	return rv;
    558 }
    559 
    560 Anope::string Anope::Hex(const char *data, unsigned len)
    561 {
    562 	const char hextable[] = "0123456789abcdef";
    563 
    564 	Anope::string rv;
    565 	for (size_t i = 0; i < len; ++i)
    566 	{
    567 		unsigned char c = data[i];
    568 		rv += hextable[c >> 4];
    569 		rv += hextable[c & 0xF];
    570 	}
    571 	return rv;
    572 }
    573 
    574 void Anope::Unhex(const Anope::string &src, Anope::string &dest)
    575 {
    576 	size_t len = src.length();
    577 	Anope::string rv;
    578 	for (size_t i = 0; i + 1 < len; i += 2)
    579 	{
    580 		char h = Anope::tolower(src[i]), l = Anope::tolower(src[i + 1]);
    581 		unsigned char byte = (h >= 'a' ? h - 'a' + 10 : h - '0') << 4;
    582 		byte += (l >= 'a' ? l - 'a' + 10 : l - '0');
    583 		rv += byte;
    584 	}
    585 	dest = rv;
    586 }
    587 
    588 void Anope::Unhex(const Anope::string &src, char *dest, size_t sz)
    589 {
    590 	Anope::string d;
    591 	Anope::Unhex(src, d);
    592 
    593 	memcpy(dest, d.c_str(), std::min(d.length() + 1, sz));
    594 }
    595 
    596 int Anope::LastErrorCode()
    597 {
    598 #ifndef _WIN32
    599 	return errno;
    600 #else
    601 	return GetLastError();
    602 #endif
    603 }
    604 
    605 const Anope::string Anope::LastError()
    606 {
    607 #ifndef _WIN32
    608 	return strerror(errno);
    609 #else
    610 	char errbuf[513];
    611 	DWORD err = GetLastError();
    612 	if (!err)
    613 		return "";
    614 	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, errbuf, 512, NULL);
    615 	return errbuf;
    616 #endif
    617 }
    618 
    619 Anope::string Anope::Version()
    620 {
    621 #ifdef VERSION_GIT
    622 	return stringify(VERSION_MAJOR) + "." + stringify(VERSION_MINOR) + "." + stringify(VERSION_PATCH) + VERSION_EXTRA + " (" + VERSION_GIT + ")";
    623 #else
    624 	return stringify(VERSION_MAJOR) + "." + stringify(VERSION_MINOR) + "." + stringify(VERSION_PATCH) + VERSION_EXTRA;
    625 #endif
    626 }
    627 
    628 Anope::string Anope::VersionShort()
    629 {
    630 	return stringify(VERSION_MAJOR) + "." + stringify(VERSION_MINOR) + "." + stringify(VERSION_PATCH);
    631 }
    632 
    633 Anope::string Anope::VersionBuildString()
    634 {
    635 #ifdef REPRODUCIBLE_BUILD
    636 	Anope::string s = "build #" + stringify(BUILD);
    637 #else
    638 	Anope::string s = "build #" + stringify(BUILD) + ", compiled " + Anope::compiled;
    639 #endif
    640 	Anope::string flags;
    641 
    642 #ifdef DEBUG_BUILD
    643 	flags += "D";
    644 #endif
    645 #ifdef VERSION_GIT
    646 	flags += "G";
    647 #endif
    648 #ifdef _WIN32
    649 	flags += "W";
    650 #endif
    651 
    652 	if (!flags.empty())
    653 		s += ", flags " + flags;
    654 
    655 	return s;
    656 }
    657 
    658 int Anope::VersionMajor() { return VERSION_MAJOR; }
    659 int Anope::VersionMinor() { return VERSION_MINOR; }
    660 int Anope::VersionPatch() { return VERSION_PATCH; }
    661 
    662 Anope::string Anope::NormalizeBuffer(const Anope::string &buf)
    663 {
    664 	Anope::string newbuf;
    665 
    666 	for (unsigned i = 0, end = buf.length(); i < end; ++i)
    667 	{
    668 		switch (buf[i])
    669 		{
    670 			/* ctrl char */
    671 			case 1:
    672 			/* Bold ctrl char */
    673 			case 2:
    674 				break;
    675 			/* Color ctrl char */
    676 			case 3:
    677 				/* If the next character is a digit, its also removed */
    678 				if (isdigit(buf[i + 1]))
    679 				{
    680 					++i;
    681 
    682 					/* not the best way to remove colors
    683 					 * which are two digit but no worse then
    684 					 * how the Unreal does with +S - TSL
    685 					 */
    686 					if (isdigit(buf[i + 1]))
    687 						++i;
    688 
    689 					/* Check for background color code
    690 					 * and remove it as well
    691 					 */
    692 					if (buf[i + 1] == ',')
    693 					{
    694 						++i;
    695 
    696 						if (isdigit(buf[i + 1]))
    697 							++i;
    698 						/* not the best way to remove colors
    699 						 * which are two digit but no worse then
    700 						 * how the Unreal does with +S - TSL
    701 						 */
    702 						if (isdigit(buf[i + 1]))
    703 							++i;
    704 					}
    705 				}
    706 
    707 				break;
    708 			/* line feed char */
    709 			case 10:
    710 			/* carriage returns char */
    711 			case 13:
    712 			/* Reverse ctrl char */
    713 			case 22:
    714 			/* Italic ctrl char */
    715 			case 29:
    716 			/* Underline ctrl char */
    717 			case 31:
    718 				break;
    719 			/* A valid char gets copied into the new buffer */
    720 			default:
    721 				newbuf += buf[i];
    722 		}
    723 	}
    724 
    725 	return newbuf;
    726 }
    727 
    728 Anope::string Anope::Resolve(const Anope::string &host, int type)
    729 {
    730 	Anope::string result = host;
    731 
    732 	addrinfo hints;
    733 	memset(&hints, 0, sizeof(hints));
    734 	hints.ai_family = type;
    735 
    736 	Log(LOG_DEBUG_2) << "Resolver: BlockingQuery: Looking up " << host;
    737 
    738 	addrinfo *addrresult = NULL;
    739 	if (getaddrinfo(host.c_str(), NULL, &hints, &addrresult) == 0)
    740 	{
    741 		sockaddrs addr;
    742 		memcpy(static_cast<void*>(&addr), addrresult->ai_addr, addrresult->ai_addrlen);
    743 		result = addr.addr();
    744 		Log(LOG_DEBUG_2) << "Resolver: " << host << " -> " << result;
    745 
    746 		freeaddrinfo(addrresult);
    747 	}
    748 
    749 	return result;
    750 }
    751 
    752 Anope::string Anope::Random(size_t len)
    753 {
    754 	char chars[] = {
    755 		'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
    756 		'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
    757 		'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
    758 		'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
    759 		'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
    760 	};
    761 	Anope::string buf;
    762 	for (size_t i = 0; i < len; ++i)
    763 		buf.append(chars[rand() % sizeof(chars)]);
    764 	return buf;
    765 }