mirror of https://github.com/dirtbags/moth.git
Lua-based puzzle lister
This commit is contained in:
parent
e7e4e1745d
commit
d50dea280f
9
TODO
9
TODO
|
@ -0,0 +1,9 @@
|
||||||
|
* Scoreboard refresh
|
||||||
|
|
||||||
|
Test:
|
||||||
|
|
||||||
|
* awarding points
|
||||||
|
* points already awarded
|
||||||
|
* bad team hash
|
||||||
|
* category doesn't exist
|
||||||
|
* puzzle doesn't exist
|
|
@ -0,0 +1,16 @@
|
||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
if [ $# -lt 3 ] || [ $# -gt 4 ]; then
|
||||||
|
echo "Usage: $0 TEAM CATEGORY POINTS [COMMENT]"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $(dirname $0)/../state
|
||||||
|
|
||||||
|
if grep -q "^[0-9]* $1 $2 $3 $4" points.log; then
|
||||||
|
echo "Points already awarded"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
now=$(date +%s)
|
||||||
|
echo "$now $1 $2 $3 cli:$4" > points.new/$now.$$
|
4
bin/once
4
bin/once
|
@ -16,14 +16,14 @@ fi
|
||||||
# Collect new points
|
# Collect new points
|
||||||
find state/points.new -type f | while read fn; do
|
find state/points.new -type f | while read fn; do
|
||||||
# Skip partially-written files
|
# Skip partially-written files
|
||||||
[ $(wc -l $fn) -eq 1 ] || continue
|
[ $(wc -l < $fn) -eq 1 ] || continue
|
||||||
|
|
||||||
cat $fn >> state/points.log
|
cat $fn >> state/points.log
|
||||||
rm $fn
|
rm $fn
|
||||||
done
|
done
|
||||||
|
|
||||||
# Generate new puzzles.html
|
# Generate new puzzles.html
|
||||||
if www/puzzles.cgi > www/puzzles.new; then
|
if bin/puzzles > www/puzzles.new; then
|
||||||
mv www/puzzles.new www/puzzles.html
|
mv www/puzzles.new www/puzzles.html
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
#! /usr/bin/lua
|
||||||
|
|
||||||
|
package.path = "cgi/?.lua"
|
||||||
|
local koth = require "koth"
|
||||||
|
|
||||||
|
local max_by_cat = {}
|
||||||
|
|
||||||
|
local f = io.popen("ls packages")
|
||||||
|
for cat in f:lines() do
|
||||||
|
max_by_cat[cat] = 0
|
||||||
|
end
|
||||||
|
f:close()
|
||||||
|
|
||||||
|
|
||||||
|
for line in io.lines("state/points.log") do
|
||||||
|
local ts, team, cat, points, comment = line:match("^(%d+) (%w+) (%w+) (%d+) ?(.*)")
|
||||||
|
points = tonumber(points) or 0
|
||||||
|
|
||||||
|
-- Skip scores for removed categories
|
||||||
|
if (max_by_cat[cat] ~= nil) then
|
||||||
|
max_by_cat[cat] = math.max(max_by_cat[cat], points)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local body = "<dl>\n"
|
||||||
|
for cat, biggest in pairs(max_by_cat) do
|
||||||
|
local points, dirname
|
||||||
|
|
||||||
|
body = body .. "<dt>" .. cat .. "</dt>"
|
||||||
|
body = body .. "<dd>"
|
||||||
|
for line in io.lines("packages/" .. cat .. "/map.txt") do
|
||||||
|
points, dirname = line:match("^(%d+) (.*)")
|
||||||
|
points = tonumber(points)
|
||||||
|
|
||||||
|
body = body .. "<a href=\"" .. cat .. "/" .. dirname .. "/index.html\">" .. points .. "</a> "
|
||||||
|
if (points > biggest) then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if (points == biggest) then
|
||||||
|
body = body .. "<span title=\"Category Complete\">⁂</span>"
|
||||||
|
end
|
||||||
|
body = body .. "</dd>\n"
|
||||||
|
end
|
||||||
|
body = body .. "</dl>\n"
|
||||||
|
|
||||||
|
koth.page("Open Puzzles", body)
|
|
@ -45,7 +45,7 @@ end
|
||||||
-- We're going to rely on `bin/once` only processing files with the right number of lines.
|
-- We're going to rely on `bin/once` only processing files with the right number of lines.
|
||||||
--
|
--
|
||||||
function koth.award_points(team, category, points, comment)
|
function koth.award_points(team, category, points, comment)
|
||||||
local filename = team .. "." .. category .. "." points
|
local filename = team .. "." .. category .. "." .. points
|
||||||
local entry = team .. " " .. category .. " " .. points
|
local entry = team .. " " .. category .. " " .. points
|
||||||
|
|
||||||
if (comment) then
|
if (comment) then
|
||||||
|
|
33
install
33
install
|
@ -28,15 +28,6 @@ copy () {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
cc () {
|
|
||||||
target=$DESTDIR/bin/$(basename $1 .c)
|
|
||||||
if older $target $@; then
|
|
||||||
src=$1; shift
|
|
||||||
echo "CC $src"
|
|
||||||
gcc -Wall -Werror -o $target $@ $src
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
web () {
|
web () {
|
||||||
target=$DESTDIR/www/${1#*/}
|
target=$DESTDIR/www/${1#*/}
|
||||||
if older $target $1; then
|
if older $target $1; then
|
||||||
|
@ -46,16 +37,6 @@ web () {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
cgi () {
|
|
||||||
target=$DESTDIR/www/$(basename $1 .c)
|
|
||||||
if older $target src/common.c $@; then
|
|
||||||
mkdir -p $(dirname $target)
|
|
||||||
src=$1; shift
|
|
||||||
echo "CGI $src"
|
|
||||||
gcc -Wall -Werror -o $target $@ src/common.c $src
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
[ -d $DESTDIR/state ] && return
|
[ -d $DESTDIR/state ] && return
|
||||||
echo "SETUP"
|
echo "SETUP"
|
||||||
|
@ -96,18 +77,8 @@ git ls-files | while read fn; do
|
||||||
bin/*)
|
bin/*)
|
||||||
copy $fn
|
copy $fn
|
||||||
;;
|
;;
|
||||||
src/common.c)
|
cgi/*)
|
||||||
;;
|
copy $fn
|
||||||
src/*.h)
|
|
||||||
;;
|
|
||||||
src/pointscli.c)
|
|
||||||
cc $fn src/common.c
|
|
||||||
;;
|
|
||||||
src/*.cgi.c)
|
|
||||||
cgi $fn
|
|
||||||
;;
|
|
||||||
src/*.c)
|
|
||||||
cc $fn
|
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "??? $fn"
|
echo "??? $fn"
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <sysexits.h>
|
|
||||||
|
|
||||||
/** Compute bubble babble for input buffer.
|
|
||||||
*
|
|
||||||
* The generated output will be of length 6*((inlen/2)+1), including the
|
|
||||||
* trailing NULL.
|
|
||||||
*
|
|
||||||
* Test vectors:
|
|
||||||
* `' (empty string) `xexax'
|
|
||||||
* `1234567890' `xesef-disof-gytuf-katof-movif-baxux'
|
|
||||||
* `Pineapple' `xigak-nyryk-humil-bosek-sonax'
|
|
||||||
*/
|
|
||||||
static char const consonants[] = "bcdfghklmnprstvz";
|
|
||||||
static char const vowels[] = "aeiouy";
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int seed = 1;
|
|
||||||
|
|
||||||
putchar('x');
|
|
||||||
while (1) {
|
|
||||||
int c;
|
|
||||||
|
|
||||||
c = getchar();
|
|
||||||
if (EOF == c) {
|
|
||||||
putchar(vowels[seed % 6]);
|
|
||||||
putchar('x');
|
|
||||||
putchar(vowels[seed / 6]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
putchar(vowels[(((c >> 6) & 3) + seed) % 6]);
|
|
||||||
putchar(consonants[(c >> 2) & 15]);
|
|
||||||
putchar(vowels[((c & 3) + (seed / 6)) % 6]);
|
|
||||||
|
|
||||||
seed = (seed * 5) + (c * 7);
|
|
||||||
c = getchar();
|
|
||||||
seed = (seed + c) % 36;
|
|
||||||
|
|
||||||
if (EOF == c) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
putchar(consonants[(c >> 4) & 15]);
|
|
||||||
putchar('-');
|
|
||||||
putchar(consonants[c & 15]);
|
|
||||||
}
|
|
||||||
|
|
||||||
putchar('x');
|
|
||||||
putchar('\n');
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,90 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
char team[TEAM_MAX] = {0};
|
|
||||||
char token[TOKEN_MAX] = {0};
|
|
||||||
|
|
||||||
if (-1 == cgi_init(argv)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
ctf_chdir();
|
|
||||||
|
|
||||||
/* Read in team and token */
|
|
||||||
while (1) {
|
|
||||||
size_t len;
|
|
||||||
char key[20];
|
|
||||||
|
|
||||||
len = cgi_item(key, sizeof(key));
|
|
||||||
if (0 == len) break;
|
|
||||||
switch (key[0]) {
|
|
||||||
case 't':
|
|
||||||
cgi_item(team, sizeof(team));
|
|
||||||
break;
|
|
||||||
case 'k':
|
|
||||||
cgi_item(token, sizeof(token));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Any weird characters in token name? */
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
if ('\0' == token[0]) {
|
|
||||||
cgi_result(409, "Must supply token", "<p>Your request did not contain a k= parameter.</p>");
|
|
||||||
}
|
|
||||||
for (p = token; *p; p += 1) {
|
|
||||||
if ((! isalnum(*p)) &&
|
|
||||||
(*p != '-') &&
|
|
||||||
(*p != ':')) {
|
|
||||||
cgi_result(409, "Not a token", "<p>This token has untokenlike characteristics.</p>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Award points */
|
|
||||||
{
|
|
||||||
char *p = token;
|
|
||||||
char *q;
|
|
||||||
char category[40];
|
|
||||||
char points_s[40];
|
|
||||||
int points;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* Pull category name out of the token */
|
|
||||||
for (q = category; *p && (*p != ':'); p += 1) {
|
|
||||||
*p = tolower(*p);
|
|
||||||
*(q++) = *p;
|
|
||||||
}
|
|
||||||
*q = '\0';
|
|
||||||
if (p) p += 1;
|
|
||||||
|
|
||||||
/* Now we can check to see if it's valid */
|
|
||||||
if ((! anchored_search(package_path("%s/tokens.txt", category), token, 0)) &&
|
|
||||||
(! anchored_search(state_path("tokens.db"), token, 0))) {
|
|
||||||
cgi_result(409, "No such token", "<p>This token has not been issued.</p>");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pull point value out of the token (if it has one) */
|
|
||||||
for (q = points_s; *p && (*p != ':'); p += 1) {
|
|
||||||
*(q++) = *p;
|
|
||||||
}
|
|
||||||
*q = '\0';
|
|
||||||
points = atoi(points_s);
|
|
||||||
if (0 == points) points = 1;
|
|
||||||
|
|
||||||
ret = award_points(team, category, points, token);
|
|
||||||
if (ret < 0) {
|
|
||||||
cgi_fail(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cgi_page("Point awarded", "<p>Congratulations.</p>");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
549
src/common.c
549
src/common.c
|
@ -1,549 +0,0 @@
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <values.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#ifdef NODUMP
|
|
||||||
#define DUMPf(fmt, args...)
|
|
||||||
#else
|
|
||||||
#define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args)
|
|
||||||
#endif
|
|
||||||
#define DUMP() DUMPf("")
|
|
||||||
#define DUMP_d(v) DUMPf("%s = %d", #v, v)
|
|
||||||
#define DUMP_x(v) DUMPf("%s = 0x%x", #v, v)
|
|
||||||
#define DUMP_s(v) DUMPf("%s = %s", #v, v)
|
|
||||||
#define DUMP_c(v) DUMPf("%s = '%c' (0x%02x)", #v, v, v)
|
|
||||||
#define DUMP_p(v) DUMPf("%s = %p", #v, v)
|
|
||||||
|
|
||||||
|
|
||||||
#define POST_MAX 1024
|
|
||||||
|
|
||||||
/*
|
|
||||||
* CGI
|
|
||||||
*/
|
|
||||||
static int is_cgi = 0;
|
|
||||||
static char **argv = NULL;
|
|
||||||
|
|
||||||
static int
|
|
||||||
read_char_argv()
|
|
||||||
{
|
|
||||||
static int arg = 0;
|
|
||||||
static char *p;
|
|
||||||
|
|
||||||
if (NULL == argv) {
|
|
||||||
return EOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (0 == arg) {
|
|
||||||
arg = 1;
|
|
||||||
p = argv[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p) {
|
|
||||||
return EOF;
|
|
||||||
} else if (!*p) {
|
|
||||||
arg += 1;
|
|
||||||
p = argv[arg];
|
|
||||||
return '&';
|
|
||||||
}
|
|
||||||
|
|
||||||
return *(p++);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
read_char_stdin()
|
|
||||||
{
|
|
||||||
static int inlen = -1;
|
|
||||||
|
|
||||||
if (-1 == inlen) {
|
|
||||||
char *p = getenv("CONTENT_LENGTH");
|
|
||||||
if (p) {
|
|
||||||
inlen = atoi(p);
|
|
||||||
if (inlen > POST_MAX) {
|
|
||||||
inlen = POST_MAX;
|
|
||||||
}
|
|
||||||
if (inlen < 0) {
|
|
||||||
inlen = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
inlen = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inlen) {
|
|
||||||
inlen -= 1;
|
|
||||||
return getchar();
|
|
||||||
}
|
|
||||||
return EOF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
read_char_query_string()
|
|
||||||
{
|
|
||||||
static char *p = (char *) -1;
|
|
||||||
|
|
||||||
if ((char *) -1 == p) {
|
|
||||||
p = getenv("QUERY_STRING");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!p) {
|
|
||||||
return EOF;
|
|
||||||
} else if (!*p) {
|
|
||||||
return EOF;
|
|
||||||
} else {
|
|
||||||
return *(p++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int (*read_char) () = read_char_argv;
|
|
||||||
|
|
||||||
int
|
|
||||||
cgi_init(char *global_argv[])
|
|
||||||
{
|
|
||||||
char *rm = getenv("REQUEST_METHOD");
|
|
||||||
|
|
||||||
if (!rm) {
|
|
||||||
read_char = read_char_argv;
|
|
||||||
argv = global_argv;
|
|
||||||
} else if (0 == strcmp(rm, "POST")) {
|
|
||||||
read_char = read_char_stdin;
|
|
||||||
is_cgi = 1;
|
|
||||||
} else if (0 == strcmp(rm, "GET")) {
|
|
||||||
read_char = read_char_query_string;
|
|
||||||
is_cgi = 1;
|
|
||||||
} else {
|
|
||||||
printf(("405 Method not allowed\r\n"
|
|
||||||
"Allow: GET, POST\r\n" "Content-type: text/plain\r\n" "\r\n" "%s is not allowed.\n"), rm);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char
|
|
||||||
tonum(int c)
|
|
||||||
{
|
|
||||||
if ((c >= '0') && (c <= '9')) {
|
|
||||||
return c - '0';
|
|
||||||
}
|
|
||||||
if ((c >= 'a') && (c <= 'f')) {
|
|
||||||
return 10 + c - 'a';
|
|
||||||
}
|
|
||||||
if ((c >= 'A') && (c <= 'F')) {
|
|
||||||
return 10 + c - 'A';
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char
|
|
||||||
read_hex()
|
|
||||||
{
|
|
||||||
int a = read_char();
|
|
||||||
int b = read_char();
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
size_t
|
|
||||||
cgi_item(char *str, size_t maxlen)
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
size_t pos = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
c = read_char();
|
|
||||||
switch (c) {
|
|
||||||
case EOF:
|
|
||||||
case '=':
|
|
||||||
case '&':
|
|
||||||
str[pos] = '\0';
|
|
||||||
return pos;
|
|
||||||
case '%':
|
|
||||||
c = read_hex();
|
|
||||||
break;
|
|
||||||
case '+':
|
|
||||||
c = ' ';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (pos < maxlen - 1) {
|
|
||||||
str[pos] = c;
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cgi_head(char *title)
|
|
||||||
{
|
|
||||||
if (is_cgi) {
|
|
||||||
printf("Content-type: text/html\r\n\r\n");
|
|
||||||
}
|
|
||||||
printf(("<!DOCTYPE html>\n"
|
|
||||||
"<html>\n"
|
|
||||||
" <head>\n"
|
|
||||||
" <title>%s</title>\n"
|
|
||||||
" <link rel=\"stylesheet\" href=\"css/style.css\">\n"
|
|
||||||
" </head>\n"
|
|
||||||
" <body><h1>%s</h1><section>\n"), title, title);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cgi_foot()
|
|
||||||
{
|
|
||||||
printf("\n</section>\n");
|
|
||||||
printf("<nav><ul>\n");
|
|
||||||
printf("<li><a href=\"register.html\">Register</a></li>\n");
|
|
||||||
printf("<li><a href=\"puzzles.html\">Puzzles</a></li>\n");
|
|
||||||
printf("<li><a href=\"scoreboard.html\">Scoreboard</a></li>\n");
|
|
||||||
printf("</ul></nav>\n");
|
|
||||||
printf("<section id=\"sponsors\">\n");
|
|
||||||
printf("<img src=\"images/lanl.png\" alt=\"Los Alamos National Laboratory\">\n");
|
|
||||||
printf("<img src=\"images/doe.png\" alt=\"US Department Of Energy\">\n");
|
|
||||||
printf("<img src=\"images/sandia.png\" alt=\"Sandia National Laboratories\">\n");
|
|
||||||
printf("</section>\n");
|
|
||||||
printf("</body></html>\n");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cgi_result(int code, char *desc, char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
if (is_cgi) {
|
|
||||||
printf("Status: %d %s\r\n", code, desc);
|
|
||||||
}
|
|
||||||
cgi_head(desc);
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vprintf(fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
cgi_foot();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cgi_fail(int err)
|
|
||||||
{
|
|
||||||
switch (err) {
|
|
||||||
case ERR_GENERAL:
|
|
||||||
cgi_result(500, "Points not awarded", "<p>The server is unable to award your points at this time.</p>");
|
|
||||||
case ERR_NOTEAM:
|
|
||||||
cgi_result(409, "No such team", "<p>There is no team with that hash.</p>");
|
|
||||||
case ERR_CLAIMED:
|
|
||||||
cgi_result(409, "Already claimed",
|
|
||||||
"<p>That is the correct answer, but your team has already claimed these points.</p>");
|
|
||||||
default:
|
|
||||||
cgi_result(409, "Failure", "<p>Failure code: %d</p>", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cgi_page(char *title, char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
cgi_head(title);
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vprintf(fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
cgi_foot();
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cgi_error(char *text)
|
|
||||||
{
|
|
||||||
cgi_result(500, "Internal error", "<p>%s</p>", text);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Common routines
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* cut -d$ANCHOR -f2- | grep -Fx "$NEEDLE"
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
anchored_search(char const *filename, char const *needle, char const anchor)
|
|
||||||
{
|
|
||||||
FILE *f = fopen(filename, "r");
|
|
||||||
size_t nlen = strlen(needle);
|
|
||||||
char line[1024];
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
while (f) {
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
if (NULL == fgets(line, sizeof line, f)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Find anchor
|
|
||||||
*/
|
|
||||||
if (anchor) {
|
|
||||||
p = strchr(line, anchor);
|
|
||||||
if (!p) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
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 just a performance hack.
|
|
||||||
*/
|
|
||||||
if ((p[nlen] != '\n') && (p[nlen] != '\0')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
p[nlen] = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Okay, now we can compare!
|
|
||||||
*/
|
|
||||||
if (0 == strcmp(p, needle)) {
|
|
||||||
ret = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f) {
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
urandom(char *buf, size_t buflen)
|
|
||||||
{
|
|
||||||
static int fd = -2;
|
|
||||||
|
|
||||||
if (-2 == fd) {
|
|
||||||
srandom(time(NULL) * getpid());
|
|
||||||
fd = open("/dev/urandom", O_RDONLY);
|
|
||||||
}
|
|
||||||
if (-1 != fd) {
|
|
||||||
int len;
|
|
||||||
|
|
||||||
len = read(fd, buf, buflen);
|
|
||||||
if (len == buflen) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fall back to libc's crappy thing
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < buflen; i += 1) {
|
|
||||||
buf[i] = (char) random();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
my_snprintf(char *buf, size_t buflen, char *fmt, ...)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
len = vsnprintf(buf, buflen - 1, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
buf[buflen - 1] = '\0';
|
|
||||||
if (len >= buflen) {
|
|
||||||
return buflen - 1;
|
|
||||||
} else {
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ctf_chdir()
|
|
||||||
{
|
|
||||||
static int initialized = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
initialized = 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* chdir to $CTF_BASE
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
char const *ctf_base = getenv("CTF_BASE");
|
|
||||||
|
|
||||||
if (ctf_base) {
|
|
||||||
chdir(ctf_base);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Keep going up one directory until there's an assigned.txt file
|
|
||||||
*/
|
|
||||||
for (i = 0; i < 5; i += 1) {
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if ((0 == stat("assigned.txt", &st)) && S_ISREG(st.st_mode)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
chdir("..");
|
|
||||||
}
|
|
||||||
fprintf(stderr, "Can not determine CTF_BASE directory: exiting.\n");
|
|
||||||
exit(66);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static char *
|
|
||||||
mkpath(char const *type, char const *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
char relpath[PATH_MAX];
|
|
||||||
static char path[PATH_MAX];
|
|
||||||
|
|
||||||
ctf_chdir();
|
|
||||||
vsnprintf(relpath, sizeof(relpath) - 1, fmt, ap);
|
|
||||||
relpath[sizeof(relpath) - 1] = '\0';
|
|
||||||
|
|
||||||
/*
|
|
||||||
* $CTF_BASE/type/relpath
|
|
||||||
*/
|
|
||||||
my_snprintf(path, sizeof(path), "%s/%s", type, relpath);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
state_path(char const *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
char *ret;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
ret = mkpath("state", fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
package_path(char const *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
char *ret;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
ret = mkpath("packages", fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
team_exists(char const *teamhash)
|
|
||||||
{
|
|
||||||
struct stat buf;
|
|
||||||
int ret;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ((!teamhash) || (!*teamhash)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check for invalid characters.
|
|
||||||
*/
|
|
||||||
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/%s", teamhash), &buf);
|
|
||||||
if (-1 == ret) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return values: -1: general error -2: no such team -3: points already awarded
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
award_points(char const *teamhash, char const *category, const long points, char const *uid)
|
|
||||||
{
|
|
||||||
char line[100];
|
|
||||||
int linelen;
|
|
||||||
char *filename;
|
|
||||||
FILE *f;
|
|
||||||
time_t now = time(NULL);
|
|
||||||
|
|
||||||
if (!team_exists(teamhash)) {
|
|
||||||
return ERR_NOTEAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
linelen = snprintf(line, sizeof(line), "%s %s %ld %s", teamhash, category, points, uid);
|
|
||||||
if (sizeof(line) <= linelen) {
|
|
||||||
return ERR_GENERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (anchored_search(state_path("points.log"), line, ' ')) {
|
|
||||||
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. Editing the log file would require locking it, which would block everything trying to score, effectively
|
|
||||||
* taking down the entire contest. If you can't lock it first--and nothing in busybox lets 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
filename = state_path("points.tmp/%lu.%d.%s.%s.%ld", (unsigned long) now, getpid(), teamhash, category, points);
|
|
||||||
f = fopen(filename, "w");
|
|
||||||
if (!f) {
|
|
||||||
return ERR_GENERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EOF == fprintf(f, "%lu %s\n", (unsigned long) now, line)) {
|
|
||||||
return ERR_GENERAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Rename into points.new
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
char ofn[PATH_MAX];
|
|
||||||
|
|
||||||
strncpy(ofn, filename, sizeof(ofn));
|
|
||||||
filename = state_path("points.new/%lu.%d.%s.%s.%ld", (unsigned long) now, getpid(), teamhash, category, points);
|
|
||||||
rename(ofn, filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
38
src/common.h
38
src/common.h
|
@ -1,38 +0,0 @@
|
||||||
#ifndef __COMMON_H__
|
|
||||||
#define __COMMON_H__
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define TEAM_MAX 40
|
|
||||||
#define CAT_MAX 40
|
|
||||||
#define TOKEN_MAX 80
|
|
||||||
#define itokenlen 5
|
|
||||||
|
|
||||||
#define ERR_GENERAL -1
|
|
||||||
#define ERR_NOTEAM -2
|
|
||||||
#define ERR_CLAIMED -3
|
|
||||||
|
|
||||||
int cgi_init(char *global_argv[]);
|
|
||||||
size_t cgi_item(char *str, size_t maxlen);
|
|
||||||
void cgi_head(char *title);
|
|
||||||
void cgi_foot();
|
|
||||||
void cgi_result(int code, char *desc, char *fmt, ...);
|
|
||||||
void cgi_fail(int err);
|
|
||||||
void cgi_page(char *title, char *fmt, ...);
|
|
||||||
void cgi_error(char *text);
|
|
||||||
|
|
||||||
|
|
||||||
void ctf_chdir();
|
|
||||||
int anchored_search(char const *filename, char const *needle, const char anchor);
|
|
||||||
void urandom(char *buf, size_t buflen);
|
|
||||||
int my_snprintf(char *buf, size_t buflen, char *fmt, ...);
|
|
||||||
char *state_path(char const *fmt, ...);
|
|
||||||
char *package_path(char const *fmt, ...);
|
|
||||||
int team_exists(char const *teamhash);
|
|
||||||
int award_points(char const *teamhash,
|
|
||||||
char const *category,
|
|
||||||
long point,
|
|
||||||
char const *uid);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,47 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sysexits.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int points;
|
|
||||||
int ret;
|
|
||||||
char comment[512];
|
|
||||||
|
|
||||||
if (argc != 5) {
|
|
||||||
fprintf(stderr, "Usage: pointscli TEAM CATEGORY POINTS 'COMMENT'\n");
|
|
||||||
return EX_USAGE;
|
|
||||||
}
|
|
||||||
ctf_chdir();
|
|
||||||
|
|
||||||
points = atoi(argv[3]);
|
|
||||||
if (0 == points) {
|
|
||||||
fprintf(stderr, "Error: award 0 points?\n");
|
|
||||||
return EX_USAGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(comment, sizeof comment, "--%s", argv[4]);
|
|
||||||
|
|
||||||
ret = award_points(argv[1], argv[2], points, comment);
|
|
||||||
switch (ret) {
|
|
||||||
case 0:
|
|
||||||
return 0;
|
|
||||||
case ERR_GENERAL:
|
|
||||||
perror("General error");
|
|
||||||
return EX_UNAVAILABLE;
|
|
||||||
case ERR_NOTEAM:
|
|
||||||
fprintf(stderr, "No such team\n");
|
|
||||||
return EX_NOUSER;
|
|
||||||
case ERR_CLAIMED:
|
|
||||||
fprintf(stderr, "Duplicate entry\n");
|
|
||||||
return EX_DATAERR;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "Error %d\n", ret);
|
|
||||||
return EX_SOFTWARE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,80 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
char team[TEAM_MAX] = {0};
|
|
||||||
char category[CAT_MAX] = {0};
|
|
||||||
char points_str[11] = {0};
|
|
||||||
char answer[500] = {0};
|
|
||||||
long points = 0;
|
|
||||||
|
|
||||||
if (-1 == cgi_init(argv)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
ctf_chdir();
|
|
||||||
|
|
||||||
/* Read in team and answer */
|
|
||||||
while (1) {
|
|
||||||
size_t len;
|
|
||||||
char key[20];
|
|
||||||
|
|
||||||
len = cgi_item(key, sizeof(key));
|
|
||||||
if (0 == len) break;
|
|
||||||
switch (key[0]) {
|
|
||||||
case 't':
|
|
||||||
cgi_item(team, sizeof(team));
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
cgi_item(category, sizeof(category));
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
cgi_item(points_str, sizeof(points_str));
|
|
||||||
points = atol(points_str);
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
cgi_item(answer, sizeof(answer));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Validate category name (prevent directory traversal) */
|
|
||||||
{
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
for (p = category; *p; p += 1) {
|
|
||||||
if ((! isalnum(*p)) && ('-' != *p)) {
|
|
||||||
cgi_page("Invalid category", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check answer (also assures category exists) */
|
|
||||||
{
|
|
||||||
char needle[400];
|
|
||||||
|
|
||||||
my_snprintf(needle, sizeof(needle), "%ld %s", points, answer);
|
|
||||||
|
|
||||||
if (! anchored_search(package_path("%s/answers.txt", category), needle, 0)) {
|
|
||||||
cgi_page("Wrong answer", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
int ret = award_points(team, category, points, "P");
|
|
||||||
if (ret < 0) {
|
|
||||||
cgi_fail(ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cgi_page("Points awarded",
|
|
||||||
("<p>%d points for %s.</p>\n"
|
|
||||||
"<p><a href=\"/puzzles.html\">Back to puzzles</a></p>\n"
|
|
||||||
"<!-- awarded %d -->"),
|
|
||||||
points, team, points);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,163 +0,0 @@
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
longcmp(long *a, long *b)
|
|
||||||
{
|
|
||||||
if (*a < *b) return -1;
|
|
||||||
if (*a > *b) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define PUZZLES_MAX 100
|
|
||||||
|
|
||||||
/** Keeps track of the most points yet awarded in each category */
|
|
||||||
int ncats = 0;
|
|
||||||
struct {
|
|
||||||
char cat[CAT_MAX];
|
|
||||||
long points;
|
|
||||||
} points_by_cat[PUZZLES_MAX];
|
|
||||||
|
|
||||||
|
|
||||||
size_t
|
|
||||||
read_until_char(FILE *f, char *buf, size_t buflen, char delim)
|
|
||||||
{
|
|
||||||
size_t i = 0;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int c = fgetc(f);
|
|
||||||
|
|
||||||
if ((EOF == c) || delim == c) {
|
|
||||||
if (buf) {
|
|
||||||
buf[i] = '\0';
|
|
||||||
}
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i + 1 < buflen) {
|
|
||||||
buf[i] = c;
|
|
||||||
}
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
DIR *opt;
|
|
||||||
|
|
||||||
if (-1 == cgi_init(argv)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
ctf_chdir();
|
|
||||||
|
|
||||||
{
|
|
||||||
FILE *f = fopen(state_path("points.log"), "r");
|
|
||||||
char cat[CAT_MAX];
|
|
||||||
char points_str[11];
|
|
||||||
long points;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
while (f && (! feof(f))) {
|
|
||||||
read_until_char(f, NULL, 0, ' ');
|
|
||||||
read_until_char(f, NULL, 0, ' ');
|
|
||||||
read_until_char(f, cat, sizeof(cat), ' ');
|
|
||||||
read_until_char(f, points_str, sizeof(points_str), ' ');
|
|
||||||
read_until_char(f, NULL, 0, '\n');
|
|
||||||
points = atol(points_str);
|
|
||||||
|
|
||||||
for (i = 0; i < ncats; i += 1) {
|
|
||||||
if (0 == strcmp(cat, points_by_cat[i].cat)) break;
|
|
||||||
}
|
|
||||||
if (i == ncats) {
|
|
||||||
if (PUZZLES_MAX == ncats) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
strncpy(points_by_cat[i].cat, cat, sizeof(points_by_cat[i].cat));
|
|
||||||
points_by_cat[i].points = 0;
|
|
||||||
ncats += 1;
|
|
||||||
}
|
|
||||||
if (points > points_by_cat[i].points) {
|
|
||||||
points_by_cat[i].points = points;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f) fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
opt = opendir(package_path(""));
|
|
||||||
if (NULL == opt) {
|
|
||||||
cgi_error("Cannot open packages directory");
|
|
||||||
}
|
|
||||||
|
|
||||||
cgi_head("Open puzzles");
|
|
||||||
printf("<dl>\n");
|
|
||||||
|
|
||||||
/* For each file in /packages/ ... */
|
|
||||||
while (1) {
|
|
||||||
struct dirent *e = readdir(opt);
|
|
||||||
char *cat;
|
|
||||||
long maxpoints = 0;
|
|
||||||
FILE *map;
|
|
||||||
|
|
||||||
if (! e) break;
|
|
||||||
|
|
||||||
cat = e->d_name;
|
|
||||||
if ('.' == cat[0]) continue;
|
|
||||||
|
|
||||||
/* Find the most points scored in this category */
|
|
||||||
for (i = 0; i < ncats; i += 1) {
|
|
||||||
if (0 == strcmp(cat, points_by_cat[i].cat)) {
|
|
||||||
maxpoints = points_by_cat[i].points;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read in category's map file, print html until point limit reached */
|
|
||||||
map = fopen(package_path("%s/map.txt", cat), "r");
|
|
||||||
if (map == NULL) continue;
|
|
||||||
|
|
||||||
printf(" <dt>%s</dt>\n", cat);
|
|
||||||
printf(" <dd>\n");
|
|
||||||
|
|
||||||
while (i < PUZZLES_MAX && (!feof(map))) {
|
|
||||||
char hash[20];
|
|
||||||
char points_str[20];
|
|
||||||
long points;
|
|
||||||
|
|
||||||
read_until_char(map, points_str, sizeof(points_str), ' ');
|
|
||||||
read_until_char(map, hash, sizeof(hash), '\n');
|
|
||||||
points = atol(points_str);
|
|
||||||
|
|
||||||
if (0 == points) {
|
|
||||||
printf(" <span title=\"Category Complete\">⁂</span>\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf(" <a href=\"/%s/%s/\">%ld</a>\n", cat, hash, points);
|
|
||||||
|
|
||||||
if (points > maxpoints) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf(" </dd>\n");
|
|
||||||
fclose(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir(opt);
|
|
||||||
|
|
||||||
printf("</dl>\n");
|
|
||||||
|
|
||||||
printf("<p>New puzzles are unlocked when any team answers the highest-scoring puzzle in a category.</p>");
|
|
||||||
printf("<p>Are you using Windows? You have our pity. But you might enjoy a program called <samp>wget</samp>, which will allow you to download every open puzzle with a single <samp>wget -r</samp>!</p>");
|
|
||||||
cgi_foot();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
130
src/tea.c
130
src/tea.c
|
@ -1,130 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sysexits.h>
|
|
||||||
|
|
||||||
static const char magic[4] = "tea.";
|
|
||||||
|
|
||||||
#define min(a,b) (((a)<(b))?(a):(b))
|
|
||||||
|
|
||||||
void
|
|
||||||
tea_encrypt(uint32_t *v, uint32_t *k) {
|
|
||||||
uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */
|
|
||||||
uint32_t delta=0x9e3779b9; /* a key schedule constant */
|
|
||||||
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
|
|
||||||
for (i=0; i < 32; i++) { /* basic cycle start */
|
|
||||||
sum += delta;
|
|
||||||
v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
|
|
||||||
v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
|
|
||||||
} /* end cycle */
|
|
||||||
v[0]=v0; v[1]=v1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Use TEA in CTR mode to create a stream cipher.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
tea_apply(FILE *out, FILE *in, uint32_t *k, uint32_t ivec)
|
|
||||||
{
|
|
||||||
uint32_t count = 0;
|
|
||||||
uint32_t v[2];
|
|
||||||
size_t idx = sizeof v;
|
|
||||||
char *p = (char *)v;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
int c = fgetc(in);
|
|
||||||
|
|
||||||
if (EOF == c) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sizeof v == idx) {
|
|
||||||
v[0] = ivec;
|
|
||||||
v[1] = count++;
|
|
||||||
tea_encrypt(v, k);
|
|
||||||
idx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fputc(c ^ p[idx++], out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
tea_decrypt_stream(FILE *out, FILE *in, uint32_t *k)
|
|
||||||
{
|
|
||||||
uint32_t ivec;
|
|
||||||
|
|
||||||
{
|
|
||||||
char m[4] = {0};
|
|
||||||
|
|
||||||
fread(m, sizeof m, 1, in);
|
|
||||||
if (memcmp(m, magic, 4)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fread(&ivec, sizeof ivec, 1, in);
|
|
||||||
tea_apply(out, in, k, ivec);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
tea_encrypt_stream(FILE *out, FILE *in, uint32_t *k)
|
|
||||||
{
|
|
||||||
uint32_t ivec;
|
|
||||||
|
|
||||||
fwrite(magic, sizeof magic, 1, out);
|
|
||||||
|
|
||||||
{
|
|
||||||
FILE *r = fopen("/dev/urandom", "r");
|
|
||||||
|
|
||||||
if (! r) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
fread(&ivec, sizeof ivec, 1, r);
|
|
||||||
fclose(r);
|
|
||||||
}
|
|
||||||
fwrite(&ivec, sizeof ivec, 1, out);
|
|
||||||
|
|
||||||
tea_apply(out, in, k, ivec);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
usage(const char *prog)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Usage: %s [-e] <PLAINTEXT\n", prog);
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
fprintf(stderr, "You must pass in a key on fd 3 or in the environment variable KEY.\n");
|
|
||||||
return EX_USAGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
uint32_t key[4] = {0};
|
|
||||||
|
|
||||||
{
|
|
||||||
char *ekey = getenv("KEY");
|
|
||||||
|
|
||||||
if (ekey) {
|
|
||||||
memcpy(key, ekey, min(strlen(ekey), sizeof(key)));
|
|
||||||
} else if (-1 == read(3, key, sizeof(key))) {
|
|
||||||
return usage(argv[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! argv[1]) {
|
|
||||||
tea_decrypt_stream(stdout, stdin, key);
|
|
||||||
} else if (0 == strcmp(argv[1], "-e")) {
|
|
||||||
tea_encrypt_stream(stdout, stdin, key);
|
|
||||||
} else {
|
|
||||||
return usage(argv[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Reference in New Issue