Merge pull request #154 from polaris-/kyle95wm-master

New ban system by kyle95wm/SMTDDR.
This commit is contained in:
AdmiralCurtiss 2015-05-04 14:32:51 +02:00
commit 12f6172562
3 changed files with 115 additions and 107 deletions

View File

@ -81,8 +81,8 @@ class AdminPage(resource.Resource):
'</head>'
'<body>'
'<p>'
'<a href="/banhammer">Blacklist</a> '
'<a href="/whitelist">Whitelist</a> '
'<a href="/banhammer">All Users</a> | '
'<a href="/banlist">Active Bans</a> '
'</p>'
)
return s
@ -114,56 +114,57 @@ class AdminPage(resource.Resource):
request.write(error_message)
return is_auth
def update_whitelist(self, request):
def update_banlist(self, request):
address = request.getClientIP()
dbconn = sqlite3.connect('gpcm.db')
userid = request.args['userid'][0].strip()
gameid = request.args['gameid'][0].upper().strip()
macadr = request.args['macadr'][0].strip()
actiontype = request.args['actiontype'][0]
if not userid.isdigit() or not gameid.isalnum() or not macadr.isalnum():
ipaddr = request.args['ipaddr'][0].strip()
actiontype = request.args['action'][0]
if not gameid.isalnum():
request.setResponseCode(500)
logger.log(logging.INFO,address+" Bad data "+userid+" "+gameid+" "+macadr)
logger.log(logging.INFO,address+" Bad data "+gameid+" "+ipaddr)
return "Bad data"
if actiontype == 'add':
dbconn.cursor().execute('insert into whitelist values(?,?,?)',(userid,gameid,macadr))
responsedata = "Added macadr=%s for gameid=%s, userid=%s" % (macadr,gameid,userid)
# this strips the region identifier from game IDs, not sure if this actually always accurate but limited testing suggests it is
if len(gameid) > 3:
gameid = gameid[:-1]
if actiontype == 'ban':
dbconn.cursor().execute('insert into banned values(?,?)',(gameid,ipaddr))
responsedata = "Added gameid=%s, ipaddr=%s" % (gameid,ipaddr)
else:
dbconn.cursor().execute('delete from whitelist where userid=? and gameid=? and macadr=?',(userid,gameid,macadr))
responsedata = "Removed macadr=%s for gameid=%s, userid=%s" % (macadr,gameid,userid)
dbconn.cursor().execute('delete from banned where gameid=? and ipaddr=?',(gameid,ipaddr))
responsedata = "Removed gameid=%s, ipaddr=%s" % (gameid,ipaddr)
dbconn.commit()
dbconn.close()
logger.log(logging.INFO,address+" "+responsedata)
request.setHeader("Content-Type", "text/html; charset=utf-8")
request.setHeader("Location", "/whitelist")
referer = request.getHeader('referer')
if not referer:
referer = "/banhammer"
request.setHeader("Location", referer)
request.setResponseCode(303)
return responsedata
def render_whitelist(self, request):
def render_banlist(self, request):
address = request.getClientIP()
dbconn = sqlite3.connect('gpcm.db')
logger.log(logging.INFO,address+" Viewed whitelist")
logger.log(logging.INFO,address+" Viewed banlist")
responsedata = (""
'<a href="http://%20:%20@'+request.getHeader('host')+'">[CLICK HERE TO LOG OUT]</a>'
"<form action='updatewhitelist' method='POST'>"
"userid:<input type='text' name='userid'>\r\n"
"gameid:<input type='text' name='gameid'>\r\n"
"macadr:<input type='text' name='macadr'>\r\n"
"<input type='hidden' name='actiontype' value='add'>\r\n"
"<input type='submit' value='Add to whitelist'></form>\r\n"
"<table border='1'>"
"<tr><td>userid</td><td>gameid</td><td>macadr</td></tr>\r\n")
for row in dbconn.cursor().execute("select * from whitelist"):
userid = str(row[0])
gameid = str(row[1])
macadr = str(row[2])
responsedata += ("<tr><td>"+userid+"</td><td>"+gameid+"</td><td>"+macadr+"</td>"
"<td><form action='updatewhitelist' method='POST'>"
"<input type='hidden' name='userid' value='"+userid+"'>"
"<tr><td>gameid</td><td>ipAddr</td></tr>\r\n")
for row in dbconn.cursor().execute("select * from banned"):
gameid = str(row[0])
ipaddr = str(row[1])
responsedata += ("<tr><td>"+gameid+"</td><td>"+ipaddr+"</td>"
"<td><form action='updatebanlist' method='POST'>"
"<input type='hidden' name='gameid' value='"+gameid+"'>"
"<input type='hidden' name='macadr' value='"+macadr+"'>"
"<input type='hidden' name='actiontype' value='remove'>\r\n"
"<input type='submit' value='Remove from whitelist'></form></td></tr>\r\n")
"<input type='hidden' name='ipaddr' value='"+ipaddr+"'>"
"<input type='hidden' name='action' value='unban'>\r\n"
"<input type='submit' value='----- UNBAN -----'></form></td></tr>\r\n")
responsedata += "</table>"
dbconn.close()
request.setHeader("Content-Type", "text/html; charset=utf-8")
@ -189,13 +190,16 @@ class AdminPage(resource.Resource):
'order by users.gameid '
'')
dbconn = sqlite3.connect('gpcm.db')
banned_list = []
for row in dbconn.cursor().execute("SELECT * FROM BANNED"):
banned_list.append(str(row[0])+":"+str(row[1]))
responsedata = (""
'<a href="http://%20:%20@'+request.getHeader('host')+'">[CLICK HERE TO LOG OUT]</a>'
"<br><br>"
"<table border='1'>"
"<tr><td>ingamesn or devname</td><td>gameid</td>"
"<td>Enabled</td><td>newest dwc_pid</td>"
"<td>gsbrcd</td><td>userid</td><td>IP</td></tr>\r\n")
"<td>gsbrcd</td><td>userid</td><td>ipAddr</td></tr>\r\n")
for row in dbconn.cursor().execute(sqlstatement):
dwc_pid = str(row[0])
enabled = str(row[1])
@ -204,7 +208,7 @@ class AdminPage(resource.Resource):
is_console = int(str(row[4]))
userid = str(row[5])
gsbrcd = str(nasdata['gsbrcd'])
ipaddr = str (nasdata['ipaddr'])
ipaddr = str(nasdata['ipaddr'])
ingamesn = ''
if 'ingamesn' in nasdata:
ingamesn = str(nasdata['ingamesn'])
@ -226,20 +230,18 @@ class AdminPage(resource.Resource):
responsedata += "<td>"+gsbrcd+"</td>"
responsedata += "<td>"+userid+"</td>"
responsedata += "<td>"+ipaddr+"</td>"
if enabled == "1":
responsedata += ("<td><form action='disableuser' method='POST'>"
"<input type='hidden' name='userid' value='"+userid+"'>"
if gameid[:-1]+":"+ipaddr in banned_list:
responsedata += ("<td><form action='updatebanlist' method='POST'>"
"<input type='hidden' name='gameid' value='"+gameid+"'>"
"<input type='hidden' name='ingamesn' value='"+ingamesn+"'>"
"<input type='hidden' name='ipaddr' value='"+ipaddr+"'>"
"<input type='submit' value='Ban'></form></td></tr>")
else:
responsedata += ("<td><form action='enableuser' method='POST'>"
"<input type='hidden' name='userid' value='"+userid+"'>"
"<input type='hidden' name='gameid' value='"+gameid+"'>"
"<input type='hidden' name='ingamesn' value='"+ingamesn+"'>"
"<input type='hidden' name='ipaddr' value='"+ipaddr+"'>"
"<input type='hidden' name='action' value='unban'>"
"<input type='submit' value='----- unban -----'></form></td></tr>")
else:
responsedata += ("<td><form action='updatebanlist' method='POST'>"
"<input type='hidden' name='gameid' value='"+gameid+"'>"
"<input type='hidden' name='ipaddr' value='"+ipaddr+"'>"
"<input type='hidden' name='action' value='ban'>"
"<input type='submit' value='Ban'></form></td></tr>")
responsedata += "</table>"
dbconn.close()
request.setHeader("Content-Type", "text/html; charset=utf-8")
@ -284,11 +286,11 @@ class AdminPage(resource.Resource):
title = None
response = ''
if request.path == "/whitelist":
title = 'AltWfc Whitelist'
response = self.render_whitelist(request)
if request.path == "/banlist":
title = 'AltWfc Banned Users'
response = self.render_banlist(request)
elif request.path == "/banhammer":
title = 'AltWfc Blacklist'
title = 'AltWfc Users'
response = self.render_blacklist(request)
return self.get_header(title) + response + self.get_footer()
@ -300,12 +302,8 @@ class AdminPage(resource.Resource):
if not self.is_authorized(request):
return ""
if request.path == "/updatewhitelist":
return self.update_whitelist(request)
elif request.path == "/enableuser":
return self.enable_disable_user(request, True)
elif request.path == "/disableuser":
return self.enable_disable_user(request, False)
if request.path == "/updatebanlist":
return self.update_banlist(request)
else:
return self.get_header() + self.get_footer()

View File

@ -105,26 +105,22 @@ class GamespyDatabase(object):
def initialize_database(self):
with Transaction(self.conn) as tx:
row = tx.queryone("SELECT COUNT(*) FROM sqlite_master WHERE name = 'users' AND type = 'table'")
count = int(row[0])
# I highly doubt having everything in a database be of the type TEXT is a good practice,
# but I'm not good with databases and I'm not 100% positive that, for instance, that all
# user id's will be ints, or all passwords will be ints, etc, despite not seeing any
# evidence yet to say otherwise as far as Nintendo DS games go.
if count < 1:
tx.nonquery("CREATE TABLE users (profileid INT, userid TEXT, password TEXT, gsbrcd TEXT, email TEXT, uniquenick TEXT, pid TEXT, lon TEXT, lat TEXT, loc TEXT, firstname TEXT, lastname TEXT, stat TEXT, partnerid TEXT, console INT, csnum TEXT, cfc TEXT, bssid TEXT, devname BLOB, birth TEXT, gameid TEXT, enabled INT, zipcode TEXT, aim TEXT)")
tx.nonquery("CREATE TABLE sessions (session TEXT, profileid INT, loginticket TEXT)")
tx.nonquery("CREATE TABLE buddies (userProfileId INT, buddyProfileId INT, time INT, status INT, notified INT, gameid TEXT, blocked INT)")
tx.nonquery("CREATE TABLE pending_messages (sourceid INT, targetid INT, msg TEXT)")
tx.nonquery("CREATE TABLE gamestat_profile (profileid INT, dindex TEXT, ptype TEXT, data TEXT)")
tx.nonquery("CREATE UNIQUE INDEX gamestatprofile_triple on gamestat_profile(profileid,dindex,ptype)")
tx.nonquery("CREATE TABLE gameinfo (profileid INT, dindex TEXT, ptype TEXT, data TEXT)")
tx.nonquery("CREATE TABLE nas_logins (userid TEXT, authtoken TEXT, data TEXT)")
tx.nonquery("CREATE TABLE IF NOT EXISTS whitelist (userid TEXT, gameid TEXT, macadr TEXT)")
tx.nonquery("CREATE TABLE IF NOT EXISTS users (profileid INT, userid TEXT, password TEXT, gsbrcd TEXT, email TEXT, uniquenick TEXT, pid TEXT, lon TEXT, lat TEXT, loc TEXT, firstname TEXT, lastname TEXT, stat TEXT, partnerid TEXT, console INT, csnum TEXT, cfc TEXT, bssid TEXT, devname BLOB, birth TEXT, gameid TEXT, enabled INT, zipcode TEXT, aim TEXT)")
tx.nonquery("CREATE TABLE IF NOT EXISTS sessions (session TEXT, profileid INT, loginticket TEXT)")
tx.nonquery("CREATE TABLE IF NOT EXISTS buddies (userProfileId INT, buddyProfileId INT, time INT, status INT, notified INT, gameid TEXT, blocked INT)")
tx.nonquery("CREATE TABLE IF NOT EXISTS pending_messages (sourceid INT, targetid INT, msg TEXT)")
tx.nonquery("CREATE TABLE IF NOT EXISTS gamestat_profile (profileid INT, dindex TEXT, ptype TEXT, data TEXT)")
tx.nonquery("CREATE TABLE IF NOT EXISTS gameinfo (profileid INT, dindex TEXT, ptype TEXT, data TEXT)")
tx.nonquery("CREATE TABLE IF NOT EXISTS nas_logins (userid TEXT, authtoken TEXT, data TEXT)")
tx.nonquery("CREATE TABLE IF NOT EXISTS banned (gameid TEXT, ipaddr TEXT)")
# Create some indexes for performance.
tx.nonquery("CREATE UNIQUE INDEX IF NOT EXISTS gamestatprofile_triple on gamestat_profile(profileid,dindex,ptype)")
tx.nonquery("CREATE UNIQUE INDEX IF NOT EXISTS users_profileid_idx ON users (profileid)")
tx.nonquery("CREATE INDEX IF NOT EXISTS users_userid_idx ON users (userid)")
tx.nonquery("CREATE INDEX IF NOT EXISTS pending_messages_targetid_idx ON pending_messages (targetid)")
@ -215,16 +211,6 @@ class GamespyDatabase(object):
def create_user(self, userid, password, email, uniquenick, gsbrcd, console, csnum, cfc, bssid, devname, birth, gameid, macadr):
#Check for console ban
with Transaction(self.conn) as tx:
row = tx.queryone("SELECT * FROM users WHERE userid = ? and gameid = ? and enabled = 0 "
"and gameid not in (select gameid from whitelist where gameid=? and macadr=?) limit 1"
,(userid, gameid, gameid, macadr))
r = self.get_dict(row)
if r != None:
logger.log(logging.INFO, "--- REJECTING BANNED CONSOLE --- userid=%s,gameid=%s,macadr=%s", userid,gameid,macadr)
return None
if self.check_user_exists(userid, gsbrcd) == 0:
profileid = self.get_next_free_profileid()
@ -413,6 +399,11 @@ class GamespyDatabase(object):
else:
return json.loads(r["data"])
def is_banned(self,postdata):
with Transaction(self.conn) as tx:
row = tx.queryone("SELECT COUNT(*) FROM banned WHERE gameid = ? AND ipaddr = ?",(postdata['gamecd'][:-1],postdata['ipaddr']))
return int(row[0]) > 0
def get_next_available_userid(self):
with Transaction(self.conn) as tx:
row = tx.queryone("SELECT max(userid) AS maxuser FROM users")

View File

@ -88,10 +88,11 @@ class NasHTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
post = self.str_to_dict(self.rfile.read(length))
if self.client_address[0] == '127.0.0.1':
client_address = (self.headers.get('x-forwarded-for', self.client_address[0]), self.client_address[1])
post['ipaddr'] = client_address[0]
else:
client_address = self.client_address
post['ipaddr'] = client_address[0]
if self.path == "/ac":
logger.log(logging.DEBUG, "Request to %s from %s", self.path, client_address)
logger.log(logging.DEBUG, post)
@ -106,41 +107,59 @@ class NasHTTPServerHandler(BaseHTTPServer.BaseHTTPRequestHandler):
if action == "acctcreate":
# TODO: test for duplicate accounts
ret["returncd"] = "002"
ret['userid'] = self.server.db.get_next_available_userid()
if self.server.db.is_banned(post):
logger.log(logging.DEBUG, "acctcreate denied for banned user "+str(post))
ret = {
"datetime": time.strftime("%Y%m%d%H%M%S"),
"returncd": "3913",
"locator": "gamespy.com",
"retry": "1",
"reason": "User banned."
}
else:
ret["returncd"] = "002"
ret['userid'] = self.server.db.get_next_available_userid()
logger.log(logging.DEBUG, "acctcreate response to %s", client_address)
logger.log(logging.DEBUG, ret)
logger.log(logging.DEBUG, "acctcreate response to %s", client_address)
logger.log(logging.DEBUG, ret)
ret = self.dict_to_str(ret)
elif action == "login":
challenge = utils.generate_random_str(8)
post["challenge"] = challenge
if not self.server.db.check_user_exists(post["userid"], post["gsbrcd"]) or self.server.db.check_user_enabled(post["userid"], post["gsbrcd"]):
authtoken = self.server.db.generate_authtoken(post["userid"], post)
ret.update({
"returncd": "001",
if self.server.db.is_banned(post):
logger.log(logging.DEBUG, "login denied for banned user "+str(post))
ret = {
"datetime": time.strftime("%Y%m%d%H%M%S"),
"returncd": "3914",
"locator": "gamespy.com",
"challenge": challenge,
"token": authtoken,
})
logger.log(logging.DEBUG, "login response to %s", client_address)
logger.log(logging.DEBUG, ret)
ret = self.dict_to_str(ret)
"retry": "1",
"reason": "User banned."
}
else:
# user is banned
ret.update({
"returncd": "3912",
"locator": "gamespy.com",
})
logger.log(logging.DEBUG, "login response to %s (user banned)", client_address)
logger.log(logging.DEBUG, ret)
challenge = utils.generate_random_str(8)
post["challenge"] = challenge
if not self.server.db.check_user_exists(post["userid"], post["gsbrcd"]) or self.server.db.check_user_enabled(post["userid"], post["gsbrcd"]):
authtoken = self.server.db.generate_authtoken(post["userid"], post)
ret.update({
"returncd": "001",
"locator": "gamespy.com",
"challenge": challenge,
"token": authtoken,
})
ret = self.dict_to_str(ret)
logger.log(logging.DEBUG, "login response to %s", client_address)
logger.log(logging.DEBUG, ret)
else:
# user is banned
ret.update({
"returncd": "3912",
"locator": "gamespy.com",
})
logger.log(logging.DEBUG, "login response to %s (user banned)", client_address)
logger.log(logging.DEBUG, ret)
ret = self.dict_to_str(ret)
elif action == "SVCLOC" or action == "svcloc": # Get service based on service id number
ret["returncd"] = "007"