aboutsummaryrefslogtreecommitdiff
path: root/src/ban.cpp
blob: 57b9f49a57aba6f02e33a940cd384d2bbe72fa98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "ban.h"
#include <fstream>
#include "threading/mutex_auto_lock.h"
#include <sstream>
#include <set>
#include "strfnd.h"
#include "util/string.h"
#include "log.h"
#include "filesys.h"

BanManager::BanManager(const std::string &banfilepath):
		m_banfilepath(banfilepath),
		m_modified(false)
{
	try{
		load();
	}
	catch(SerializationError &e)
	{
		warningstream<<"BanManager: creating "
				<<m_banfilepath<<std::endl;
	}
}

BanManager::~BanManager()
{
	save();
}

void BanManager::load()
{
	MutexAutoLock lock(m_mutex);
	infostream<<"BanManager: loading from "<<m_banfilepath<<std::endl;
	std::ifstream is(m_banfilepath.c_str(), std::ios::binary);
	if(is.good() == false)
	{
		infostream<<"BanManager: failed loading from "<<m_banfilepath<<std::endl;
		throw SerializationError("BanManager::load(): Couldn't open file");
	}

	while(!is.eof() && is.good())
	{
		std::string line;
		std::getline(is, line, '\n');
		Strfnd f(line);
		std::string ip = trim(f.next("|"));
		std::string name = trim(f.next("|"));
		if(!ip.empty()) {
			m_ips[ip] = name;
		}
	}
	m_modified = false;
}

void BanManager::save()
{
	MutexAutoLock lock(m_mutex);
	infostream << "BanManager: saving to " << m_banfilepath << std::endl;
	std::ostringstream ss(std::ios_base::binary);

	for (StringMap::iterator it = m_ips.begin(); it != m_ips.end(); ++it)
		ss << it->first << "|" << it->second << "\n";

	if (!fs::safeWriteToFile(m_banfilepath, ss.str())) {
		infostream << "BanManager: failed saving to " << m_banfilepath << std::endl;
		throw SerializationError("BanManager::save(): Couldn't write file");
	}

	m_modified = false;
}

bool BanManager::isIpBanned(const std::string &ip)
{
	MutexAutoLock lock(m_mutex);
	return m_ips.find(ip) != m_ips.end();
}

std::string BanManager::getBanDescription(const std::string &ip_or_name)
{
	MutexAutoLock lock(m_mutex);
	std::string s = "";
	for (StringMap::iterator it = m_ips.begin(); it != m_ips.end(); ++it) {
		if (it->first  == ip_or_name || it->second == ip_or_name
				|| ip_or_name == "") {
			s += it->first + "|" + it->second + ", ";
		}
	}
	s = s.substr(0, s.size() - 2);
	return s;
}

std::string BanManager::getBanName(const std::string &ip)
{
	MutexAutoLock lock(m_mutex);
	StringMap::iterator it = m_ips.find(ip);
	if (it == m_ips.end())
		return "";
	return it->second;
}

void BanManager::add(const std::string &ip, const std::string &name)
{
	MutexAutoLock lock(m_mutex);
	m_ips[ip] = name;
	m_modified = true;
}

void BanManager::remove(const std::string &ip_or_name)
{
	MutexAutoLock lock(m_mutex);
	for (StringMap::iterator it = m_ips.begin(); it != m_ips.end();) {
		if ((it->first == ip_or_name) || (it->second == ip_or_name)) {
			m_ips.erase(it++);
		} else {
			++it;
		}
	}
	m_modified = true;
}


bool BanManager::isModified()
{
	MutexAutoLock lock(m_mutex);
	return m_modified;
}

ckopt(m_handle, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char *>(&value), sizeof(value)); } return true; } UDPSocket::~UDPSocket() { if (socket_enable_debug_output) { dstream << "UDPSocket( " << (int)m_handle << ")::~UDPSocket()" << std::endl; } #ifdef _WIN32 closesocket(m_handle); #else close(m_handle); #endif } void UDPSocket::Bind(Address addr) { if (socket_enable_debug_output) { dstream << "UDPSocket(" << (int)m_handle << ")::Bind(): " << addr.serializeString() << ":" << addr.getPort() << std::endl; } if (addr.getFamily() != m_addr_family) { static const char *errmsg = "Socket and bind address families do not match"; errorstream << "Bind failed: " << errmsg << std::endl; throw SocketException(errmsg); } if (m_addr_family == AF_INET6) { struct sockaddr_in6 address; memset(&address, 0, sizeof(address)); address = addr.getAddress6(); address.sin6_family = AF_INET6; address.sin6_port = htons(addr.getPort()); if (bind(m_handle, (const struct sockaddr *)&address, sizeof(struct sockaddr_in6)) < 0) { dstream << (int)m_handle << ": Bind failed: " << strerror(errno) << std::endl; throw SocketException("Failed to bind socket"); } } else { struct sockaddr_in address; memset(&address, 0, sizeof(address)); address = addr.getAddress(); address.sin_family = AF_INET; address.sin_port = htons(addr.getPort()); if (bind(m_handle, (const struct sockaddr *)&address, sizeof(struct sockaddr_in)) < 0) { dstream << (int)m_handle << ": Bind failed: " << strerror(errno) << std::endl; throw SocketException("Failed to bind socket"); } } } void UDPSocket::Send(const Address &destination, const void *data, int size) { bool dumping_packet = false; // for INTERNET_SIMULATOR if (INTERNET_SIMULATOR) dumping_packet = myrand() % INTERNET_SIMULATOR_PACKET_LOSS == 0; if (socket_enable_debug_output) { // Print packet destination and size dstream << (int)m_handle << " -> "; destination.print(&dstream); dstream << ", size=" << size; // Print packet contents dstream << ", data="; for (int i = 0; i < size && i < 20; i++) { if (i % 2 == 0) dstream << " "; unsigned int a = ((const unsigned char *)data)[i]; dstream << std::hex << std::setw(2) << std::setfill('0') << a; } if (size > 20) dstream << "..."; if (dumping_packet) dstream << " (DUMPED BY INTERNET_SIMULATOR)"; dstream << std::endl; } if (dumping_packet) { // Lol let's forget it dstream << "UDPSocket::Send(): INTERNET_SIMULATOR: dumping packet." << std::endl; return; } if (destination.getFamily() != m_addr_family) throw SendFailedException("Address family mismatch"); int sent; if (m_addr_family == AF_INET6) { struct sockaddr_in6 address = destination.getAddress6(); address.sin6_port = htons(destination.getPort()); sent = sendto(m_handle, (const char *)data, size, 0, (struct sockaddr *)&address, sizeof(struct sockaddr_in6)); } else { struct sockaddr_in address = destination.getAddress(); address.sin_port = htons(destination.getPort()); sent = sendto(m_handle, (const char *)data, size, 0, (struct sockaddr *)&address, sizeof(struct sockaddr_in)); } if (sent != size) throw SendFailedException("Failed to send packet"); } int UDPSocket::Receive(Address &sender, void *data, int size) { // Return on timeout if (!WaitData(m_timeout_ms)) return -1; int received; if (m_addr_family == AF_INET6) { struct sockaddr_in6 address; memset(&address, 0, sizeof(address)); socklen_t address_len = sizeof(address); received = recvfrom(m_handle, (char *)data, size, 0, (struct sockaddr *)&address, &address_len); if (received < 0) return -1; u16 address_port = ntohs(address.sin6_port); IPv6AddressBytes bytes; memcpy(bytes.bytes, address.sin6_addr.s6_addr, 16); sender = Address(&bytes, address_port); } else { struct sockaddr_in address; memset(&address, 0, sizeof(address)); socklen_t address_len = sizeof(address); received = recvfrom(m_handle, (char *)data, size, 0, (struct sockaddr *)&address, &address_len); if (received < 0) return -1; u32 address_ip = ntohl(address.sin_addr.s_addr); u16 address_port = ntohs(address.sin_port); sender = Address(address_ip, address_port); } if (socket_enable_debug_output) { // Print packet sender and size dstream << (int)m_handle << " <- "; sender.print(&dstream); dstream << ", size=" << received; // Print packet contents dstream << ", data="; for (int i = 0; i < received && i < 20; i++) { if (i % 2 == 0) dstream << " "; unsigned int a = ((const unsigned char *)data)[i]; dstream << std::hex << std::setw(2) << std::setfill('0') << a; } if (received > 20) dstream << "..."; dstream << std::endl; } return received; } int UDPSocket::GetHandle() { return m_handle; } void UDPSocket::setTimeoutMs(int timeout_ms) { m_timeout_ms = timeout_ms; } bool UDPSocket::WaitData(int timeout_ms) { fd_set readset; int result; // Initialize the set FD_ZERO(&readset); FD_SET(m_handle, &readset); // Initialize time out struct struct timeval tv; tv.tv_sec = 0; tv.tv_usec = timeout_ms * 1000; // select() result = select(m_handle + 1, &readset, NULL, NULL, &tv); if (result == 0) return false; if (result < 0 && (errno == EINTR || errno == EBADF)) { // N.B. select() fails when sockets are destroyed on Connection's dtor // with EBADF. Instead of doing tricky synchronization, allow this // thread to exit but don't throw an exception. return false; } if (result < 0) { dstream << m_handle << ": Select failed: " << strerror(errno) << std::endl; #ifdef _WIN32 int e = WSAGetLastError(); dstream << (int)m_handle << ": WSAGetLastError()=" << e << std::endl; if (e == 10004 /* WSAEINTR */ || e == 10009 /* WSAEBADF */) { infostream << "Ignoring WSAEINTR/WSAEBADF." << std::endl; return false; } #endif throw SocketException("Select failed"); } else if (!FD_ISSET(m_handle, &readset)) { // No data return false; } // There is data return true; }