00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "config.hpp"
00017 #include "foreach.hpp"
00018 #include "log.hpp"
00019 #include "filesystem.hpp"
00020 #include "serialization/parser.hpp"
00021 #include "serialization/binary_or_text.hpp"
00022 #include "serialization/string_utils.hpp"
00023 #include "util.hpp"
00024
00025 #include "ban.hpp"
00026
00027
00028 #include <boost/bind.hpp>
00029
00030 namespace wesnothd {
00031
00032
00033 static lg::log_domain log_server("server");
00034 #define ERR_SERVER LOG_STREAM(err, log_server)
00035 #define LOG_SERVER LOG_STREAM(info, log_server)
00036 #define DBG_SERVER LOG_STREAM(debug, log_server)
00037
00038 std::ostream& operator<<(std::ostream& o, const banned& n)
00039 {
00040 return o << "IP: " << n.get_ip() <<
00041 (n.get_nick().empty() ? "" : " nick: " + n.get_nick()) <<
00042 " reason: '" << n.get_reason() << "'"
00043 " start_time: " << n.get_human_start_time() <<
00044 " end_time: " << n.get_human_end_time() <<
00045 " issuer: " << n.get_who_banned();
00046 }
00047
00048 bool banned_compare::operator()(const banned_ptr& a, const banned_ptr& b) const
00049 {
00050 return (*a) > (*b);
00051 }
00052
00053 banned_compare_subnet::compare_fn banned_compare_subnet::active_ = &banned_compare_subnet::less;
00054
00055 bool banned_compare_subnet::operator()(const banned_ptr& a, const banned_ptr& b) const
00056 {
00057 return (this->*(active_))(a,b);
00058 }
00059
00060 bool banned_compare_subnet::less(const banned_ptr& a, const banned_ptr& b) const
00061 {
00062 return a->get_int_ip() < b->get_int_ip();
00063 }
00064
00065 const std::string banned::who_banned_default_ = "system";
00066
00067 banned_ptr banned::create_dummy(const std::string& ip)
00068 {
00069 banned_ptr dummy(new banned(ip));
00070 return dummy;
00071 }
00072
00073 banned::banned(const std::string& ip) :
00074 ip_(0),
00075 mask_(0),
00076 ip_text_(),
00077 end_time_(0),
00078 start_time_(0),
00079 reason_(),
00080 who_banned_(who_banned_default_),
00081 group_(),
00082 nick_()
00083 {
00084 ip_mask pair = parse_ip(ip);
00085 ip_ = pair.first;
00086 mask_ = 0xFFFFFFFF;
00087 }
00088
00089 banned::banned(const std::string& ip,
00090 const time_t end_time,
00091 const std::string& reason,
00092 const std::string& who_banned,
00093 const std::string& group,
00094 const std::string& nick) :
00095 ip_(0),
00096 mask_(0),
00097 ip_text_(ip),
00098 end_time_(end_time),
00099 start_time_(time(0)),
00100 reason_(reason),
00101 who_banned_(who_banned),
00102 group_(group),
00103 nick_(nick)
00104 {
00105 ip_mask pair = parse_ip(ip_text_);
00106 ip_ = pair.first;
00107 mask_ = pair.second;
00108 }
00109
00110 banned::banned(const config& cfg) :
00111 ip_(0),
00112 mask_(0),
00113 ip_text_(),
00114 end_time_(0),
00115 start_time_(0),
00116 reason_(),
00117 who_banned_(who_banned_default_),
00118 group_(),
00119 nick_()
00120 {
00121 read(cfg);
00122 }
00123
00124 ip_mask parse_ip(const std::string& ip)
00125 {
00126
00127
00128 ip_mask ret;
00129 ret.first = 0;
00130 ret.second = 0;
00131 std::vector<std::string> split_ip = utils::split(ip, '.');
00132 if (split_ip.size() > 4) throw banned::error("Malformed ip address: " + ip);
00133
00134 unsigned int shift = 4*8;
00135 unsigned int mask = 0xFF000000;
00136 const unsigned int complete_part_mask = 0xFF;
00137 std::vector<std::string>::const_iterator part = split_ip.begin();
00138 bool wildcard = false;
00139 do {
00140 shift -= 8;
00141 mask >>= 8;
00142 if (part == split_ip.end())
00143 {
00144 if (!wildcard)
00145 throw banned::error("Malformed ip address: '" + ip + "'");
00146
00147
00148 break;
00149 } else {
00150 if (*part == "*")
00151 {
00152 wildcard = true;
00153
00154 } else {
00155
00156 unsigned int part_ip = lexical_cast_default<unsigned int>(*part, complete_part_mask + 1);
00157 if (part_ip > complete_part_mask)
00158 throw banned::error("Malformed ip address: '" + ip + "'");
00159 ret.first |= (part_ip << shift);
00160 ret.second |= (complete_part_mask << shift);
00161 }
00162 }
00163 ++part;
00164 } while (shift);
00165 return ret;
00166 }
00167
00168 void banned::read(const config& cfg)
00169 {
00170 {
00171
00172 ip_text_ = cfg["ip"].str();
00173 ip_mask pair = parse_ip(ip_text_);
00174 ip_ = pair.first;
00175 mask_ = pair.second;
00176 }
00177 nick_ = cfg["nick"].str();
00178 if (cfg.has_attribute("end_time"))
00179 end_time_ = lexical_cast_default<time_t>(cfg["end_time"], 0);
00180 if (cfg.has_attribute("start_time"))
00181 start_time_ = lexical_cast_default<time_t>(cfg["start_time"], 0);
00182 reason_ = cfg["reason"].str();
00183
00184
00185 if (cfg.has_attribute("who_banned"))
00186 who_banned_ = cfg["who_banned"].str();
00187 if (cfg.has_attribute("group"))
00188 group_ = cfg["group"].str();
00189 }
00190
00191 void banned::write(config& cfg) const
00192 {
00193 cfg["ip"] = get_ip();
00194 cfg["nick"] = get_nick();
00195 if (end_time_ > 0)
00196 {
00197 std::stringstream ss;
00198 ss << end_time_;
00199 cfg["end_time"] = ss.str();
00200 }
00201 if (start_time_ > 0)
00202 {
00203 std::stringstream ss;
00204 ss << start_time_;
00205 cfg["start_time"] = ss.str();
00206 }
00207
00208 cfg["reason"] = reason_;
00209 if (who_banned_ != who_banned_default_)
00210 {
00211 cfg["who_banned"] = who_banned_;
00212 }
00213 if (!group_.empty())
00214 {
00215 cfg["group"] = group_;
00216 }
00217 }
00218
00219 std::string banned::get_human_start_time() const
00220 {
00221 if (start_time_ == 0)
00222 return "unknown";
00223 return lg::get_timestamp(start_time_, "%H:%M:%S %d.%m.%Y");
00224 }
00225
00226 std::string banned::get_human_end_time() const
00227 {
00228 if (end_time_ == 0)
00229 {
00230 return "permanent";
00231 }
00232 return lg::get_timestamp(end_time_, "%H:%M:%S %d.%m.%Y");
00233 }
00234
00235 bool banned::operator>(const banned& b) const
00236 {
00237 return end_time_ > b.get_end_time();
00238 }
00239
00240 unsigned int banned::get_mask_ip(unsigned int mask) const
00241 {
00242 return ip_ & mask & mask_;
00243 }
00244
00245 bool banned::match_ip(const ip_mask& pair) const {
00246 return (ip_ & mask_) == (pair.first & mask_);
00247 }
00248
00249
00250 bool banned::match_ipmask(const ip_mask& pair) const {
00251 return (ip_ & mask_ & pair.second) == (pair.first & pair.second & mask_);
00252 }
00253
00254 void ban_manager::read()
00255 {
00256 if (filename_.empty() || !file_exists(filename_))
00257 return;
00258 LOG_SERVER << "Reading bans from " << filename_ << "\n";
00259 config cfg;
00260 scoped_istream ban_file = istream_file(filename_);
00261 read_gz(cfg, *ban_file);
00262
00263 foreach (const config &b, cfg.child_range("ban"))
00264 {
00265 try {
00266 banned_ptr new_ban(new banned(b));
00267 assert(bans_.insert(new_ban).second);
00268
00269 if (new_ban->get_end_time() != 0)
00270 time_queue_.push(new_ban);
00271 } catch (banned::error& e) {
00272 ERR_SERVER << e.message << " while reading bans\n";
00273 }
00274 }
00275
00276
00277 if (const config &cfg_del = cfg.child("deleted"))
00278 {
00279 foreach (const config &b, cfg_del.child_range("ban"))
00280 {
00281 try {
00282 banned_ptr new_ban(new banned(b));
00283 deleted_bans_.push_back(new_ban);
00284 } catch (banned::error& e) {
00285 ERR_SERVER << e.message << " while reading deleted bans\n";
00286 }
00287 }
00288 }
00289
00290
00291 }
00292
00293 void ban_manager::write()
00294 {
00295 if (filename_.empty() || !dirty_)
00296 return;
00297 LOG_SERVER << "Writing bans to " << filename_ << "\n";
00298 dirty_ = false;
00299 config cfg;
00300 for (ban_set::const_iterator itor = bans_.begin();
00301 itor != bans_.end(); ++itor)
00302 {
00303 config& child = cfg.add_child("ban");
00304 (*itor)->write(child);
00305 }
00306 config& deleted = cfg.add_child("deleted");
00307 for (deleted_ban_list::const_iterator itor = deleted_bans_.begin();
00308 itor != deleted_bans_.end(); ++itor)
00309 {
00310 config& child = deleted.add_child("ban");
00311 (*itor)->write(child);
00312 }
00313
00314 scoped_ostream ban_file = ostream_file(filename_);
00315 config_writer writer(*ban_file, true);
00316 writer.write(cfg);
00317 }
00318
00319 bool ban_manager::parse_time(const std::string& duration, time_t* time) const
00320 {
00321 if (!time) return false;
00322
00323 if (duration.substr(0,4) == "TIME") {
00324 struct tm* loc;
00325 loc = localtime(time);
00326
00327 size_t number = 0;
00328 for (std::string::const_iterator i = duration.begin() + 4;
00329 i != duration.end(); ++i) {
00330 if (is_digit(*i))
00331 {
00332 number = number * 10 + to_digit(*i);
00333 }
00334 else
00335 {
00336 switch(*i)
00337 {
00338 case 'Y':
00339 loc->tm_year = number;
00340 break;
00341 case 'M':
00342 loc->tm_mon = number;
00343 break;
00344 case 'D':
00345 loc->tm_mday = number;
00346 break;
00347 case 'h':
00348 loc->tm_hour = number;
00349 break;
00350 case 'm':
00351 loc->tm_min = number;
00352 break;
00353 case 's':
00354 loc->tm_sec = number;
00355 break;
00356 default:
00357 LOG_SERVER << "Invalid time modifier given: '" << *i << "'.\n";
00358 break;
00359 }
00360 number = 0;
00361 }
00362 }
00363 *time = mktime(loc);
00364 return true;
00365 }
00366 default_ban_times::const_iterator time_itor = ban_times_.find(duration);
00367 if (utils::lowercase(duration) == "permanent" || duration == "0") {
00368 *time = 0;
00369 } else if (ban_times_.find(duration) != ban_times_.end()) {
00370 *time += time_itor->second;
00371 } else {
00372 std::string::const_iterator i = duration.begin();
00373 int number = -1;
00374 for (std::string::const_iterator d_end = duration.end(); i != d_end; ++i) {
00375 if (is_digit(*i))
00376 {
00377 if (number == -1) number = 0;
00378 number = number * 10 + to_digit(*i);
00379 } else {
00380 if (number == -1) number = 1;
00381 switch(*i)
00382 {
00383 case 'Y':
00384 case 'y':
00385 if (++i != d_end && tolower(*i) == 'e'
00386 && ++i != d_end && tolower(*i) == 'a'
00387 && ++i != d_end && tolower(*i) == 'r'
00388 && ++i != d_end && tolower(*i) == 's') {
00389 } else --i;
00390 *time += number * 365*24*60*60;
00391 break;
00392 case 'M':
00393 if (++i != d_end && tolower(*i) == 'i') {
00394 if (++i != d_end && tolower(*i) == 'n'
00395 && ++i != d_end && tolower(*i) == 'u'
00396 && ++i != d_end && tolower(*i) == 't'
00397 && ++i != d_end && tolower(*i) == 'e'
00398 && ++i != d_end && tolower(*i) == 's') {
00399 } else --i;
00400 *time += number * 60;
00401 break;
00402 }
00403 --i;
00404 if (++i != d_end && tolower(*i) == 'o'
00405 && ++i != d_end && tolower(*i) == 'n'
00406 && ++i != d_end && tolower(*i) == 't'
00407 && ++i != d_end && tolower(*i) == 'h'
00408 && ++i != d_end && tolower(*i) == 's') {
00409 } else --i;
00410 *time += number * 30*24*60*60;
00411 break;
00412 case 'D':
00413 case 'd':
00414 if (++i != d_end && tolower(*i) == 'a'
00415 && ++i != d_end && tolower(*i) == 'y'
00416 && ++i != d_end && tolower(*i) == 's') {
00417 } else --i;
00418 *time += number * 24*60*60;
00419 break;
00420 case 'H':
00421 case 'h':
00422 if (++i != d_end && tolower(*i) == 'o'
00423 && ++i != d_end && tolower(*i) == 'u'
00424 && ++i != d_end && tolower(*i) == 'r'
00425 && ++i != d_end && tolower(*i) == 's') {
00426 } else --i;
00427 *time += number * 60*60;
00428 break;
00429 case 'm':
00430 if (++i != d_end && tolower(*i) == 'o') {
00431 if (++i != d_end && tolower(*i) == 'n'
00432 && ++i != d_end && tolower(*i) == 't'
00433 && ++i != d_end && tolower(*i) == 'h'
00434 && ++i != d_end && tolower(*i) == 's') {
00435 } else --i;
00436 *time += number * 30*24*60*60;
00437 break;
00438 }
00439 --i;
00440 if (++i != d_end && tolower(*i) == 'i'
00441 && ++i != d_end && tolower(*i) == 'n'
00442 && ++i != d_end && tolower(*i) == 'u'
00443 && ++i != d_end && tolower(*i) == 't'
00444 && ++i != d_end && tolower(*i) == 'e'
00445 && ++i != d_end && tolower(*i) == 's') {
00446 } else --i;
00447 *time += number * 60;
00448 break;
00449 case 'S':
00450 case 's':
00451 if (++i != d_end && tolower(*i) == 'e'
00452 && ++i != d_end && tolower(*i) == 'c'
00453 && ++i != d_end && tolower(*i) == 'o'
00454 && ++i != d_end && tolower(*i) == 'n'
00455 && ++i != d_end && tolower(*i) == 'd'
00456 && ++i != d_end && tolower(*i) == 's') {
00457 } else --i;
00458 *time += number;
00459 break;
00460 default:
00461 return false;
00462 break;
00463 }
00464 number = -1;
00465 }
00466 }
00467 if (is_digit(*--i)) {
00468 *time += number * 60;
00469 }
00470 }
00471 return true;
00472 }
00473
00474 std::string ban_manager::ban(const std::string& ip,
00475 const time_t& end_time,
00476 const std::string& reason,
00477 const std::string& who_banned,
00478 const std::string& group,
00479 const std::string& nick)
00480 {
00481 std::ostringstream ret;
00482 try {
00483 ban_set::iterator ban;
00484 if ((ban = bans_.find(banned::create_dummy(ip))) != bans_.end())
00485 {
00486
00487 ret << "Overwriting ban: " << (**ban) << "\n";
00488 bans_.erase(ban);
00489 }
00490 } catch (banned::error& e) {
00491 ERR_SERVER << e.message << " while creating dummy ban for finding existing ban\n";
00492 return e.message;
00493 }
00494 try {
00495 banned_ptr new_ban(new banned(ip, end_time, reason,who_banned, group, nick));
00496 bans_.insert(new_ban);
00497 if (end_time != 0)
00498 time_queue_.push(new_ban);
00499 ret << *new_ban;
00500 } catch (banned::error& e) {
00501 ERR_SERVER << e.message << " while banning\n";
00502 return e.message;
00503 }
00504 dirty_ = true;
00505 return ret.str();
00506 }
00507
00508 void ban_manager::unban(std::ostringstream& os, const std::string& ip)
00509 {
00510 ban_set::iterator ban;
00511 try {
00512 ban = bans_.find(banned::create_dummy(ip));
00513 } catch (banned::error& e) {
00514 ERR_SERVER << e.message << "\n";
00515 os << e.message;
00516 return;
00517 }
00518
00519 if (ban == bans_.end())
00520 {
00521 os << "There is no ban on '" << ip << "'.";
00522 return;
00523 }
00524
00525 os << "Ban on '" << **ban << "' removed.";
00526
00527 if ((*ban)->get_group().empty()) deleted_bans_.push_back(*ban);
00528 bans_.erase(ban);
00529 dirty_ = true;
00530
00531 }
00532
00533 void ban_manager::unban_group(std::ostringstream& os, const std::string& group)
00534 {
00535 ban_set temp;
00536 std::insert_iterator<ban_set> temp_inserter(temp, temp.begin());
00537 std::remove_copy_if(bans_.begin(), bans_.end(), temp_inserter, boost::bind(&banned::match_group,boost::bind(&banned_ptr::get,_1),group));
00538
00539 os << "Removed " << (bans_.size() - temp.size()) << " bans";
00540 bans_.swap(temp);
00541 dirty_ = true;
00542 }
00543
00544 void ban_manager::check_ban_times(time_t time_now)
00545 {
00546 while (!time_queue_.empty())
00547 {
00548 banned_ptr ban = time_queue_.top();
00549
00550 if (ban->get_end_time() > time_now)
00551 {
00552
00553 DBG_SERVER << "ban " << ban->get_ip() << " not removed. time: " << time_now << " end_time " << ban->get_end_time() << "\n";
00554 break;
00555 }
00556
00557
00558 LOG_SERVER << "Remove a ban " << ban->get_ip() << ". time: " << time_now << " end_time " << ban->get_end_time() << "\n";
00559 std::ostringstream os;
00560 unban(os, ban->get_ip());
00561 time_queue_.pop();
00562
00563 }
00564
00565 write();
00566 }
00567
00568 void ban_manager::list_deleted_bans(std::ostringstream& out, const std::string& mask) const
00569 {
00570 if (deleted_bans_.empty())
00571 {
00572 out << "No removed bans found.";
00573 return;
00574 }
00575
00576 ip_mask pair;
00577 try {
00578 pair = parse_ip(mask);
00579 } catch (banned::error& e) {
00580 out << "parse error: " << e.message;
00581 return;
00582 }
00583
00584 out << "DELETED BANS LIST";
00585 for (deleted_ban_list::const_iterator i = deleted_bans_.begin();
00586 i != deleted_bans_.end();
00587 ++i)
00588 {
00589 if ((*i)->match_ipmask(pair)) out << "\n" << (**i);
00590 }
00591
00592 }
00593
00594
00595
00596 void ban_manager::list_bans(std::ostringstream& out, const std::string& mask) const
00597 {
00598 if (bans_.empty())
00599 {
00600 out << "No bans set.";
00601 return;
00602 }
00603
00604 ip_mask pair;
00605 try {
00606 pair = parse_ip(mask);
00607 } catch (banned::error& e) {
00608 out << "parse error: " << e.message;
00609 return;
00610 }
00611
00612 out << "BAN LIST";
00613 std::set<std::string> groups;
00614
00615 for (ban_set::const_iterator i = bans_.begin();
00616 i != bans_.end(); ++i)
00617 {
00618 if ((*i)->get_group().empty())
00619 {
00620 if ((*i)->match_ipmask(pair)) out << "\n" << (**i);
00621 } else {
00622 groups.insert((*i)->get_group());
00623 }
00624 }
00625
00626
00627 if (!groups.empty() && mask == "*")
00628 {
00629 out << "\nban groups: ";
00630
00631 out << *groups.begin();
00632 std::ostream& (*fn)(std::ostream&,const std::string&) = &std::operator<<;
00633 std::for_each( ++groups.begin(), groups.end(), boost::bind(fn,boost::bind(fn,boost::ref(out),std::string(", ")),_1));
00634 }
00635
00636 }
00637
00638
00639 std::string ban_manager::is_ip_banned(const std::string& ip) const
00640 {
00641 ip_mask pair;
00642 try {
00643 pair = parse_ip(ip);
00644 } catch (banned::error&) {
00645 return "";
00646 }
00647 ban_set::const_iterator ban = std::find_if(bans_.begin(), bans_.end(), boost::bind(&banned::match_ip, boost::bind(&banned_ptr::get, _1), pair));
00648 if (ban == bans_.end()) return "";
00649 const std::string& nick = (*ban)->get_nick();
00650 return (*ban)->get_reason() + (nick.empty() ? "" : " (" + nick + ")");
00651 }
00652
00653 void ban_manager::init_ban_help()
00654 {
00655 ban_help_ = "ban <mask> <time> <reason>\n"
00656 "The time format is: %d[%s[%d[%s[...]]]] where %s is a time"
00657 " modifier: s or S (seconds), m (minutes), h or H (hours), d"
00658 " or D (days), M (months) or y or Y (years) and %d is a number.\n"
00659 "Permanent bans can be set with 'permanent' or '0' as the time"
00660 " argument.\n";
00661 default_ban_times::iterator itor = ban_times_.begin();
00662 if (itor != ban_times_.end())
00663 {
00664 ban_help_ += "You can also use " + itor->first;
00665 ++itor;
00666 }
00667 for (; itor != ban_times_.end(); ++itor)
00668 {
00669 ban_help_ += std::string(", ") + itor->first;
00670 }
00671 if (!ban_times_.empty())
00672 {
00673 ban_help_ += " for standard ban times. (not combinable)\n";
00674 }
00675 ban_help_ += "ban 127.0.0.1 2h20m flooded lobby\n"
00676 "kban suokko 5D flooded again\n"
00677 "kban suokko Y One year ban for constant flooding";
00678 }
00679
00680 void ban_manager::load_config(const config& cfg)
00681 {
00682 ban_times_.clear();
00683 foreach (const config &bt, cfg.child_range("ban_time")) {
00684 time_t duration = 0;
00685 if (parse_time(bt["time"], &duration)) {
00686 ban_times_.insert(default_ban_times::value_type(bt["name"], duration));
00687 }
00688 }
00689 init_ban_help();
00690 if (filename_ != cfg["ban_save_file"])
00691 {
00692 dirty_ = true;
00693 filename_ = cfg["ban_save_file"].str();
00694 }
00695 }
00696
00697 ban_manager::~ban_manager()
00698 {
00699 write();
00700 }
00701
00702 ban_manager::ban_manager()
00703 : bans_()
00704 , deleted_bans_()
00705 , time_queue_()
00706 , ban_times_()
00707 , ban_help_()
00708 , filename_()
00709 , dirty_(false)
00710 {
00711 init_ban_help();
00712 }
00713
00714
00715 }