mirror of https://github.com/dirtbags/moth.git
installing okay now
This commit is contained in:
parent
23e750ed85
commit
4acc889c09
58
bin/kothd
58
bin/kothd
|
@ -1,58 +0,0 @@
|
||||||
#! /bin/sh
|
|
||||||
|
|
||||||
echo "Figuring out web user..."
|
|
||||||
for www in www-data http _; do
|
|
||||||
id $www && break
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ $www = _ ]; then
|
|
||||||
echo "Unable to determine httpd user on this system. Dying."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
initialize () {
|
|
||||||
for i in points.new points.tmp teams; do
|
|
||||||
mkdir -p state/$i
|
|
||||||
setfacl -m ${www}:rwx state/$i
|
|
||||||
done
|
|
||||||
|
|
||||||
>> state/points.log
|
|
||||||
hd < /dev/urandom | awk '{print $3 $4 $5 $6;}' | head -n 100 > state/teams/assigned.txt
|
|
||||||
}
|
|
||||||
|
|
||||||
once () {
|
|
||||||
if [ -f disabled ]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! [ -d state ]; then
|
|
||||||
initialize $1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Collect new points
|
|
||||||
find state/points.new -type f | while read fn; do
|
|
||||||
cat $fn >> state/points.log
|
|
||||||
rm $fn
|
|
||||||
done
|
|
||||||
|
|
||||||
# Generate new puzzles.html
|
|
||||||
if $KOTH_BASE/puzzles.cgi > www/puzzles.new; then
|
|
||||||
mv www/puzzles.new www/puzzles.html
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate new points.json
|
|
||||||
if $KOTH_BASE/points > www/points.new; then
|
|
||||||
mv www/points.new www/points.json
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
cd $(basename $0)
|
|
||||||
KOTH_BASE=$(pwd)
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
for dn in $KOTH_BASE/*; do
|
|
||||||
cd $dn
|
|
||||||
once
|
|
||||||
done
|
|
||||||
sleep 5
|
|
||||||
done
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
newdir=$1
|
||||||
|
if [ -z "$newdir" ]; then
|
||||||
|
echo "Usage: $0 NEWDIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
KOTH_BASE=$(cd $(dirname $0)/.. && pwd)
|
||||||
|
|
||||||
|
echo "Figuring out web user..."
|
||||||
|
for www in www-data http _; do
|
||||||
|
id $www && break
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $www = _ ]; then
|
||||||
|
echo "Unable to determine httpd user on this system. Dying."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p $newdir
|
||||||
|
cd $newdir
|
||||||
|
|
||||||
|
for i in points.new points.tmp teams; do
|
||||||
|
mkdir -p state/$i
|
||||||
|
setfacl -m ${www}:rwx state/$i
|
||||||
|
done
|
||||||
|
|
||||||
|
>> state/points.log
|
||||||
|
|
||||||
|
if ! [ -f assigned.txt ]; then
|
||||||
|
hd < /dev/urandom | awk '{print $3 $4 $5 $6;}' | head -n 100 > assigned.txt
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p www
|
||||||
|
cp -r $KOTH_BASE/html/* www/
|
||||||
|
cp $KOTH_BASE/bin/*.cgi www/
|
|
@ -0,0 +1,30 @@
|
||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
cd $(dirname $0)
|
||||||
|
|
||||||
|
# Do nothing if `disabled` is present
|
||||||
|
if [ -f disabled ]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Reset to initial state if `reset` is present
|
||||||
|
if [ -f reset ]; then
|
||||||
|
rm -f state/teams/* state/points.new/* state/points.tmp/*
|
||||||
|
> state/points.log
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Collect new points
|
||||||
|
find state/points.new -type f | while read fn; do
|
||||||
|
cat $fn >> state/points.log
|
||||||
|
rm $fn
|
||||||
|
done
|
||||||
|
|
||||||
|
# Generate new puzzles.html
|
||||||
|
if $KOTH_BASE/puzzles.cgi > www/puzzles.new; then
|
||||||
|
mv www/puzzles.new www/puzzles.html
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate new points.json
|
||||||
|
if $KOTH_BASE/points > www/points.new; then
|
||||||
|
mv www/points.new www/points.json
|
||||||
|
fi
|
|
@ -40,7 +40,7 @@ EOF
|
||||||
|
|
||||||
if [ -z "$hash" ] || [ -z "$team" ]; then
|
if [ -z "$hash" ] || [ -z "$team" ]; then
|
||||||
echo "<p>Empty field, cannot complete request</p>"
|
echo "<p>Empty field, cannot complete request</p>"
|
||||||
elif ! grep -q "^$hash$" state/teams/assigned.txt; then
|
elif ! grep -q "^$hash$" assigned.txt; then
|
||||||
echo "<p>That hash has not been assigned.</p>"
|
echo "<p>That hash has not been assigned.</p>"
|
||||||
elif [ -f state/teams/names/$hash ]; then
|
elif [ -f state/teams/names/$hash ]; then
|
||||||
echo "<p>That hash has already been registered.</p>"
|
echo "<p>That hash has already been registered.</p>"
|
|
@ -8,7 +8,7 @@
|
||||||
<h1>Credits</h1>
|
<h1>Credits</h1>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<p>Dirtbags King of the Hill was created by:</p>
|
<h2>Created By</h2>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Neale Pickett</li>
|
<li>Neale Pickett</li>
|
||||||
|
@ -26,18 +26,23 @@
|
||||||
<li>William Phillips</li>
|
<li>William Phillips</li>
|
||||||
<li>Should your name be here? Please remind me!</li>
|
<li>Should your name be here? Please remind me!</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
<p>Parts of this contest were inspired by contests from:</p>
|
<section>
|
||||||
|
<h2>Inspiration</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>DC949</li>
|
<li>DC949</li>
|
||||||
<li>Tube Warriors</li>
|
<li>Tube Warriors</li>
|
||||||
<li>Sandia National Laboratories</li>
|
<li>Sandia National Laboratories</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Thanks</h2>
|
||||||
<p>
|
<p>
|
||||||
Lastly, this contest would not exist were it not for hundreds of
|
This contest would not exist were it not for hundreds of
|
||||||
thousands of lines of code from free software authors around the
|
thousands of lines of code from free software authors around the
|
||||||
world, including:
|
world, including:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Busybox</li>
|
<li>Busybox</li>
|
||||||
|
|
|
@ -6,7 +6,15 @@
|
||||||
<link rel="stylesheet" href="css/style.css" type="text/css">
|
<link rel="stylesheet" href="css/style.css" type="text/css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Tracer FIRE 6</h1>
|
<h1>Tracer FIRE</h1>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="register.html">Register</a></li>
|
||||||
|
<li><a href="puzzles.html">Puzzles</a></li>
|
||||||
|
<li><a href="scoreboard.html">Scoreboard</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Getting Started</h2>
|
<h2>Getting Started</h2>
|
||||||
|
@ -42,7 +50,7 @@
|
||||||
<h2>Reading Material</h2>
|
<h2>Reading Material</h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Stuck? Need a break? In the bathroom?
|
Stuck? Taking a break?
|
||||||
Here are some things to read.
|
Here are some things to read.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -57,12 +65,5 @@
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<nav>
|
|
||||||
<ul>
|
|
||||||
<li><a href="register.html">Register</a></li>
|
|
||||||
<li><a href="puzzles.html">Puzzles</a></li>
|
|
||||||
<li><a href="scoreboard.html">Scoreboard</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,25 +1,28 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Team Registration</title>
|
<title>Team Registration</title>
|
||||||
<link rel="stylesheet" href="ctf.css" type="text/css">
|
<link rel="stylesheet" href="css/style.css" type="text/css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Team Registration</h1>
|
<h1>Team Registration</h1>
|
||||||
<p>
|
|
||||||
Before you can use a token, you must choose a team name.
|
<section>
|
||||||
You can only do this once per token,
|
<p>
|
||||||
so make sure it's the team name you actually want.
|
Before you can use a token, you must choose a team name.
|
||||||
Staff are unable to make changes to team names.
|
You can only do this once per token,
|
||||||
</p>
|
so make sure it's the team name you actually want.
|
||||||
<form method="get" action="register.cgi">
|
Staff are unable to make changes to team names.
|
||||||
<label>Team Hash (Token):</label>
|
</p>
|
||||||
<input type="text" name="h">
|
<form method="get" action="register.cgi">
|
||||||
<br>
|
<label>Team Hash (Token):</label>
|
||||||
<label>Team Name:</label>
|
<input type="text" name="h">
|
||||||
<input type="text" name="n">
|
<br>
|
||||||
<br>
|
<label>Team Name:</label>
|
||||||
<input type="submit" value="Register">
|
<input type="text" name="n">
|
||||||
</form>
|
<br>
|
||||||
</body>
|
<input type="submit" value="Register">
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
60
install
60
install
|
@ -1,6 +1,11 @@
|
||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
|
|
||||||
DESTDIR=${1:-/opt/koth}
|
DESTDIR=$1
|
||||||
|
|
||||||
|
if [ -z "$DESTDIR" ]; then
|
||||||
|
echo "Usage: $0 DESTDIR"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
cd $(dirname $0)
|
cd $(dirname $0)
|
||||||
|
|
||||||
|
@ -14,15 +19,6 @@ older () {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
html () {
|
|
||||||
target=$DESTDIR/${1%mdwn}html
|
|
||||||
if older $target $1 tmpl/*; then
|
|
||||||
echo "HTML $1"
|
|
||||||
mkdir -p $(dirname $target)
|
|
||||||
./tmpl/mdwntohtml < $1 > $target
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
copy () {
|
copy () {
|
||||||
target=$DESTDIR/$1
|
target=$DESTDIR/$1
|
||||||
if older $target $1; then
|
if older $target $1; then
|
||||||
|
@ -41,6 +37,41 @@ cc () {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cgi () {
|
||||||
|
target=$DESTDIR/www/$(basename $1 .c)
|
||||||
|
if older $target $@; then
|
||||||
|
mkdir -p $(dirname $target)
|
||||||
|
src=$1; shift
|
||||||
|
echo "CC $src"
|
||||||
|
gcc -Wall -Werror -o $target $@ src/common.c $src
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
echo "SETUP"
|
||||||
|
for i in points.new points.tmp teams; do
|
||||||
|
dir=$DESTDIR/state/$i
|
||||||
|
mkdir -p $dir
|
||||||
|
setfacl -m ${www}:rwx $dir
|
||||||
|
done
|
||||||
|
>> $DESTDIR/state/points.log
|
||||||
|
if ! [ -f $DESTDIR/assigned.txt ]; then
|
||||||
|
hd </dev/urandom | awk '{print $3 $4 $5 $6;}' | head -n 100 > assigned.txt
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
echo "Figuring out web user..."
|
||||||
|
for www in www-data http _; do
|
||||||
|
id $www && break
|
||||||
|
done
|
||||||
|
if [ $www = _ ]; then
|
||||||
|
echo "Unable to determine httpd user on this system. Dying."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p $DESTDIR || exit 1
|
||||||
|
|
||||||
git ls-files | while read fn; do
|
git ls-files | while read fn; do
|
||||||
case "$fn" in
|
case "$fn" in
|
||||||
install|.*)
|
install|.*)
|
||||||
|
@ -53,8 +84,13 @@ git ls-files | while read fn; do
|
||||||
bin/*)
|
bin/*)
|
||||||
copy $fn
|
copy $fn
|
||||||
;;
|
;;
|
||||||
src/*.cgi.c|src/pointscli.c)
|
src/common.c)
|
||||||
cc src/common.c $fn
|
;;
|
||||||
|
src/pointscli.c)
|
||||||
|
cc $fn src/common.c
|
||||||
|
;;
|
||||||
|
src/*.cgi.c)
|
||||||
|
cgi $fn
|
||||||
;;
|
;;
|
||||||
src/*.c)
|
src/*.c)
|
||||||
cc $fn
|
cc $fn
|
||||||
|
|
693
src/common.c
693
src/common.c
|
@ -12,9 +12,9 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#ifdef NODUMP
|
#ifdef NODUMP
|
||||||
# define DUMPf(fmt, args...)
|
#define DUMPf(fmt, args...)
|
||||||
#else
|
#else
|
||||||
# define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args)
|
#define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args)
|
||||||
#endif
|
#endif
|
||||||
#define DUMP() DUMPf("")
|
#define DUMP() DUMPf("")
|
||||||
#define DUMP_d(v) DUMPf("%s = %d", #v, v)
|
#define DUMP_d(v) DUMPf("%s = %d", #v, v)
|
||||||
|
@ -29,237 +29,230 @@
|
||||||
/*
|
/*
|
||||||
* CGI
|
* CGI
|
||||||
*/
|
*/
|
||||||
static int is_cgi = 0;
|
static int is_cgi = 0;
|
||||||
static char **argv = NULL;
|
static char **argv = NULL;
|
||||||
|
|
||||||
static int
|
static int
|
||||||
read_char_argv()
|
read_char_argv()
|
||||||
{
|
{
|
||||||
static int arg = 0;
|
static int arg = 0;
|
||||||
static char *p;
|
static char *p;
|
||||||
|
|
||||||
if (NULL == argv) {
|
if (NULL == argv) {
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 == arg) {
|
if (0 == arg) {
|
||||||
arg = 1;
|
arg = 1;
|
||||||
p = argv[1];
|
p = argv[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! p) {
|
if (!p) {
|
||||||
return EOF;
|
return EOF;
|
||||||
} else if (! *p) {
|
} else if (!*p) {
|
||||||
arg += 1;
|
arg += 1;
|
||||||
p = argv[arg];
|
p = argv[arg];
|
||||||
return '&';
|
return '&';
|
||||||
}
|
}
|
||||||
|
|
||||||
return *(p++);
|
return *(p++);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
read_char_stdin()
|
read_char_stdin()
|
||||||
{
|
{
|
||||||
static int inlen = -1;
|
static int inlen = -1;
|
||||||
|
|
||||||
if (-1 == inlen) {
|
if (-1 == inlen) {
|
||||||
char *p = getenv("CONTENT_LENGTH");
|
char *p = getenv("CONTENT_LENGTH");
|
||||||
if (p) {
|
if (p) {
|
||||||
inlen = atoi(p);
|
inlen = atoi(p);
|
||||||
if (inlen > POST_MAX) {
|
if (inlen > POST_MAX) {
|
||||||
inlen = POST_MAX;
|
inlen = POST_MAX;
|
||||||
}
|
}
|
||||||
if (inlen < 0) {
|
if (inlen < 0) {
|
||||||
inlen = 0;
|
inlen = 0;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
inlen = 0;
|
inlen = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inlen) {
|
if (inlen) {
|
||||||
inlen -= 1;
|
inlen -= 1;
|
||||||
return getchar();
|
return getchar();
|
||||||
}
|
}
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
read_char_query_string()
|
read_char_query_string()
|
||||||
{
|
{
|
||||||
static char *p = (char *)-1;
|
static char *p = (char *) -1;
|
||||||
|
|
||||||
if ((char *)-1 == p) {
|
if ((char *) -1 == p) {
|
||||||
p = getenv("QUERY_STRING");
|
p = getenv("QUERY_STRING");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! p) {
|
if (!p) {
|
||||||
return EOF;
|
return EOF;
|
||||||
} else if (! *p) {
|
} else if (!*p) {
|
||||||
return EOF;
|
return EOF;
|
||||||
} else {
|
} else {
|
||||||
return *(p++);
|
return *(p++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int (* read_char)() = read_char_argv;
|
static int (*read_char) () = read_char_argv;
|
||||||
|
|
||||||
int
|
int
|
||||||
cgi_init(char *global_argv[])
|
cgi_init(char *global_argv[])
|
||||||
{
|
{
|
||||||
char *rm = getenv("REQUEST_METHOD");
|
char *rm = getenv("REQUEST_METHOD");
|
||||||
|
|
||||||
if (! rm) {
|
if (!rm) {
|
||||||
read_char = read_char_argv;
|
read_char = read_char_argv;
|
||||||
argv = global_argv;
|
argv = global_argv;
|
||||||
} else if (0 == strcmp(rm, "POST")) {
|
} else if (0 == strcmp(rm, "POST")) {
|
||||||
read_char = read_char_stdin;
|
read_char = read_char_stdin;
|
||||||
is_cgi = 1;
|
is_cgi = 1;
|
||||||
} else if (0 == strcmp(rm, "GET")) {
|
} else if (0 == strcmp(rm, "GET")) {
|
||||||
read_char = read_char_query_string;
|
read_char = read_char_query_string;
|
||||||
is_cgi = 1;
|
is_cgi = 1;
|
||||||
} else {
|
} else {
|
||||||
printf(("405 Method not allowed\r\n"
|
printf(("405 Method not allowed\r\n"
|
||||||
"Allow: GET, POST\r\n"
|
"Allow: GET, POST\r\n" "Content-type: text/plain\r\n" "\r\n" "%s is not allowed.\n"), rm);
|
||||||
"Content-type: text/plain\r\n"
|
return -1;
|
||||||
"\r\n"
|
}
|
||||||
"%s is not allowed.\n"),
|
|
||||||
rm);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char
|
static char
|
||||||
tonum(int c)
|
tonum(int c)
|
||||||
{
|
{
|
||||||
if ((c >= '0') && (c <= '9')) {
|
if ((c >= '0') && (c <= '9')) {
|
||||||
return c - '0';
|
return c - '0';
|
||||||
}
|
}
|
||||||
if ((c >= 'a') && (c <= 'f')) {
|
if ((c >= 'a') && (c <= 'f')) {
|
||||||
return 10 + c - 'a';
|
return 10 + c - 'a';
|
||||||
}
|
}
|
||||||
if ((c >= 'A') && (c <= 'F')) {
|
if ((c >= 'A') && (c <= 'F')) {
|
||||||
return 10 + c - 'A';
|
return 10 + c - 'A';
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char
|
static char
|
||||||
read_hex()
|
read_hex()
|
||||||
{
|
{
|
||||||
int a = read_char();
|
int a = read_char();
|
||||||
int b = read_char();
|
int b = read_char();
|
||||||
|
|
||||||
return tonum(a)*16 + tonum(b);
|
return tonum(a) * 16 + tonum(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read a key or a value. Since & and = aren't supposed to appear
|
/*
|
||||||
outside of boundaries, we can use the same function for both.
|
* Read a key or a value. Since & and = aren't supposed to appear outside of boundaries, we can use the same function for both.
|
||||||
*/
|
*/
|
||||||
size_t
|
size_t
|
||||||
cgi_item(char *str, size_t maxlen)
|
cgi_item(char *str, size_t maxlen)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
c = read_char();
|
c = read_char();
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case EOF:
|
case EOF:
|
||||||
case '=':
|
case '=':
|
||||||
case '&':
|
case '&':
|
||||||
str[pos] = '\0';
|
str[pos] = '\0';
|
||||||
return pos;
|
return pos;
|
||||||
case '%':
|
case '%':
|
||||||
c = read_hex();
|
c = read_hex();
|
||||||
break;
|
break;
|
||||||
case '+':
|
case '+':
|
||||||
c = ' ';
|
c = ' ';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (pos < maxlen - 1) {
|
if (pos < maxlen - 1) {
|
||||||
str[pos] = c;
|
str[pos] = c;
|
||||||
pos += 1;
|
pos += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cgi_head(char *title)
|
cgi_head(char *title)
|
||||||
{
|
{
|
||||||
if (is_cgi) {
|
if (is_cgi) {
|
||||||
printf("Content-type: text/html\r\n\r\n");
|
printf("Content-type: text/html\r\n\r\n");
|
||||||
}
|
}
|
||||||
printf(("<!DOCTYPE html>\n"
|
printf(("<!DOCTYPE html>\n"
|
||||||
"<html>\n"
|
"<html>\n"
|
||||||
" <head>\n"
|
" <head>\n"
|
||||||
" <title>%s</title>\n"
|
" <title>%s</title>\n"
|
||||||
" <link rel=\"stylesheet\" href=\"ctf.css\" type=\"text/css\">\n"
|
" <link rel=\"stylesheet\" href=\"css/style.css\">\n"
|
||||||
" </head>\n"
|
" </head>\n"
|
||||||
" <body>\n"
|
" <body><h1>%s</h1><section>\n"), title, title);
|
||||||
" <h1>%s</h1>\n"),
|
|
||||||
title, title);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cgi_foot()
|
cgi_foot()
|
||||||
{
|
{
|
||||||
printf("\n"
|
printf("\n</section></body></html>\n");
|
||||||
" </body>\n"
|
|
||||||
"</html>\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cgi_result(int code, char *desc, char *fmt, ...)
|
cgi_result(int code, char *desc, char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
if (is_cgi) {
|
if (is_cgi) {
|
||||||
printf("Status: %d %s\r\n", code, desc);
|
printf("Status: %d %s\r\n", code, desc);
|
||||||
}
|
}
|
||||||
cgi_head(desc);
|
cgi_head(desc);
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vprintf(fmt, ap);
|
vprintf(fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
cgi_foot();
|
cgi_foot();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cgi_fail(int err)
|
cgi_fail(int err)
|
||||||
{
|
{
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case ERR_GENERAL:
|
case ERR_GENERAL:
|
||||||
cgi_result(500, "Points not awarded", "<p>The server is unable to award your points at this time.</p>");
|
cgi_result(500, "Points not awarded", "<p>The server is unable to award your points at this time.</p>");
|
||||||
case ERR_NOTEAM:
|
case ERR_NOTEAM:
|
||||||
cgi_result(409, "No such team", "<p>There is no team with that hash.</p>");
|
cgi_result(409, "No such team", "<p>There is no team with that hash.</p>");
|
||||||
case ERR_CLAIMED:
|
case ERR_CLAIMED:
|
||||||
cgi_result(409, "Already claimed", "<p>That is the correct answer, but your team has already claimed these points.</p>");
|
cgi_result(409, "Already claimed",
|
||||||
default:
|
"<p>That is the correct answer, but your team has already claimed these points.</p>");
|
||||||
cgi_result(409, "Failure", "<p>Failure code: %d</p>", err);
|
default:
|
||||||
}
|
cgi_result(409, "Failure", "<p>Failure code: %d</p>", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cgi_page(char *title, char *fmt, ...)
|
cgi_page(char *title, char *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
cgi_head(title);
|
cgi_head(title);
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vprintf(fmt, ap);
|
vprintf(fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
cgi_foot();
|
cgi_foot();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cgi_error(char *text)
|
cgi_error(char *text)
|
||||||
{
|
{
|
||||||
cgi_result(500, "Internal error", "<p>%s</p>", text);
|
cgi_result(500, "Internal error", "<p>%s</p>", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -267,276 +260,278 @@ cgi_error(char *text)
|
||||||
* Common routines
|
* Common routines
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* cut -d$ANCHOR -f2- | grep -Fx "$NEEDLE" */
|
/*
|
||||||
|
* cut -d$ANCHOR -f2- | grep -Fx "$NEEDLE"
|
||||||
|
*/
|
||||||
int
|
int
|
||||||
anchored_search(char const *filename, char const *needle, char const anchor)
|
anchored_search(char const *filename, char const *needle, char const anchor)
|
||||||
{
|
{
|
||||||
FILE *f = fopen(filename, "r");
|
FILE *f = fopen(filename, "r");
|
||||||
size_t nlen = strlen(needle);
|
size_t nlen = strlen(needle);
|
||||||
char line[1024];
|
char line[1024];
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
while (f) {
|
while (f) {
|
||||||
char *p;
|
char *p;
|
||||||
|
|
||||||
if (NULL == fgets(line, sizeof line, f)) {
|
if (NULL == fgets(line, sizeof line, f)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find anchor */
|
/*
|
||||||
if (anchor) {
|
* Find anchor
|
||||||
p = strchr(line, anchor);
|
*/
|
||||||
if (! p) {
|
if (anchor) {
|
||||||
continue;
|
p = strchr(line, anchor);
|
||||||
}
|
if (!p) {
|
||||||
p += 1;
|
continue;
|
||||||
} else {
|
}
|
||||||
p = line;
|
p += 1;
|
||||||
}
|
} else {
|
||||||
|
p = line;
|
||||||
|
}
|
||||||
|
|
||||||
/* Don't bother with strcmp if string lengths differ.
|
/*
|
||||||
If this string is shorter than the previous, it's okay. This is
|
* Don't bother with strcmp if string lengths differ. If this string is shorter than the previous, it's okay. This
|
||||||
just a performance hack.
|
* is just a performance hack.
|
||||||
*/
|
*/
|
||||||
if ((p[nlen] != '\n') &&
|
if ((p[nlen] != '\n') && (p[nlen] != '\0')) {
|
||||||
(p[nlen] != '\0')) {
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
p[nlen] = 0;
|
||||||
p[nlen] = 0;
|
|
||||||
|
|
||||||
/* Okay, now we can compare! */
|
/*
|
||||||
if (0 == strcmp(p, needle)) {
|
* Okay, now we can compare!
|
||||||
ret = 1;
|
*/
|
||||||
break;
|
if (0 == strcmp(p, needle)) {
|
||||||
}
|
ret = 1;
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (f) {
|
if (f) {
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
urandom(char *buf, size_t buflen)
|
urandom(char *buf, size_t buflen)
|
||||||
{
|
{
|
||||||
static int fd = -2;
|
static int fd = -2;
|
||||||
|
|
||||||
if (-2 == fd) {
|
if (-2 == fd) {
|
||||||
srandom(time(NULL) * getpid());
|
srandom(time(NULL) * getpid());
|
||||||
fd = open("/dev/urandom", O_RDONLY);
|
fd = open("/dev/urandom", O_RDONLY);
|
||||||
}
|
}
|
||||||
if (-1 != fd) {
|
if (-1 != fd) {
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
len = read(fd, buf, buflen);
|
len = read(fd, buf, buflen);
|
||||||
if (len == buflen) {
|
if (len == buflen) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fall back to libc's crappy thing */
|
/*
|
||||||
{
|
* Fall back to libc's crappy thing
|
||||||
int i;
|
*/
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < buflen; i += 1) {
|
for (i = 0; i < buflen; i += 1) {
|
||||||
buf[i] = (char)random();
|
buf[i] = (char) random();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
my_snprintf(char *buf, size_t buflen, char *fmt, ...)
|
my_snprintf(char *buf, size_t buflen, char *fmt, ...)
|
||||||
{
|
{
|
||||||
int len;
|
int len;
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
len = vsnprintf(buf, buflen - 1, fmt, ap);
|
len = vsnprintf(buf, buflen - 1, fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
buf[buflen - 1] = '\0';
|
buf[buflen - 1] = '\0';
|
||||||
if (len >= buflen) {
|
if (len >= buflen) {
|
||||||
return buflen - 1;
|
return buflen - 1;
|
||||||
} else {
|
} else {
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ctf_chdir()
|
ctf_chdir()
|
||||||
{
|
{
|
||||||
static int initialized = 0;
|
static int initialized = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (initialized) {
|
if (initialized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
initialized = 1;
|
initialized = 1;
|
||||||
|
|
||||||
/* chdir to $CTF_BASE */
|
/*
|
||||||
{
|
* chdir to $CTF_BASE
|
||||||
char const *ctf_base = getenv("CTF_BASE");
|
*/
|
||||||
|
{
|
||||||
|
char const *ctf_base = getenv("CTF_BASE");
|
||||||
|
|
||||||
if (ctf_base) {
|
if (ctf_base) {
|
||||||
chdir(ctf_base);
|
chdir(ctf_base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Keep going up one directory until there's a packages directory */
|
/*
|
||||||
for (i = 0; i < 5; i += 1) {
|
* Keep going up one directory until there's a packages directory
|
||||||
struct stat st;
|
*/
|
||||||
|
for (i = 0; i < 5; i += 1) {
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
if ((0 == stat("packages", &st)) &&
|
if ((0 == stat("packages", &st)) && S_ISDIR(st.st_mode)) {
|
||||||
S_ISDIR(st.st_mode)) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
chdir("..");
|
||||||
chdir("..");
|
}
|
||||||
}
|
fprintf(stderr, "Can not determine CTF_BASE directory: exiting.\n");
|
||||||
fprintf(stderr, "Can not determine CTF_BASE directory: exiting.\n");
|
exit(66);
|
||||||
exit(66);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
mkpath(char const *type, char const *fmt, va_list ap)
|
mkpath(char const *type, char const *fmt, va_list ap)
|
||||||
{
|
{
|
||||||
char relpath[PATH_MAX];
|
char relpath[PATH_MAX];
|
||||||
static char path[PATH_MAX];
|
static char path[PATH_MAX];
|
||||||
|
|
||||||
ctf_chdir();
|
ctf_chdir();
|
||||||
vsnprintf(relpath, sizeof(relpath) - 1, fmt, ap);
|
vsnprintf(relpath, sizeof(relpath) - 1, fmt, ap);
|
||||||
relpath[sizeof(relpath) - 1] = '\0';
|
relpath[sizeof(relpath) - 1] = '\0';
|
||||||
|
|
||||||
/* $CTF_BASE/type/relpath */
|
/*
|
||||||
my_snprintf(path, sizeof(path), "%s/%s", type, relpath);
|
* $CTF_BASE/type/relpath
|
||||||
return path;
|
*/
|
||||||
|
my_snprintf(path, sizeof(path), "%s/%s", type, relpath);
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
state_path(char const *fmt, ...)
|
state_path(char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char *ret;
|
char *ret;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
ret = mkpath("state", fmt, ap);
|
ret = mkpath("state", fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
package_path(char const *fmt, ...)
|
package_path(char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
char *ret;
|
char *ret;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
ret = mkpath("packages", fmt, ap);
|
ret = mkpath("packages", fmt, ap);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
team_exists(char const *teamhash)
|
team_exists(char const *teamhash)
|
||||||
{
|
{
|
||||||
struct stat buf;
|
struct stat buf;
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if ((! teamhash) || (! *teamhash)) {
|
if ((!teamhash) || (!*teamhash)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check for invalid characters. */
|
/*
|
||||||
for (i = 0; teamhash[i]; i += 1) {
|
* Check for invalid characters.
|
||||||
if (! isalnum(teamhash[i])) {
|
*/
|
||||||
return 0;
|
for (i = 0; teamhash[i]; i += 1) {
|
||||||
}
|
if (!isalnum(teamhash[i])) {
|
||||||
}
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* stat seems to be the preferred way to check for existence. */
|
/*
|
||||||
ret = stat(state_path("teams/names/%s", teamhash), &buf);
|
* stat seems to be the preferred way to check for existence.
|
||||||
if (-1 == ret) {
|
*/
|
||||||
return 0;
|
ret = stat(state_path("teams/names/%s", teamhash), &buf);
|
||||||
}
|
if (-1 == ret) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return values:
|
/*
|
||||||
-1: general error
|
* Return values: -1: general error -2: no such team -3: points already awarded
|
||||||
-2: no such team
|
|
||||||
-3: points already awarded
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
award_points(char const *teamhash,
|
award_points(char const *teamhash, char const *category, const long points, char const *uid)
|
||||||
char const *category,
|
|
||||||
const long points,
|
|
||||||
char const *uid)
|
|
||||||
{
|
{
|
||||||
char line[100];
|
char line[100];
|
||||||
int linelen;
|
int linelen;
|
||||||
char *filename;
|
char *filename;
|
||||||
FILE *f;
|
FILE *f;
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
|
|
||||||
if (! team_exists(teamhash)) {
|
if (!team_exists(teamhash)) {
|
||||||
return ERR_NOTEAM;
|
return ERR_NOTEAM;
|
||||||
}
|
}
|
||||||
|
|
||||||
linelen = snprintf(line, sizeof(line),
|
linelen = snprintf(line, sizeof(line), "%s %s %ld %s", teamhash, category, points, uid);
|
||||||
"%s %s %ld %s",
|
if (sizeof(line) <= linelen) {
|
||||||
teamhash, category, points, uid);
|
return ERR_GENERAL;
|
||||||
if (sizeof(line) <= linelen) {
|
}
|
||||||
return ERR_GENERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (anchored_search(state_path("points.log"), line, ' ')) {
|
if (anchored_search(state_path("points.log"), line, ' ')) {
|
||||||
return ERR_CLAIMED;
|
return ERR_CLAIMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* At one time I had this writing to a single log file, using lockf.
|
/*
|
||||||
This works, as long as nobody ever tries to edit the log file.
|
* At one time I had this writing to a single log file, using lockf. This works, as long as nobody ever tries to edit the
|
||||||
Editing the log file would require locking it, which would block
|
* log file. Editing the log file would require locking it, which would block everything trying to score, effectively
|
||||||
everything trying to score, effectively taking down the entire
|
* taking down the entire contest. If you can't lock it first--and nothing in busybox lets you do this--you have to bring
|
||||||
contest. If you can't lock it first--and nothing in busybox lets
|
* down pretty much everything manually anyway.
|
||||||
you do this--you have to bring down pretty much everything manually
|
*
|
||||||
anyway.
|
* By putting new scores into new files and periodically appending those files to the main log file, it is possible to stop
|
||||||
|
* the thing that appends, edit the file at leisure, and then start the appender back up, all without affecting things
|
||||||
|
* trying to score: they're still able to record their score and move on.
|
||||||
|
*/
|
||||||
|
|
||||||
By putting new scores into new files and periodically appending
|
filename = state_path("points.tmp/%lu.%d.%s.%s.%ld", (unsigned long) now, getpid(), teamhash, category, points);
|
||||||
those files to the main log file, it is possible to stop the thing
|
f = fopen(filename, "w");
|
||||||
that appends, edit the file at leisure, and then start the appender
|
if (!f) {
|
||||||
back up, all without affecting things trying to score: they're
|
return ERR_GENERAL;
|
||||||
still able to record their score and move on.
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
filename = state_path("points.tmp/%lu.%d.%s.%s.%ld",
|
if (EOF == fprintf(f, "%lu %s\n", (unsigned long) now, line)) {
|
||||||
(unsigned long)now, getpid(),
|
return ERR_GENERAL;
|
||||||
teamhash, category, points);
|
}
|
||||||
f = fopen(filename, "w");
|
|
||||||
if (! f) {
|
|
||||||
return ERR_GENERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EOF == fprintf(f, "%lu %s\n", (unsigned long)now, line)) {
|
fclose(f);
|
||||||
return ERR_GENERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
/*
|
||||||
|
* Rename into points.new
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
char ofn[PATH_MAX];
|
||||||
|
|
||||||
/* Rename into points.new */
|
strncpy(ofn, filename, sizeof(ofn));
|
||||||
{
|
filename = state_path("points.new/%lu.%d.%s.%s.%ld", (unsigned long) now, getpid(), teamhash, category, points);
|
||||||
char ofn[PATH_MAX];
|
rename(ofn, filename);
|
||||||
|
}
|
||||||
|
|
||||||
strncpy(ofn, filename, sizeof(ofn));
|
return 0;
|
||||||
filename = state_path("points.new/%lu.%d.%s.%s.%ld",
|
|
||||||
(unsigned long)now, getpid(),
|
|
||||||
teamhash, category, points);
|
|
||||||
rename(ofn, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,42 +7,41 @@
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int points;
|
int points;
|
||||||
int ret;
|
int ret;
|
||||||
char comment[512];
|
char comment[512];
|
||||||
|
|
||||||
if (argc != 5) {
|
if (argc != 5) {
|
||||||
fprintf(stderr,
|
fprintf(stderr, "Usage: pointscli TEAM CATEGORY POINTS 'COMMENT'\n");
|
||||||
"Usage: pointscli TEAM CATEGORY POINTS 'COMMENT'\n");
|
return EX_USAGE;
|
||||||
return EX_USAGE;
|
}
|
||||||
}
|
ctf_chdir();
|
||||||
ctf_chdir();
|
|
||||||
|
|
||||||
points = atoi(argv[3]);
|
points = atoi(argv[3]);
|
||||||
if (0 == points) {
|
if (0 == points) {
|
||||||
fprintf(stderr, "Error: award 0 points?\n");
|
fprintf(stderr, "Error: award 0 points?\n");
|
||||||
return EX_USAGE;
|
return EX_USAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(comment, sizeof comment, "--%s", argv[4]);
|
snprintf(comment, sizeof comment, "--%s", argv[4]);
|
||||||
|
|
||||||
ret = award_points(argv[1], argv[2], points, comment);
|
ret = award_points(argv[1], argv[2], points, comment);
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case 0:
|
case 0:
|
||||||
return 0;
|
return 0;
|
||||||
case ERR_GENERAL:
|
case ERR_GENERAL:
|
||||||
perror("General error");
|
perror("General error");
|
||||||
return EX_UNAVAILABLE;
|
return EX_UNAVAILABLE;
|
||||||
case ERR_NOTEAM:
|
case ERR_NOTEAM:
|
||||||
fprintf(stderr, "No such team\n");
|
fprintf(stderr, "No such team\n");
|
||||||
return EX_NOUSER;
|
return EX_NOUSER;
|
||||||
case ERR_CLAIMED:
|
case ERR_CLAIMED:
|
||||||
fprintf(stderr, "Duplicate entry\n");
|
fprintf(stderr, "Duplicate entry\n");
|
||||||
return EX_DATAERR;
|
return EX_DATAERR;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Error %d\n", ret);
|
fprintf(stderr, "Error %d\n", ret);
|
||||||
return EX_SOFTWARE;
|
return EX_SOFTWARE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue