mirror of https://github.com/dirtbags/moth.git
Mostly cosmetics
This commit is contained in:
parent
d9d7ddfe53
commit
ec202f23f5
63
ctf.css
63
ctf.css
|
@ -30,6 +30,49 @@ h1:first-child:before {
|
||||||
content: "Capture The Flag: ";
|
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 ****/
|
/**** body ****/
|
||||||
|
|
||||||
a img {
|
a img {
|
||||||
|
@ -64,6 +107,18 @@ th, td {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
line-height: 1.4em;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: #f4f4f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**** special cases ****/
|
||||||
|
|
||||||
|
.wide {
|
||||||
|
max-width: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.scoreboard {
|
.scoreboard {
|
||||||
background: #222;
|
background: #222;
|
||||||
}
|
}
|
||||||
|
@ -72,12 +127,6 @@ th, td {
|
||||||
height: 400px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
|
||||||
line-height: 1.4em;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
color: #f4f4f4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.solved {
|
.solved {
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
@ -93,3 +142,5 @@ table.pollster td {
|
||||||
table.pollster thead {
|
table.pollster thead {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,10 @@ if 'home' in os.environ.get('SCRIPT_FILENAME', ''):
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
# An actual installation
|
# An actual installation
|
||||||
config = {'global':
|
config = {
|
||||||
{'data_dir': '/var/lib/ctf',
|
'global':
|
||||||
|
{
|
||||||
|
'data_dir': '/var/lib/ctf',
|
||||||
'base_url': '/',
|
'base_url': '/',
|
||||||
'css_url': '/ctf.css',
|
'css_url': '/ctf.css',
|
||||||
'disabled_dir': '/var/lib/ctf/disabled',
|
'disabled_dir': '/var/lib/ctf/disabled',
|
||||||
|
@ -37,13 +39,17 @@ else:
|
||||||
'house_team': 'dirtbags',
|
'house_team': 'dirtbags',
|
||||||
'passwd': '/var/lib/ctf/passwd',
|
'passwd': '/var/lib/ctf/passwd',
|
||||||
'team_colors': team_colors,
|
'team_colors': team_colors,
|
||||||
|
},
|
||||||
|
'pollster':
|
||||||
|
{
|
||||||
'poll_interval': 60,
|
'poll_interval': 60,
|
||||||
'poll_timeout': 0.5,
|
'poll_timeout': 0.5,
|
||||||
'heartbeat_dir': '/var/lib/pollster',
|
'heartbeat_dir': '/var/lib/pollster',
|
||||||
'poll_dir': '/var/lib/www',
|
'results': '/var/lib/pollster/status.html',
|
||||||
},
|
},
|
||||||
'puzzler':
|
'puzzler':
|
||||||
{'dir': '/usr/lib/www/puzzler',
|
{
|
||||||
|
'dir': '/usr/lib/www/puzzler',
|
||||||
'cgi_url': '/puzzler.cgi',
|
'cgi_url': '/puzzler.cgi',
|
||||||
'base_url': '/puzzler',
|
'base_url': '/puzzler',
|
||||||
'keys_file': '/usr/lib/ctf/puzzler.keys',
|
'keys_file': '/usr/lib/ctf/puzzler.keys',
|
||||||
|
@ -71,22 +77,37 @@ def datafile(filename):
|
||||||
def url(path):
|
def url(path):
|
||||||
return base_url + path
|
return base_url + path
|
||||||
|
|
||||||
def start_html(title):
|
def start_html(title, hdr='', cls=''):
|
||||||
|
ret = []
|
||||||
if os.environ.get('GATEWAY_INTERFACE'):
|
if os.environ.get('GATEWAY_INTERFACE'):
|
||||||
print('Content-type: text/html')
|
ret.append('Content-type: text/html')
|
||||||
print()
|
ret.append('')
|
||||||
print('''<?xml version="1.0" encoding="UTF-8"?>
|
ret.append('''<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE html PUBLIC
|
<!DOCTYPE html PUBLIC
|
||||||
"-//W3C//DTD XHTML 1.0 Strict//EN"
|
"-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<title>%s</title>
|
<title>%(title)s</title>
|
||||||
<link rel="stylesheet" href="%s" type="text/css" />
|
<link rel="stylesheet" href="%(css)s" type="text/css" />
|
||||||
|
%(hdr)s
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="%(class)s">
|
||||||
<h1>%s</h1>
|
<h1>%(title)s</h1>
|
||||||
''' % (title, css, title))
|
<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():
|
def end_html():
|
||||||
print('</body></html>')
|
return '</body></html>'
|
||||||
|
|
|
@ -66,9 +66,10 @@ def start_html(title):
|
||||||
if passwd:
|
if passwd:
|
||||||
c['passwd'] = passwd
|
c['passwd'] = passwd
|
||||||
print(c)
|
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):
|
def safe_join(*args):
|
||||||
safe = list(args[:1])
|
safe = list(args[:1])
|
||||||
|
|
|
@ -7,26 +7,7 @@ import string
|
||||||
from . import teams
|
from . import teams
|
||||||
from . import config
|
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():
|
def main():
|
||||||
print('Content-type: text/html')
|
|
||||||
print()
|
|
||||||
|
|
||||||
f = cgi.FieldStorage()
|
f = cgi.FieldStorage()
|
||||||
|
|
||||||
team = f.getfirst('team', '')
|
team = f.getfirst('team', '')
|
||||||
|
|
|
@ -15,20 +15,8 @@ def main():
|
||||||
|
|
||||||
categories = [(cat, s.cat_points(cat)) for cat in s.categories()]
|
categories = [(cat, s.cat_points(cat)) for cat in s.categories()]
|
||||||
|
|
||||||
print('Content-type: text/html')
|
|
||||||
print('Refresh: 10')
|
print('Refresh: 10')
|
||||||
print()
|
print(config.start_html('Scoreboard', cls='wide'))
|
||||||
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('<table class="scoreboard">')
|
print('<table class="scoreboard">')
|
||||||
print('<tr>')
|
print('<tr>')
|
||||||
print('<th>Overall</th>')
|
print('<th>Overall</th>')
|
||||||
|
@ -71,9 +59,8 @@ def main():
|
||||||
<p class="histogram">
|
<p class="histogram">
|
||||||
<img src="histogram.png" alt="scores over time" />
|
<img src="histogram.png" alt="scores over time" />
|
||||||
</p>
|
</p>
|
||||||
|
''')
|
||||||
</body>
|
print(config.end_html())
|
||||||
</html>''')
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
7
ctfd.py
7
ctfd.py
|
@ -24,9 +24,6 @@ def reap():
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def sigchld(signum, frame):
|
|
||||||
do_reap = True
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
p = optparse.OptionParser()
|
p = optparse.OptionParser()
|
||||||
p.add_option('-p', '--genpass', dest='cat', default=None,
|
p.add_option('-p', '--genpass', dest='cat', default=None,
|
||||||
|
@ -39,13 +36,11 @@ def main():
|
||||||
pointsrv = pointsd.start()
|
pointsrv = pointsd.start()
|
||||||
flagsrv = flagd.start()
|
flagsrv = flagd.start()
|
||||||
|
|
||||||
signal.signal(signal.SIGCHLD, sigchld)
|
|
||||||
s = pointsrv.store
|
s = pointsrv.store
|
||||||
slen = 0
|
slen = 0
|
||||||
while True:
|
while True:
|
||||||
if do_reap:
|
|
||||||
reap()
|
|
||||||
asyncore.loop(timeout=30, use_poll=True, count=1)
|
asyncore.loop(timeout=30, use_poll=True, count=1)
|
||||||
|
reap()
|
||||||
if len(s) > slen:
|
if len(s) > slen:
|
||||||
slen = len(s)
|
slen = len(s)
|
||||||
chart(s)
|
chart(s)
|
||||||
|
|
72
mkpuzzles.py
72
mkpuzzles.py
|
@ -17,40 +17,7 @@ opts, args = p.parse_args()
|
||||||
|
|
||||||
keys = []
|
keys = []
|
||||||
|
|
||||||
for cat in os.listdir(opts.puzzles):
|
js = '''
|
||||||
dirname = os.path.join(opts.puzzles, cat)
|
|
||||||
for points in os.listdir(dirname):
|
|
||||||
pointsdir = os.path.join(dirname, points)
|
|
||||||
outdir = os.path.join(opts.htmldir, cat, points)
|
|
||||||
try:
|
|
||||||
os.makedirs(outdir)
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
readme = ''
|
|
||||||
files = []
|
|
||||||
for fn in os.listdir(pointsdir):
|
|
||||||
path = os.path.join(pointsdir, fn)
|
|
||||||
if fn == 'key':
|
|
||||||
key = open(path, encoding='utf-8').readline().strip()
|
|
||||||
keys.append((cat, points, key))
|
|
||||||
elif fn == 'index.html':
|
|
||||||
readme = open(path, encoding='utf-8').read()
|
|
||||||
elif fn.endswith('~'):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
files.append((fn, path))
|
|
||||||
|
|
||||||
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">
|
<script type="text/javascript">
|
||||||
function readCookie(key) {
|
function readCookie(key) {
|
||||||
var s = key + '=';
|
var s = key + '=';
|
||||||
|
@ -79,11 +46,35 @@ for cat in os.listdir(opts.puzzles):
|
||||||
}
|
}
|
||||||
window.onload = getTeamInfo;
|
window.onload = getTeamInfo;
|
||||||
</script>
|
</script>
|
||||||
</head>
|
'''
|
||||||
<body>
|
|
||||||
<h1>%(title)s</h1>
|
for cat in os.listdir(opts.puzzles):
|
||||||
''' % {'title': title,
|
dirname = os.path.join(opts.puzzles, cat)
|
||||||
'css': config.css})
|
for points in os.listdir(dirname):
|
||||||
|
pointsdir = os.path.join(dirname, points)
|
||||||
|
outdir = os.path.join(opts.htmldir, cat, points)
|
||||||
|
try:
|
||||||
|
os.makedirs(outdir)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
readme = ''
|
||||||
|
files = []
|
||||||
|
for fn in os.listdir(pointsdir):
|
||||||
|
path = os.path.join(pointsdir, fn)
|
||||||
|
if fn == 'key':
|
||||||
|
key = open(path, encoding='utf-8').readline().strip()
|
||||||
|
keys.append((cat, points, key))
|
||||||
|
elif fn == 'index.html':
|
||||||
|
readme = open(path, encoding='utf-8').read()
|
||||||
|
elif fn.endswith('~'):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
files.append((fn, path))
|
||||||
|
|
||||||
|
title = '%s for %s points' % (cat, points)
|
||||||
|
f = open(os.path.join(outdir, 'index.html'), 'w', encoding='utf-8')
|
||||||
|
f.write(config.start_html(title, js))
|
||||||
if readme:
|
if readme:
|
||||||
f.write('<div class="readme">%s</div>\n' % readme)
|
f.write('<div class="readme">%s</div>\n' % readme)
|
||||||
if files:
|
if files:
|
||||||
|
@ -105,11 +96,10 @@ for cat in os.listdir(opts.puzzles):
|
||||||
<input type="submit" />
|
<input type="submit" />
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
''' % {'cgi': config.get('puzzler', 'cgi_url'),
|
''' % {'cgi': config.get('puzzler', 'cgi_url'),
|
||||||
'cat': cat,
|
'cat': cat,
|
||||||
'points': points})
|
'points': points})
|
||||||
|
f.write(config.end_html())
|
||||||
|
|
||||||
f = open(opts.keyfile, 'w', encoding='utf-8')
|
f = open(opts.keyfile, 'w', encoding='utf-8')
|
||||||
for key in keys:
|
for key in keys:
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
|
|
||||||
case "$REMOTEADDR" in
|
ip=$(echo $UDPREMOTEADDR | cut -d: -f1)
|
||||||
10.0.0.[2-254])
|
case "$ip" in
|
||||||
touch /var/lib/pollster/$REMOTEADDR
|
10.0.0.*)
|
||||||
|
touch /var/lib/pollster/$ip
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
echo 'Hello.'
|
||||||
|
|
|
@ -13,10 +13,10 @@ from ctf import config
|
||||||
from ctf import pointscli
|
from ctf import pointscli
|
||||||
|
|
||||||
DEBUG = False
|
DEBUG = False
|
||||||
POLL_INTERVAL = config.get('poll_interval')
|
POLL_INTERVAL = config.get('pollster', 'poll_interval')
|
||||||
IP_DIR = config.get('heartbeat_dir')
|
IP_DIR = config.get('pollster', 'heartbeat_dir')
|
||||||
REPORT_PATH = config.get('poll_dir')
|
REPORT_PATH = config.get('pollster', 'results')
|
||||||
SOCK_TIMEOUT = config.get('poll_timeout')
|
SOCK_TIMEOUT = config.get('pollster', 'poll_timeout')
|
||||||
|
|
||||||
class PointSubmitter(threading.Thread):
|
class PointSubmitter(threading.Thread):
|
||||||
''' Pulls point allocations from the queue and submits them. '''
|
''' Pulls point allocations from the queue and submits them. '''
|
||||||
|
@ -184,16 +184,9 @@ while True:
|
||||||
out = open(REPORT_PATH, 'w')
|
out = open(REPORT_PATH, 'w')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
out = None
|
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:
|
for ip in ips:
|
||||||
|
|
||||||
# check file name format is ip
|
# check file name format is ip
|
||||||
if ip_re.match(ip) is None:
|
if ip_re.match(ip) is None:
|
||||||
continue
|
continue
|
||||||
|
@ -240,8 +233,7 @@ while True:
|
||||||
sleep_time = POLL_INTERVAL - exec_time
|
sleep_time = POLL_INTERVAL - exec_time
|
||||||
|
|
||||||
if out is not None:
|
if out is not None:
|
||||||
out.write('<p><b>Next poll in: %ds</b></p>\n' % sleep_time)
|
out.write(config.end_html())
|
||||||
out.write('</body>\n</html>\n')
|
|
||||||
out.close()
|
out.close()
|
||||||
|
|
||||||
# sleep until its time to poll again
|
# sleep until its time to poll again
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
|
|
||||||
exec udpsvd 0 9 /usr/sbin/in.heartbeatd
|
exec udpsvd 0 9 envuidgid ctf /usr/sbin/in.heartbeatd
|
||||||
|
|
|
@ -14,6 +14,8 @@ MAX_HIST = 30
|
||||||
HIST_STEP = 100
|
HIST_STEP = 100
|
||||||
key = 'tanks:::2bac5e912ff2e1ad559b177eb5aeecca'
|
key = 'tanks:::2bac5e912ff2e1ad559b177eb5aeecca'
|
||||||
|
|
||||||
|
running = True
|
||||||
|
|
||||||
class Flagger(asynchat.async_chat):
|
class Flagger(asynchat.async_chat):
|
||||||
"""Use to connect to flagd and submit the current flag holder."""
|
"""Use to connect to flagd and submit the current flag holder."""
|
||||||
|
|
||||||
|
@ -31,6 +33,7 @@ class Flagger(asynchat.async_chat):
|
||||||
def handle_error(self):
|
def handle_error(self):
|
||||||
# If we lose the connection to flagd, nobody can score any
|
# If we lose the connection to flagd, nobody can score any
|
||||||
# points. Terminate everything.
|
# points. Terminate everything.
|
||||||
|
running = False
|
||||||
asyncore.close_all()
|
asyncore.close_all()
|
||||||
asynchat.async_chat.handle_error(self)
|
asynchat.async_chat.handle_error(self)
|
||||||
|
|
||||||
|
@ -84,6 +87,8 @@ def main():
|
||||||
lastrun = 0
|
lastrun = 0
|
||||||
while True:
|
while True:
|
||||||
asyncore.loop(60, count=1)
|
asyncore.loop(60, count=1)
|
||||||
|
if not running:
|
||||||
|
break
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if now - lastrun >= 60:
|
if now - lastrun >= 60:
|
||||||
run_tanks(args, turns, flagger)
|
run_tanks(args, turns, flagger)
|
||||||
|
|
Loading…
Reference in New Issue