1 ////////////////////////////////////////////////////////////////////////////////
2 //    Scorched3D (c) 2000-2011
3 //
4 //    This file is part of Scorched3D.
5 //
6 //    Scorched3D is free software; you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation; either version 2 of the License, or
9 //    (at your option) any later version.
10 //
11 //    Scorched3D is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License along
17 //    with this program; if not, write to the Free Software Foundation, Inc.,
18 //    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20 
21 #include <server/ServerBanned.h>
22 #include <server/ScorchedServer.h>
23 #include <common/OptionsScorched.h>
24 #include <common/Defines.h>
25 #include <common/Logger.h>
26 #include <net/NetInterface.h>
27 #include <XML/XMLFile.h>
28 #include <limits.h>
29 
ServerBanned()30 ServerBanned::ServerBanned() : lastReadTime_(0)
31 {
32 }
33 
~ServerBanned()34 ServerBanned::~ServerBanned()
35 {
36 }
37 
load(bool force)38 bool ServerBanned::load(bool force)
39 {
40 	std::string filename =
41 		S3D::getSettingsFile(S3D::formatStringBuffer("banned-%i.xml",
42 			ScorchedServer::instance()->getOptionsGame().getPortNo()));
43 	if (!S3D::fileExists(filename)) return true;
44 
45 	time_t fileTime = S3D::fileModTime(filename);
46 	if (!force && fileTime == lastReadTime_) return true;
47 
48 	XMLFile file;
49 	if (!file.readFile(filename))
50 	{
51 		Logger::log(S3D::formatStringBuffer("Failed to parse banned file \"%s\"\n%s",
52 			filename.c_str(), file.getParserError()));
53 		return false;
54 	}
55 
56 	Logger::log(S3D::formatStringBuffer("Refreshing banned list %s", filename.c_str()));
57 	lastReadTime_ = fileTime;
58 	bannedIps_.clear();
59 	bannedIds_.clear();
60 	bannedSUIs_.clear();
61 	if (!file.getRootNode()) return true; // Empty File
62 
63 	std::list<XMLNode *>::iterator childrenItor;
64 	std::list<XMLNode *> &children = file.getRootNode()->getChildren();
65 	for (childrenItor = children.begin();
66 		 childrenItor != children.end();
67 		++childrenItor)
68 	{
69 		XMLNode *currentNode = (*childrenItor);
70 		XMLNode *maskNode = 0, *timeNode = 0, *typeNode = 0;
71 
72 		// Read the mask
73 		unsigned int m = UINT_MAX;
74 		if (currentNode->getNamedParameter("mask", maskNode, false))
75 		{
76 			unsigned int mask[4];
77 			if (sscanf(maskNode->getContent(), "%u.%u.%u.%u",
78 				&mask[3], &mask[2], &mask[1], &mask[0]) != 4)
79 			{
80 				S3D::dialogMessage("ServerBanned",
81 					S3D::formatStringBuffer("Failed to parse mask %s",
82 					maskNode->getContent()));
83 				return false;
84 			}
85 			m = mask[3] << 24 | mask[2] << 16 | mask[1] << 8 | mask[0];
86 		}
87 
88 		LangString name;
89 		std::string adminname, uniqueid, reason, SUI;
90 		currentNode->getNamedParameter("name", name, false);
91 		currentNode->getNamedParameter("adminname", adminname, false);
92 		currentNode->getNamedParameter("reason", reason, false);
93 		currentNode->getNamedParameter("id", uniqueid, false);
94 		currentNode->getNamedParameter("SUI", SUI, false);
95 
96 		// Time
97 		time_t bantime = 0;
98 		if (currentNode->getNamedParameter("time", timeNode, false))
99 		{
100 			sscanf(timeNode->getContent(), "%u", &bantime);
101 		}
102 
103 		// Type
104 		BannedType type = Banned;
105 		if (currentNode->getNamedParameter("type", typeNode, false))
106 		{
107 			if (0 == strcmp("banned", typeNode->getContent())) type = Banned;
108 			else if (0 == strcmp("muted", typeNode->getContent())) type = Muted;
109 			else if (0 == strcmp("flagged", typeNode->getContent())) type = Flagged;
110 			else
111 			{
112 				S3D::dialogMessage("ServerBanned",
113 					S3D::formatStringBuffer("Failed to parse banned type %s",
114 					typeNode->getContent()));
115 				return false;
116 			}
117 		}
118 
119 		// Read the ip address
120 		unsigned int address[4];
121 		if (sscanf(currentNode->getContent(), "%u.%u.%u.%u",
122 			&address[3], &address[2], &address[1], &address[0]) != 4)
123 		{
124 			S3D::dialogMessage("ServerBanned",
125 				S3D::formatStringBuffer("Failed to parse ip address %s",
126 				currentNode->getContent()));
127 			return false;
128 		}
129 
130 		unsigned int ip = 0;
131 		ip = address[3] << 24 | address[2] << 16 | address[1] << 8 | address[0];
132 
133 		// Add the new entry
134 		addBannedEntry(ip, m, name, uniqueid.c_str(), SUI.c_str(),
135 			(unsigned int) bantime, type,
136 			adminname.c_str(), reason.c_str());
137 	}
138 	return true;
139 }
140 
getBannedIps()141 std::list<ServerBanned::BannedRange> &ServerBanned::getBannedIps()
142 {
143 	load();
144 	return bannedIps_;
145 }
146 
getBanned(const char * unqiueid,const char * SUI)147 ServerBanned::BannedType ServerBanned::getBanned(
148 	const char *unqiueid, const char *SUI)
149 {
150 	load();
151 
152 	// Check if the unique id has been banned
153 	std::map<std::string, BannedEntry>::iterator findItor =
154 		bannedIds_.find(unqiueid);
155 	if (findItor != bannedIds_.end())
156 	{
157 		BannedEntry &entry = (*findItor).second;
158 		return entry.type;
159 	}
160 
161 	// Check if the SUI has been banned
162 	std::map<std::string, BannedEntry>::iterator findItor2 =
163 		bannedSUIs_.find(SUI);
164 	if (findItor2 != bannedSUIs_.end())
165 	{
166 		BannedEntry &entry = (*findItor2).second;
167 		return entry.type;
168 	}
169 	return NotBanned;
170 }
171 
getBanned(unsigned int ip)172 ServerBanned::BannedType ServerBanned::getBanned(
173 	unsigned int ip)
174 {
175 	load();
176 
177 	// Check if the ip address has been banned
178 	std::list<BannedRange>::iterator itor;
179 	for (itor = bannedIps_.begin();
180 		itor != bannedIps_.end();
181 		++itor)
182 	{
183 		BannedRange &range = *itor;
184 		unsigned int newip = range.mask & ip;
185 		std::map<unsigned int, BannedEntry>::iterator findItor =
186 			range.ips.find(newip);
187 		if (findItor != range.ips.end())
188 		{
189 			BannedEntry &entry = (*findItor).second;
190 			return entry.type;
191 		}
192 	}
193 	return NotBanned;
194 }
195 
addBanned(unsigned int ip,const LangString & name,const char * uniqueId,const char * SUI,BannedType type,const char * adminname,const char * reason)196 void ServerBanned::addBanned(unsigned int ip, const LangString &name,
197 	const char *uniqueId, const char *SUI, BannedType type,
198 	const char *adminname, const char *reason)
199 {
200 	unsigned int t = (unsigned int) time(0);
201 	addBannedEntry(ip, UINT_MAX, name, uniqueId, SUI, t, type, adminname, reason);
202 	save();
203 }
204 
addBannedEntry(unsigned int ip,unsigned int mask,const LangString & name,const char * uniqueId,const char * SUid,unsigned int bantime,BannedType type,const char * adminname,const char * reason)205 void ServerBanned::addBannedEntry(unsigned int ip, unsigned int mask,
206 	const LangString &name, const char *uniqueId, const char *SUid, unsigned int bantime, BannedType type,
207 	const char *adminname, const char *reason)
208 {
209 	unsigned int newip = mask & ip;
210 	BannedEntry newEntry;
211 	newEntry.name = name;
212 	newEntry.bantime = bantime;
213 	newEntry.type = type;
214 	newEntry.uniqueid = uniqueId;
215 	newEntry.SUI = SUid;
216 	newEntry.adminname = adminname;
217 	newEntry.reason = reason;
218 
219 	BannedRange *found = 0;
220 	std::list<BannedRange>::iterator itor;
221 	for (itor = bannedIps_.begin();
222 		itor != bannedIps_.end();
223 		++itor)
224 	{
225 		BannedRange &range = *itor;
226 		if (range.mask == mask)
227 		{
228 			found = &range;
229 			break;
230 		}
231 	}
232 	if (!found)
233 	{
234 		BannedRange range;
235 		range.mask = mask;
236 		bannedIps_.push_back(range);
237 		found = &bannedIps_.back();
238 	}
239 
240 	// Add ip to list of banned ips
241 	found->ips[newip] = newEntry;
242 
243 	// Add id to list of banned ids
244 	if (uniqueId[0])
245 	{
246 		std::map<std::string, BannedEntry>::iterator findItor =
247 			bannedIds_.find(uniqueId);
248 		if (findItor == bannedIds_.end())
249 		{
250 			bannedIds_[uniqueId] = newEntry;
251 		}
252 	}
253 
254  	// Add SUI to list of banned SUIs
255  	if (SUid[0])
256  	{
257  		std::map<std::string, BannedEntry>::iterator findItor2 =
258 			bannedSUIs_.find(SUid);
259  		if (findItor2 == bannedSUIs_.end())
260  		{
261  			bannedSUIs_[SUid] = newEntry;
262  		}
263  	}
264 }
265 
getBannedTypeStr(BannedType type)266 const char *ServerBanned::getBannedTypeStr(BannedType type)
267 {
268 	const char *str = "error";
269 	switch (type)
270 	{
271 		case Muted:
272 			str = "muted";
273 			break;
274 		case Banned:
275 			str = "banned";
276 			break;
277 		case NotBanned:
278 			str = "notbanned";
279 			break;
280 		case Flagged:
281 			str = "flagged";
282 			break;
283 	}
284 	return str;
285 }
286 
save()287 bool ServerBanned::save()
288 {
289 	std::string filename =
290 		S3D::getSettingsFile(S3D::formatStringBuffer("banned-%i.xml",
291 			ScorchedServer::instance()->getOptionsGame().getPortNo()));
292 
293 	XMLNode node("bannednodes");
294 	std::list<BannedRange>::iterator itor;
295 	for (itor = bannedIps_.begin();
296 		itor != bannedIps_.end();
297 		++itor)
298 	{
299 		BannedRange &range = *itor;
300 		unsigned int m = range.mask;
301 		std::map<unsigned int, BannedEntry>::iterator ipitor;
302 		for (ipitor = range.ips.begin();
303 			ipitor != range.ips.end();
304 			++ipitor)
305 		{
306 			// Add ip address
307 			unsigned int ip = (*ipitor).first;
308 			BannedEntry &entry = (*ipitor).second;
309 			XMLNode *optionNode =
310 				new XMLNode("ipaddress", NetInterface::getIpName(ip));
311 
312 			// Add the mask
313 			if (m != UINT_MAX)
314 			{
315 				optionNode->addParameter(new XMLNode("mask", NetInterface::getIpName(m),
316 						XMLNode::XMLParameterType));
317 			}
318 			optionNode->addParameter(new XMLNode("name",
319 					entry.name,
320 					XMLNode::XMLParameterType));
321 			optionNode->addParameter(new XMLNode("time",
322 					S3D::formatStringBuffer("%u", (unsigned int) entry.bantime),
323 					XMLNode::XMLParameterType));
324 			optionNode->addParameter(new XMLNode("type",
325 					getBannedTypeStr(entry.type),
326 					XMLNode::XMLParameterType));
327 			optionNode->addParameter(new XMLNode("id",
328 					entry.uniqueid,
329 					XMLNode::XMLParameterType));
330 			optionNode->addParameter(new XMLNode("SUI",
331  					entry.SUI,
332  					XMLNode::XMLParameterType));
333 			optionNode->addParameter(new XMLNode("adminname",
334 					entry.adminname,
335 					XMLNode::XMLParameterType));
336 			optionNode->addParameter(new XMLNode("reason",
337 					entry.reason,
338 					XMLNode::XMLParameterType));
339 
340 			// Add to file
341 			if (entry.type != NotBanned)
342 			{
343 				node.addChild(optionNode);
344 			}
345 		}
346 	}
347 	return node.writeToFile(filename);
348 }
349 
350