Mostly cosmetics

This commit is contained in:
Neale Pickett 2009-10-08 16:42:26 -06:00
parent d9d7ddfe53
commit ec202f23f5
11 changed files with 179 additions and 155 deletions

69
ctf.css
View File

@ -30,6 +30,49 @@ h1:first-child:before {
content: "Capture The Flag: ";
}
/*** left side bar ***/
#navigation {
position: fixed;
background: #222;
opacity: 0.9;
top: 80px;
left: 0px;
padding: 0;
}
#navigation ul {
list-style: none;
padding: 0;
margin: 0;
}
#navigation li a {
display: block;
height: 25px;
width: 90px;
padding: 5px;
margin: 5px;
background: inherit;
border-right: 4px solid #444;
color: #999;
text-transform: lowercase;
font-size: 0.9em;
}
#navigation li a:hover {
color: #f4f4f4;
background: #333;
border-right: 4px solid #2a2;
}
#navigation li .active {
color: #999;
background: #333;
border-right: 4px solid #444;
}
/**** body ****/
a img {
@ -64,6 +107,18 @@ th, td {
vertical-align: top;
}
p {
line-height: 1.4em;
margin-bottom: 20px;
color: #f4f4f4;
}
/**** special cases ****/
.wide {
max-width: inherit;
}
.scoreboard {
background: #222;
}
@ -72,24 +127,20 @@ th, td {
height: 400px;
}
p {
line-height: 1.4em;
margin-bottom: 20px;
color: #f4f4f4;
}
.solved {
text-decoration: line-through;
}
table.pollster {
margin-left: 5em;
margin-left: 5em;
}
table.pollster td {
padding: 2px 1em 2px 5px;
padding: 2px 1em 2px 5px;
}
table.pollster thead {
font-weight: bold;
font-weight: bold;
}

View File

@ -28,27 +28,33 @@ if 'home' in os.environ.get('SCRIPT_FILENAME', ''):
}
else:
# An actual installation
config = {'global':
{'data_dir': '/var/lib/ctf',
'base_url': '/',
'css_url': '/ctf.css',
'disabled_dir': '/var/lib/ctf/disabled',
'flags_dir': '/var/lib/ctf/flags',
'house_team': 'dirtbags',
'passwd': '/var/lib/ctf/passwd',
'team_colors': team_colors,
'poll_interval': 60,
'poll_timeout': 0.5,
'heartbeat_dir': '/var/lib/pollster',
'poll_dir': '/var/lib/www',
},
'puzzler':
{'dir': '/usr/lib/www/puzzler',
'cgi_url': '/puzzler.cgi',
'base_url': '/puzzler',
'keys_file': '/usr/lib/ctf/puzzler.keys',
},
}
config = {
'global':
{
'data_dir': '/var/lib/ctf',
'base_url': '/',
'css_url': '/ctf.css',
'disabled_dir': '/var/lib/ctf/disabled',
'flags_dir': '/var/lib/ctf/flags',
'house_team': 'dirtbags',
'passwd': '/var/lib/ctf/passwd',
'team_colors': team_colors,
},
'pollster':
{
'poll_interval': 60,
'poll_timeout': 0.5,
'heartbeat_dir': '/var/lib/pollster',
'results': '/var/lib/pollster/status.html',
},
'puzzler':
{
'dir': '/usr/lib/www/puzzler',
'cgi_url': '/puzzler.cgi',
'base_url': '/puzzler',
'keys_file': '/usr/lib/ctf/puzzler.keys',
},
}
def get(section, key):
return config[section][key]
@ -71,22 +77,37 @@ def datafile(filename):
def url(path):
return base_url + path
def start_html(title):
def start_html(title, hdr='', cls=''):
ret = []
if os.environ.get('GATEWAY_INTERFACE'):
print('Content-type: text/html')
print()
print('''<?xml version="1.0" encoding="UTF-8"?>
ret.append('Content-type: text/html')
ret.append('')
ret.append('''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>%s</title>
<link rel="stylesheet" href="%s" type="text/css" />
<title>%(title)s</title>
<link rel="stylesheet" href="%(css)s" type="text/css" />
%(hdr)s
</head>
<body>
<h1>%s</h1>
''' % (title, css, title))
<body class="%(class)s">
<h1>%(title)s</h1>
<div id="navigation">
<ul>
<li><a href="%(base)s">Home</a></li>
<li><a href="%(base)sintro.html">Intro/Rules</a></li>
<li><a href="%(base)spuzzler.cgi">Puzzles</a></li>
<li><a href="%(base)sscoresboard.cgi">Scoreboard</a></li>
</ul>
</div>
''' % {'title': title,
'css': css,
'hdr': hdr,
'base': base_url,
'class': cls})
return '\n'.join(ret)
def end_html():
print('</body></html>')
return '</body></html>'

View File

@ -66,9 +66,10 @@ def start_html(title):
if passwd:
c['passwd'] = passwd
print(c)
config.start_html(title)
print(config.start_html(title))
end_html = config.end_html
def end_html():
print(config.end_html())
def safe_join(*args):
safe = list(args[:1])

View File

@ -7,26 +7,7 @@ import string
from . import teams
from . import config
def head(title):
return '''<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Team Registration</title>
<link rel="stylesheet" href="%s" type="text/css" />
</head>
<body>
<h1>%s</h1>
''' % (config.css, title)
def foot():
return '''</body></html>'''
def main():
print('Content-type: text/html')
print()
f = cgi.FieldStorage()
team = f.getfirst('team', '')

View File

@ -15,20 +15,8 @@ def main():
categories = [(cat, s.cat_points(cat)) for cat in s.categories()]
print('Content-type: text/html')
print('Refresh: 10')
print()
print('''<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>Scoreboard</title>
<link rel="stylesheet" href="%sctf.css" type="text/css" />
</head>
<body style="max-width: inherit;">
<h1>Scoreboard</h1>
''' % config.base_url)
print(config.start_html('Scoreboard', cls='wide'))
print('<table class="scoreboard">')
print('<tr>')
print('<th>Overall</th>')
@ -71,9 +59,8 @@ def main():
<p class="histogram">
<img src="histogram.png" alt="scores over time" />
</p>
</body>
</html>''')
''')
print(config.end_html())
if __name__ == '__main__':
main()

View File

@ -24,9 +24,6 @@ def reap():
except OSError:
pass
def sigchld(signum, frame):
do_reap = True
def main():
p = optparse.OptionParser()
p.add_option('-p', '--genpass', dest='cat', default=None,
@ -39,13 +36,11 @@ def main():
pointsrv = pointsd.start()
flagsrv = flagd.start()
signal.signal(signal.SIGCHLD, sigchld)
s = pointsrv.store
slen = 0
while True:
if do_reap:
reap()
asyncore.loop(timeout=30, use_poll=True, count=1)
reap()
if len(s) > slen:
slen = len(s)
chart(s)

View File

@ -17,6 +17,37 @@ opts, args = p.parse_args()
keys = []
js = '''
<script type="text/javascript">
function readCookie(key) {
var s = key + '=';
var toks = document.cookie.split(';');
for (var i = 0; i < toks.length; i++) {
var tok = toks[i];
while (tok.charAt(0) == ' ') {
tok = tok.substring(1, tok.length);
}
if (tok.indexOf(s) == 0) {
return tok.substring(s.length, tok.length);
}
}
return null;
}
function getTeamInfo() {
team = readCookie('team');
passwd = readCookie('passwd');
if (team != null) {
document.getElementById("form").t.value = team;
}
if (passwd != null) {
document.getElementById("form").w.value = passwd;
}
}
window.onload = getTeamInfo;
</script>
'''
for cat in os.listdir(opts.puzzles):
dirname = os.path.join(opts.puzzles, cat)
for points in os.listdir(dirname):
@ -43,47 +74,7 @@ for cat in os.listdir(opts.puzzles):
title = '%s for %s points' % (cat, points)
f = open(os.path.join(outdir, 'index.html'), 'w', encoding='utf-8')
f.write('''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>%(title)s</title>
<link rel="stylesheet" href="%(css)s" type="text/css" />
<script type="text/javascript">
function readCookie(key) {
var s = key + '=';
var toks = document.cookie.split(';');
for (var i = 0; i < toks.length; i++) {
var tok = toks[i];
while (tok.charAt(0) == ' ') {
tok = tok.substring(1, tok.length);
}
if (tok.indexOf(s) == 0) {
return tok.substring(s.length, tok.length);
}
}
return null;
}
function getTeamInfo() {
team = readCookie('team');
passwd = readCookie('passwd');
if (team != null) {
document.getElementById("form").t.value = team;
}
if (passwd != null) {
document.getElementById("form").w.value = passwd;
}
}
window.onload = getTeamInfo;
</script>
</head>
<body>
<h1>%(title)s</h1>
''' % {'title': title,
'css': config.css})
f.write(config.start_html(title, js))
if readme:
f.write('<div class="readme">%s</div>\n' % readme)
if files:
@ -105,11 +96,10 @@ for cat in os.listdir(opts.puzzles):
<input type="submit" />
</fieldset>
</form>
</body>
</html>
''' % {'cgi': config.get('puzzler', 'cgi_url'),
'cat': cat,
'points': points})
f.write(config.end_html())
f = open(opts.keyfile, 'w', encoding='utf-8')
for key in keys:

View File

@ -1,8 +1,9 @@
#! /bin/sh
case "$REMOTEADDR" in
10.0.0.[2-254])
touch /var/lib/pollster/$REMOTEADDR
ip=$(echo $UDPREMOTEADDR | cut -d: -f1)
case "$ip" in
10.0.0.*)
touch /var/lib/pollster/$ip
;;
esac
echo 'Hello.'

View File

@ -13,10 +13,10 @@ from ctf import config
from ctf import pointscli
DEBUG = False
POLL_INTERVAL = config.get('poll_interval')
IP_DIR = config.get('heartbeat_dir')
REPORT_PATH = config.get('poll_dir')
SOCK_TIMEOUT = config.get('poll_timeout')
POLL_INTERVAL = config.get('pollster', 'poll_interval')
IP_DIR = config.get('pollster', 'heartbeat_dir')
REPORT_PATH = config.get('pollster', 'results')
SOCK_TIMEOUT = config.get('pollster', 'poll_timeout')
class PointSubmitter(threading.Thread):
''' Pulls point allocations from the queue and submits them. '''
@ -24,7 +24,7 @@ class PointSubmitter(threading.Thread):
threading.Thread.__init__(self)
self.point_queue = point_queue
self.sock = pointscli.makesock('localhost')
def run(self):
# loop forever
while(True):
@ -40,7 +40,7 @@ class PointSubmitter(threading.Thread):
def socket_poll(ip, port, msg, prot, max_recv=1):
''' Connect via socket to the specified <ip>:<port> using the
specified <prot>, send the specified <msg> and return the
specified <prot>, send the specified <msg> and return the
response or None if something went wrong. <max_recvs> specifies
how many times to read from the socket (default to once). '''
@ -51,7 +51,7 @@ def socket_poll(ip, port, msg, prot, max_recv=1):
print('pollster: create socket failed (%s)' % e)
traceback.print_exc()
return None
sock.settimeout(SOCK_TIMEOUT)
# connect
@ -90,7 +90,7 @@ def socket_poll(ip, port, msg, prot, max_recv=1):
except Exception as e:
print('pollster: receive from %s:%d failed (%s)' % (ip, port, e))
traceback.print_exc()
if len(resp) == 0:
return None
@ -131,7 +131,7 @@ def poll_catcgi(ip):
content_len = int(content[0])
except Exception as e:
return None
if content_len <= 0:
return None
return content[1].strip('\r\n')
@ -141,13 +141,13 @@ def poll_tftpd(ip):
resp = socket_poll(ip, 69, b'\x00\x01' + b'flag' + b'\x00' + b'octet' + b'\x00', socket.SOCK_DGRAM)
if resp is None:
return None
if len(resp) <= 5:
return None
resp = resp.split('\n')[0]
return resp[4:].strip('\r\n')
# PUT POLL FUNCTIONS IN HERE OR THEY WONT BE POLLED
POLLS = {
'fingerd' : poll_fingerd,
@ -174,7 +174,7 @@ while True:
except Exception as e:
print('pollster: could not list dir %s (%s)' % (IP_DIR, e))
traceback.print_exc()
try:
os.remove(REPORT_PATH)
except Exception as e:
@ -184,16 +184,9 @@ while True:
out = open(REPORT_PATH, 'w')
except Exception as e:
out = None
pass
if out is not None:
out.write('<html>\n<head>\n')
out.write('<title>Pollster Results</title>\n')
out.write('<link rel="stylesheet" href="ctf.css" type="text/css" media="all" />\n')
out.write('</head><body>\n<h1>Polling Results</h1>\n')
out.write(config.start_html('Team Service Availability'))
for ip in ips:
# check file name format is ip
if ip_re.match(ip) is None:
continue
@ -231,7 +224,7 @@ while True:
if out is not None:
out.write('</table>\n')
if DEBUG is True:
print('+-----------------------------------------+')
@ -240,8 +233,7 @@ while True:
sleep_time = POLL_INTERVAL - exec_time
if out is not None:
out.write('<p><b>Next poll in: %ds</b></p>\n' % sleep_time)
out.write('</body>\n</html>\n')
out.write(config.end_html())
out.close()
# sleep until its time to poll again

View File

@ -1,3 +1,3 @@
#! /bin/sh
exec udpsvd 0 9 /usr/sbin/in.heartbeatd
exec udpsvd 0 9 envuidgid ctf /usr/sbin/in.heartbeatd

View File

@ -14,6 +14,8 @@ MAX_HIST = 30
HIST_STEP = 100
key = 'tanks:::2bac5e912ff2e1ad559b177eb5aeecca'
running = True
class Flagger(asynchat.async_chat):
"""Use to connect to flagd and submit the current flag holder."""
@ -31,6 +33,7 @@ class Flagger(asynchat.async_chat):
def handle_error(self):
# If we lose the connection to flagd, nobody can score any
# points. Terminate everything.
running = False
asyncore.close_all()
asynchat.async_chat.handle_error(self)
@ -84,6 +87,8 @@ def main():
lastrun = 0
while True:
asyncore.loop(60, count=1)
if not running:
break
now = time.time()
if now - lastrun >= 60:
run_tanks(args, turns, flagger)