Initial Commit of hypercube

This commit is contained in:
Darren VanBuren 2016-09-12 22:08:46 -07:00
commit 79732cf0d6
29 changed files with 4379 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
build-Desktop-Debug
dist.h
*.o

18
README Normal file
View file

@ -0,0 +1,18 @@
<beak> hypercube är ungefär som rap på skånska
<beak> coolt men lite skumt
How to compile (no autoconf, sorry...):
- Edit asio/asio.h and select what async I/O interface you wanna use.
- Edit Makefile.
- If you are on Linux, USE_LINUX_SENDFILE should be defined.
- If you are on Linux, using epoll (kernel >=2.6), and have a libc which
doesn't support them (warnings about 'epoll_* is not implemented and
will always fail' on linking), uncomment the AKEPOLL* lines.
- Maybe put that final touch to config.h.
It ships with DEBUG enabled. You probably want to disable this.
Now make :-).
The rest of the configuration is in your CFG_FILE (hypercube.cfg by
default).
- anakata@anakata.hack.se || anakata@g-con.org

2
access.cfg.dist Normal file
View file

@ -0,0 +1,2 @@
#deny .* /status

7
akbuf/Makefile Normal file
View file

@ -0,0 +1,7 @@
CC=gcc
#CCFLAGS=-Wall -Werror -ggdb #-pg
CCFLAGS=-ggdb
all: akbuf.o
akbuf.o: akbuf.c akbuf.h
$(CC) $(CCFLAGS) -c akbuf.c

585
akbuf/akbuf.c Normal file
View file

@ -0,0 +1,585 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <sys/time.h>
#include "akbuf.h"
akbuf_ctx *ctx_hash[AKBUF_CTX_HASH_SIZE];
unsigned int ctx_hash_initialized = 0;
unsigned int prng_initialized = 0;
//#define RANDFUNC vanilla_rand /* XXX */
#define RANDFUNC randfunc_inc
//#define AKBUF_DEBUG
#define AK_REALSIZE(size) (((size) <= GRAIN_SIZE && (size) != 0)? (size) : (((size) | (GRAIN_SIZE - 1)) + 1))
#define CTX_HASH_FN(hnd) ((hnd) & (AKBUF_CTX_HASH_SIZE - 1))
#ifdef AKBUF_TEST
#define AKBUF_DEBUG
#endif
#ifdef AKBUF_DEBUG
void dump_all_ctxs(void)
{
#if 0
akbuf_ctx *ctx;
printf("dumping ctxs head %.8x:\n", (unsigned int)ctx_head);
ctx = ctx_head;
while (ctx != NULL)
{
printf(" ctx @ %.8x; handle %.8x bufs %.8x\n", (unsigned int)ctx, (unsigned int)ctx->hnd, (unsigned int)ctx->head);
ctx = ctx->next;
}
printf("end of dump\n");
#endif
}
#endif
unsigned int vanilla_rand(void)
{
if (prng_initialized == 0)
{
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_sec ^ tv.tv_usec ^ (getpid() << 16));
prng_initialized = 1;
}
return rand();
}
static unsigned int cur_ctx;
unsigned int randfunc_inc(void)
{
if (prng_initialized == 0)
{
struct timeval tv;
gettimeofday(&tv, NULL);
cur_ctx = tv.tv_usec;
prng_initialized = 1;
}
return cur_ctx ++;
}
void akbuf_panic(unsigned char *file, unsigned int line, unsigned char *from_file, unsigned int from_line, unsigned char *format, ...)
{
char msg[2048];
va_list va;
if (from_file != NULL)
{
snprintf(msg, sizeof(msg) - 1, "AKbuf PANIC @ %s:%u (from %s:%u): ", file, line, from_file, from_line); msg[sizeof(msg) - 1] = 0;
} else
{
snprintf(msg, sizeof(msg) - 1, "AKbuf PANIC @ %s:%u: ", file, line); msg[sizeof(msg) - 1] = 0;
}
write(2, msg, strlen(msg));
va_start(va, format);
vsnprintf(msg, sizeof(msg) - 1, format, va); msg[sizeof(msg) - 1] = 0;
va_end(va);
write(2, msg, strlen(msg));
write(2, "\n", 1);
_exit(1);
}
akbuf_ctx *akbuf_find_ctx(akbuf_ctxh hnd)
{
akbuf_ctx *cur;
if (ctx_hash_initialized == 0)
{
unsigned int i;
for (i = 0; i < AKBUF_CTX_HASH_SIZE; i ++) ctx_hash[i] = NULL;
ctx_hash_initialized = 1;
}
cur = ctx_hash[CTX_HASH_FN(hnd)];
while (cur != NULL) { if (cur->hnd == hnd) return cur; cur = cur->next; }
return NULL;
}
akbuf_ctxh akbuf_new_ctx(void)
{
akbuf_ctx *newCtx;
unsigned int idx;
newCtx = malloc(sizeof(*newCtx));
AKbuf_ASSERT(newCtx != NULL);
do
{
newCtx->hnd = RANDFUNC();
} while (akbuf_find_ctx(newCtx->hnd) != NULL || newCtx->hnd == AKbuf_INVALID_CTX);
newCtx->head = NULL;
newCtx->prev = NULL;
newCtx->next = ctx_hash[idx = CTX_HASH_FN(newCtx->hnd)];
if (newCtx->next != NULL) newCtx->next->prev = newCtx;
ctx_hash[idx] = newCtx;
return newCtx->hnd;
}
akbuf *akbuf_init(akbuf_ctxh ctxh, AKsize_t _size)
{
AKsize_t size;
akbuf *newBuf;
akbuf_ctx *ctx;
if ((ctx = akbuf_find_ctx(ctxh)) == NULL) AKbuf_PANIC("akbuf_init(): Invalid ctx %.8x", ctxh);
size = AK_REALSIZE(_size);
#ifdef AKBUF_DEBUG
printf("DEBUG: req size %.8x, final size %.8x\n", _size, size);
#endif
AKbuf_ASSERT(size >= _size);
newBuf = malloc(sizeof(akbuf));
AKbuf_ASSERT(newBuf != NULL);
newBuf->in_use = 1;
newBuf->size = size;
newBuf->idx = 0;
newBuf->prev = NULL;
newBuf->next = ctx->head;
newBuf->head = malloc(size);
AKbuf_ASSERT(newBuf->head != NULL);
if (ctx->head != NULL) ctx->head->prev = newBuf;
ctx->head = newBuf;
#ifdef AKBUF_DEBUG
memset(newBuf->head, 'A', newBuf->size);
#endif
return newBuf;
}
void akbuf_set_size(akbuf *buf, AKsize_t _size)
{
AKsize_t size;
AKbuf_dASSERT(buf != NULL);
size = AK_REALSIZE(_size);
AKbuf_ASSERT(size >= _size);
if (size == buf->size) return;
buf->head = realloc(buf->head, size);
AKbuf_ASSERT(buf->head != NULL);
buf->size = size;
if (buf->idx > size) buf->idx = size;
return;
}
void akbuf_consume(akbuf *buf, AKsize_t len)
{
AKbuf_dASSERT(buf != NULL);
if (len == 0) return;
AKbuf_ASSERT(buf->idx >= len && buf->idx <= buf->size);
memcpy(buf->head, buf->head + len, buf->idx - len);
buf->idx -= len;
if (len >= GRAIN_SIZE) akbuf_set_size(buf, buf->idx);
return;
}
unsigned char akbuf_eat_byte(akbuf *buf)
{
unsigned char ret;
AKbuf_dASSERT(buf != NULL);
AKbuf_ASSERT(buf->idx > 0 && buf->idx <= buf->size);
ret = *buf->head;
memcpy(buf->head, buf->head + 1, buf->idx - 1);
buf->idx --;
return ret;
}
void akbuf_set_data(akbuf *buf, unsigned char *data, AKsize_t len)
{
AKbuf_dASSERT(buf != NULL && data != NULL);
if (len > buf->size) akbuf_set_size(buf, len);
memcpy(buf->head, data, len);
buf->idx = len;
return;
}
void akbuf_vsprintf(akbuf *buf, unsigned char *format, va_list va)
{
AKssize_t n;
AKbuf_dASSERT(buf != NULL && format != NULL);
if ((n = vsnprintf(buf->head, buf->size, format, va)) >= buf->size && n >= 0)
{
AKbuf_ASSERT((n + 1) > n);
akbuf_set_size(buf, n + 1);
n = vsnprintf(buf->head, buf->size, format, va);
}
AKbuf_ASSERT(n >= 0);
buf->idx = n;
return;
}
void akbuf_sprintf(akbuf *buf, unsigned char *format, ...)
{
va_list va;
va_start(va, format);
akbuf_vsprintf(buf, format, va);
va_end(va);
return;
}
void akbuf_appendf(akbuf *buf, unsigned char *format, ...)
{
akbuf_ctxh ctx;
akbuf *appendbuf;
va_list va;
ctx = akbuf_new_ctx();
appendbuf = akbuf_init(ctx, 0);
va_start(va, format);
akbuf_vsprintf(appendbuf, format, va);
va_end(va);
akbuf_append_buf(buf, appendbuf);
akbuf_free_ctx(ctx);
}
void akbuf_append_data(akbuf *buf, unsigned char *data, AKsize_t len)
{
AKbuf_dASSERT(buf != NULL && data != NULL);
AKbuf_ASSERT(buf->idx + len >= buf->idx && buf->idx <= buf->size);
if (buf->idx + len >= buf->size) akbuf_set_size(buf, buf->idx + len);
memcpy(buf->head + buf->idx, data, len); buf->idx += len;
return;
}
void akbuf_append_byte(akbuf *buf, unsigned char b)
{
AKbuf_dASSERT(buf != NULL);
AKbuf_ASSERT(buf->idx + 1 > buf->idx && buf->idx <= buf->size);
if (buf->idx + 1 >= buf->size) akbuf_set_size(buf, buf->idx + 1);
buf->head[buf->idx ++] = b;
return;
}
int akbuf_chr(akbuf *buf, unsigned char b)
{
unsigned char *p;
AKbuf_dASSERT(buf != NULL);
if ((p = memchr(buf->head, b, buf->idx)) == NULL) return -1;
return (int)(p - buf->head);
}
void akbuf_asciiz(akbuf *buf)
{
AKbuf_dASSERT(buf != NULL);
AKbuf_ASSERT(buf->idx + 1 > buf->idx && buf->idx <= buf->size);
if (buf->idx == buf->size) akbuf_set_size(buf, buf->idx + 1);
buf->head[buf->idx] = 0;
return;
}
void akbuf_free(akbuf_ctxh ctxh, akbuf *buf)
{
akbuf_ctx *ctx;
akbuf *cur;
AKbuf_dASSERT(buf != NULL);
if ((ctx = akbuf_find_ctx(ctxh)) == NULL) AKbuf_PANIC("akbuf_free(): Invalid ctx %.8x", ctxh);
cur = ctx->head;
while (cur != buf && cur != NULL) cur = cur->next;
if (cur == NULL) AKbuf_PANIC("akbuf_free(): Unknown akbuf %.8x", (unsigned int)buf);
#ifdef AKBUF_DEBUG
printf("DEBUG: freeing buf %.8x\n", (unsigned int)cur);
#endif
AKbuf_ASSERT(cur->in_use == 1 && cur->head != NULL);
cur->in_use = 0;
if (cur->prev != NULL) cur->prev->next = cur->next;
if (cur->next != NULL) cur->next->prev = cur->prev;
if (cur == ctx->head) ctx->head = cur->next;
cur->prev = cur->next = NULL;
cur->idx = (unsigned int)-1;
#ifdef AKBUF_DEBUG
memset(cur->head, 'F', cur->size);
#else
//memset(cur->head, 'F', (cur->size > GRAIN_SIZE)? GRAIN_SIZE : cur->size);
#endif
cur->size = 0;
AKfree(cur->head); cur->head = NULL;
free(cur);
}
void _akbuf_free_ctx(akbuf_ctxh ctxh, unsigned char *from_file, unsigned int from_line)
{
akbuf_ctx *ctx;
akbuf *cur, *prev;
unsigned int idx;
if (ctxh == AKbuf_INVALID_CTX) return;
if ((ctx = akbuf_find_ctx(ctxh)) == NULL) AKbuf_PANIC_FROM("akbuf_free_ctx(): Invalid ctx %.8x", ctxh);
cur = ctx->head;
while (cur != NULL)
{
prev = cur;
cur = cur->next;
AKbuf_ASSERT(prev->in_use == 1 && prev->head != NULL);
prev->in_use = 0;
prev->prev = prev->next = NULL;
prev->idx = (unsigned int)-1;
#ifdef AKBUF_DEBUG
printf("DEBUG: prev %.8x prev->size %.8x\n", (unsigned int)prev, prev->size);
memset(prev->head, 'G', prev->size);
#endif
//memset(prev->head, 'F', (prev->size > GRAIN_SIZE)? GRAIN_SIZE : prev->size);
prev->size = 0;
AKfree(prev->head); prev->head = NULL;
free(prev);
}
ctx->head = NULL;
if (ctx->prev != NULL) ctx->prev->next = ctx->next;
if (ctx->next != NULL) ctx->next->prev = ctx->prev;
if (ctx == ctx_hash[idx = CTX_HASH_FN(ctxh)]) ctx_hash[idx] = ctx->next;
#ifdef AKBUF_DEBUG
memset(ctx, 'C', sizeof(akbuf_ctx));
#endif
free(ctx);
}
akbuf_table *akbuf_table_init(akbuf_ctxh ctxh, unsigned int type)
{
akbuf *tblbuf;
akbuf_table *ret;
tblbuf = akbuf_init(ctxh, sizeof(akbuf_table));
AKbuf_dASSERT(tblbuf != NULL);
akbuf_set_idx(tblbuf, sizeof(akbuf_table));
ret = (akbuf_table *)akbuf_data(tblbuf);
ret->head = ret->tail = NULL;
ret->type = type;
ret->ctxh = ctxh;
return ret;
}
akbuf_table_entry *akbuf_table_entry_add(akbuf_ctxh ctxh, akbuf_table *tbl, unsigned char *key, akbuf *data)
{
akbuf *entbuf;
akbuf_table_entry *new;
AKbuf_dASSERT(tbl != NULL && key != NULL && data != NULL);
AKbuf_ASSERT(ctxh == tbl->ctxh);
if (akbuf_find_ctx(ctxh) == NULL) AKbuf_PANIC("akbuf_table_entry_add(): Invalid ctx %.08x", ctxh);
if ((new = akbuf_table_entry_find(tbl, key)) != NULL)
{
akbuf_clone(new->data, data);
return new;
}
entbuf = akbuf_init(ctxh, sizeof(akbuf_table_entry));
AKbuf_dASSERT(entbuf != NULL);
akbuf_set_idx(entbuf, sizeof(akbuf_table_entry));
new = (akbuf_table_entry *)akbuf_data(entbuf);
new->key = akbuf_init(ctxh, 0);
akbuf_strcpy(new->key, key);
akbuf_append_byte(new->key, 0);
new->data = akbuf_init(ctxh, akbuf_idx(data));
akbuf_clone(new->data, data);
new->prev = tbl->tail;
new->next = NULL;
if (tbl->head == NULL)
{
tbl->head = new;
} else
{
tbl->tail->next = new;
}
tbl->tail = new;
return new;
}
akbuf_table_entry *akbuf_table_entry_add_buf(akbuf_ctxh ctxh, akbuf_table *tbl, akbuf *key, akbuf *data)
{
akbuf *entbuf;
akbuf_table_entry *new;
AKbuf_dASSERT(tbl != NULL && key != NULL && data != NULL);
AKbuf_ASSERT(tbl->type == AKBUF_TABLE_BIN);
AKbuf_ASSERT(ctxh == tbl->ctxh);
if (akbuf_find_ctx(ctxh) == NULL) AKbuf_PANIC("akbuf_table_entry_add(): Invalid ctx %.08x", ctxh);
entbuf = akbuf_init(ctxh, sizeof(akbuf_table_entry));
AKbuf_dASSERT(entbuf != NULL);
akbuf_set_idx(entbuf, sizeof(akbuf_table_entry));
new = (akbuf_table_entry *)akbuf_data(entbuf);
new->key = akbuf_init(ctxh, akbuf_idx(key));
akbuf_clone(new->key, key);
akbuf_append_byte(new->key, 0);
new->data = akbuf_init(ctxh, akbuf_idx(data));
akbuf_clone(new->data, data);
new->prev = tbl->tail;
new->next = NULL;
if (tbl->head == NULL)
{
tbl->head = new;
} else
{
tbl->tail->next = new;
}
tbl->tail = new;
return new;
}
akbuf_table_entry *akbuf_table_entry_add_str(akbuf_ctxh ctxh, akbuf_table *tbl, unsigned char *key, unsigned char *data)
{
akbuf *buf;
akbuf_ctxh ctx;
akbuf_table_entry *ret;
ctx = akbuf_new_ctx();
buf = akbuf_init(ctx, 0);
akbuf_strcpy(buf, data); akbuf_append_byte(buf, 0);
ret = akbuf_table_entry_add(ctxh, tbl, key, buf);
akbuf_free_ctx(ctx);
return ret;
}
akbuf_table_entry *akbuf_table_entry_find(akbuf_table *tbl, unsigned char *key)
{
int (*cmpfn)();
akbuf_table_entry *ent;
AKbuf_dASSERT(tbl != NULL && key != NULL);
AKbuf_ASSERT(tbl->type == AKBUF_TABLE_NOCASE || tbl->type == AKBUF_TABLE_CASE);
cmpfn = (tbl->type == AKBUF_TABLE_CASE)? strcmp : strcasecmp;
ent = tbl->head;
while (ent != NULL)
{
if (cmpfn(akbuf_data(ent->key), key) == 0) return ent;
ent = ent->next;
}
return NULL;
}
akbuf *akbuf_table_entry_get(akbuf_table *tbl, unsigned char *key)
{
akbuf_table_entry *ent;
if ((ent = akbuf_table_entry_find(tbl, key)) == NULL) return NULL;
return ent->data;
}
unsigned char *akbuf_table_entry_get_str(akbuf_table *tbl, unsigned char *key)
{
akbuf *buf;
if ((buf = akbuf_table_entry_get(tbl, key)) == NULL) return NULL;
akbuf_asciiz(buf);
return akbuf_data(buf);
}
void akbuf_urlencode_data(unsigned char *data, AKsize_t len, akbuf *outbuf)
{
unsigned int i;
unsigned int c;
for (i = 0; i < len; i ++)
{
unsigned char hexchars[] = "0123456789ABCDEF";
c = data[i];
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= '<'))
{
akbuf_append_byte(outbuf, c);
} else
{
akbuf_append_byte(outbuf, '%');
akbuf_append_byte(outbuf, hexchars[(c >> 4) & 0xf]);
akbuf_append_byte(outbuf, hexchars[c & 0xf]);
}
}
}
void akbuf_base64encode_data(unsigned char *data, AKsize_t len, akbuf *outbuf)
{
AKsize_t rem;
unsigned char b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
rem = len;
while (rem > 2)
{
akbuf_append_byte(outbuf, b64chars[data[0] >> 2]);
akbuf_append_byte(outbuf, b64chars[((data[0] & 0x03) << 4) + (data[1] >> 4)]);
akbuf_append_byte(outbuf, b64chars[((data[1] & 0x0f) << 2) + (data[2] >> 6)]);
akbuf_append_byte(outbuf, b64chars[data[2] & 0x3f]);
data += 3;
rem -= 3;
}
if (rem != 0)
{
akbuf_append_byte(outbuf, b64chars[data[0] >> 2]);
if (rem > 1)
{
akbuf_append_byte(outbuf, b64chars[((data[0] & 0x03) << 4) + (data[1] >> 4)]);
akbuf_append_byte(outbuf, b64chars[((data[1] & 0x0f) << 2)]);
akbuf_append_byte(outbuf, '=');
} else
{
akbuf_append_byte(outbuf, b64chars[((data[0] & 0x03) << 4)]);
akbuf_append_byte(outbuf, '=');
akbuf_append_byte(outbuf, '=');
}
}
}
#ifdef AKBUF_TEST
int main(int argc, char *argv[])
{
akbuf_ctxh ctx;
akbuf *buf, *buf2;
int i;
akbuf_table *tbl;
akbuf_table_entry *ent;
printf("akbuf_new_ctx()\n");
ctx = akbuf_new_ctx();
printf(" = %.8x\n", (unsigned int)ctx);
printf("akbuf_new_ctx() second = %.8x\n", (unsigned int)akbuf_new_ctx());
dump_all_ctxs();
buf = akbuf_init(ctx, 0);
akbuf_strcpy(buf, "blutti");
printf("akbuf_table_init() =\n");
tbl = akbuf_table_init(ctx, 0);
printf(" %.08x\n", (unsigned int)tbl);
printf("akbuf_table_entry_add() =\n");
ent = akbuf_table_entry_add(ctx, tbl, "foobar", buf);
printf(" %.08x\n", (unsigned int)ent);
printf("[%s]\n", akbuf_data(ent->key));
akbuf_strcpy(buf, "fnutti");
ent = akbuf_table_entry_add(ctx, tbl, "foobar2", buf);
printf(" next = %.08x prev = %.08x head = %.08x tail = %.08x\n", (unsigned int)ent->next, (unsigned int)ent->prev, (unsigned int)tbl->head, (unsigned int)tbl->tail);
printf("akbuf_table_entry_get(..., \"foobar\")\n");
buf2 = akbuf_table_entry_get(tbl, "foobar");
if (buf2 != NULL)
{
akbuf_asciiz(buf2);
printf(" [%s]\n", akbuf_data(buf2));
}
printf("akbuf_table_entry_get(..., \"foobar2\")\n");
buf2 = akbuf_table_entry_get(tbl, "foobar2");
if (buf2 != NULL)
{
akbuf_asciiz(buf2);
printf(" [%s]\n", akbuf_data(buf2));
}
#if 1
printf("akbuf_init(0x242)\n");
buf = akbuf_init(ctx, 0x242);
printf(" = %.8x\n", (unsigned int)buf);
//printf("akbuf_init(0xffffffff)\n");
//akbuf_init(ctx, 0xffffffff);
akbuf_strcpy(buf, "foobar blutti");
akbuf_append_str(buf, " fnutti");
akbuf_asciiz(buf);
printf("[%s]\n", akbuf_data(buf));
buf2 = akbuf_init(ctx, 0);
akbuf_strcpy(buf2, "1 2 3");
akbuf_append_str(buf2," 4 5 6");
akbuf_consume(buf2, 4);
akbuf_asciiz(buf2);
printf("[%s]\n", akbuf_data(buf2));
buf = akbuf_init(ctx, 0);
akbuf_sprintf(buf, "foobar %u [%s]", 0x242, "foo");
akbuf_asciiz(buf);
printf("fmt [%s]\n", akbuf_data(buf));
printf("freeing\n");
akbuf_free_ctx(ctx);
dump_all_ctxs();
ctx = akbuf_new_ctx();
buf = akbuf_init(ctx, 0x666);
akbuf_strcpy(buf, "foo\nbar\nblutti\nfnutti");
buf2 = akbuf_init(ctx, 0);
while ((i = akbuf_chr(buf, '\n')) != -1)
{
akbuf_split(buf, buf2, i);
akbuf_asciiz(buf2);
printf("split [%s]\n", akbuf_data(buf2));
}
akbuf_asciiz(buf);
printf("rem [%s]\n", akbuf_data(buf));
dump_all_ctxs();
akbuf_free_ctx(ctx);
#endif
akbuf_free_ctx(ctx);
return 0;
}
#endif

133
akbuf/akbuf.h Normal file
View file

@ -0,0 +1,133 @@
//#define GRAIN_SIZE 0x1000 /* must be power of 2 */
//#define AKBUF_CTX_HASH_SIZE 0x400000 /* must be power of 2 */
#define GRAIN_SIZE 0x100
#define AKBUF_CTX_HASH_SIZE 0x40000
#define AKsize_t size_t
#define AKssize_t ssize_t
#define AKbuf_PANIC(f...) akbuf_panic(__FILE__, __LINE__, NULL, 0, ##f)
#define AKbuf_PANIC_FROM(f...) akbuf_panic(__FILE__, __LINE__, from_file, from_line, ##f)
#define AKbuf_ASSERT(cond) if (!(cond)) akbuf_panic(__FILE__, __LINE__, NULL, 0, "Assertion (" __STRING(cond) ") failed")
#ifdef AKBUF_DEBUG
#define AKbuf_dASSERT(cond) AKbuf_ASSERT(cond)
#else
#define AKbuf_dASSERT(cond)
#endif
#define AKfree(ptr) if ((ptr) != NULL) { free(ptr); (ptr) = NULL; }
typedef unsigned int akbuf_ctxh;
#define AKbuf_INVALID_CTX ((akbuf_ctxh)-1)
typedef struct akbuf_s akbuf;
typedef struct akbuf_ctx_s akbuf_ctx;
struct akbuf_s
{
unsigned int in_use;
akbuf *prev;
akbuf *next;
unsigned int idx;
AKsize_t size;
unsigned char *head;
};
struct akbuf_ctx_s
{
akbuf_ctxh hnd;
akbuf *head;
akbuf_ctx *prev;
akbuf_ctx *next;
};
typedef struct akbuf_table_s akbuf_table;
typedef struct akbuf_table_entry_s akbuf_table_entry;
struct akbuf_table_s
{
akbuf_ctxh ctxh;
unsigned int type;
akbuf_table_entry *head;
akbuf_table_entry *tail;
};
struct akbuf_table_entry_s
{
akbuf *key;
akbuf *data;
akbuf_table_entry *prev;
akbuf_table_entry *next;
};
enum { AKBUF_TABLE_NOCASE, AKBUF_TABLE_CASE, AKBUF_TABLE_BIN };
void akbuf_panic(unsigned char *, unsigned int, unsigned char *, unsigned int, unsigned char *, ...);
akbuf_ctxh akbuf_new_ctx(void);
void akbuf_free(akbuf_ctxh, akbuf *);
void _akbuf_free_ctx(akbuf_ctxh, unsigned char *, unsigned int);
void akbuf_consume(akbuf *, AKsize_t);
unsigned char akbuf_eat_byte(akbuf *);
int akbuf_chr(akbuf *, unsigned char);
void akbuf_asciiz(akbuf *);
akbuf *akbuf_init(akbuf_ctxh, AKsize_t);
void akbuf_set_size(akbuf *, AKsize_t);
void akbuf_set_data(akbuf *, unsigned char *, AKsize_t);
void akbuf_append_data(akbuf *, unsigned char *, AKsize_t);
void akbuf_append_byte(akbuf *, unsigned char);
void akbuf_vsprintf(akbuf *, unsigned char *, va_list);
void akbuf_sprintf(akbuf *, unsigned char *, ...);
void akbuf_appendf(akbuf *, unsigned char *, ...);
akbuf_table *akbuf_table_init(akbuf_ctxh, unsigned int);
akbuf_table_entry *akbuf_table_entry_add(akbuf_ctxh, akbuf_table *, unsigned char *, akbuf *);
akbuf_table_entry *akbuf_table_entry_add_buf(akbuf_ctxh, akbuf_table *, akbuf *, akbuf *);
akbuf_table_entry *akbuf_table_entry_add_str(akbuf_ctxh, akbuf_table *, unsigned char *, unsigned char *);
akbuf_table_entry *akbuf_table_entry_find(akbuf_table *, unsigned char *);
akbuf *akbuf_table_entry_get(akbuf_table *, unsigned char *);
unsigned char *akbuf_table_entry_get_str(akbuf_table *, unsigned char *);
void akbuf_urlencode_data(unsigned char *, AKsize_t, akbuf *);
void akbuf_base64encode_data(unsigned char *, AKsize_t, akbuf *);
#define akbuf_free_ctx(ctx) _akbuf_free_ctx((ctx), __FILE__, __LINE__)
#define akbuf_idx(buf) ((buf)->idx)
#define akbuf_size(buf) ((buf)->size)
#define akbuf_data(buf) ((buf)->head)
#define akbuf_empty(buf) (akbuf_idx(buf) == 0)
#define akbuf_strcpy(buf, str) akbuf_set_data((buf), (str), strlen(str))
#define akbuf_append_str(buf, str) akbuf_append_data((buf), (str), strlen(str))
#define akbuf_clone(buf, src) akbuf_set_data((buf), akbuf_data(src), akbuf_idx(src))
#define akbuf_append_buf(buf, src) akbuf_append_data((buf), akbuf_data(src), akbuf_idx(src))
#define akbuf_urlencode(buf, out) akbuf_urlencode(akbuf_data(buf), akbuf_idx(buf), (out))
#define akbuf_split(buf, dest, idx)\
{\
akbuf_set_data((dest), akbuf_data((buf)), (idx));\
akbuf_consume((buf), (idx) + 1);\
}
#define akbuf_set_byte(buf, idx, byte)\
{\
AKbuf_ASSERT((unsigned int)(idx) < akbuf_idx(buf) && (unsigned int)(idx) < akbuf_size(buf));\
*(akbuf_data(buf) + (idx)) = (byte);\
}
#define akbuf_consume_end(buf, len)\
{\
AKbuf_ASSERT((unsigned int)(len) <= akbuf_idx(buf));\
akbuf_idx(buf) -= (len);\
}
#define akbuf_set_idx(buf, idx)\
{\
AKbuf_ASSERT((unsigned int)(idx) <= akbuf_size(buf));\
akbuf_idx(buf) = (idx);\
}
#define akbuf_get_byte(buf, idx, byte)\
{\
AKbuf_ASSERT((unsigned int)(idx) < akbuf_idx(buf));\
(byte) = *(akbuf_data(buf) + (idx));\
}

3
asio/AKepoll.h Normal file
View file

@ -0,0 +1,3 @@
int AKepoll_create(int);
int AKepoll_ctl(int, int, int, struct epoll_event *);
int AKepoll_wait(int, struct epoll_event *, int, int);

60
asio/AKepoll.s Normal file
View file

@ -0,0 +1,60 @@
.globl AKepoll_create
.globl AKepoll_ctl
.globl AKepoll_wait
AKepoll_create:
pushl %ebp
movl %esp,%ebp
addl $4,%ebp
pushl %ebx
movl 4(%ebp),%ebx #size
movl $254,%eax #__NR_epoll_create
int $0x80
popl %ebx
popl %ebp
ret
AKepoll_ctl:
pushl %ebp
movl %esp,%ebp
addl $4,%ebp
pushl %ebx
pushl %ecx
pushl %edx
pushl %esi
movl 4(%ebp),%ebx #epfd
movl 8(%ebp),%ecx #op
movl 12(%ebp),%edx #fd
movl 16(%ebp),%esi #event
movl $255,%eax #__NR_epoll_ctl
int $0x80
popl %esi
popl %edx
popl %ecx
popl %ebx
popl %ebp
ret
AKepoll_wait:
pushl %ebp
movl %esp,%ebp
addl $4,%ebp
pushl %ebx
pushl %ecx
pushl %edx
pushl %esi
movl 4(%ebp),%ebx #epfd
movl 8(%ebp),%ecx #events
movl 12(%ebp),%edx #maxevents
movl 16(%ebp),%esi #timeout
movl $256,%eax #__NR_epoll_wait
int $0x80
popl %esi
popl %edx
popl %ecx
popl %ebx
popl %ebp
ret

6
asio/Makefile Normal file
View file

@ -0,0 +1,6 @@
CC=gcc
CCFLAGS=-Werror -ggdb #-pg
all: asio.o
asio.o: asio.c asio.h
$(CC) $(CCFLAGS) -c asio.c

352
asio/asio.c Normal file
View file

@ -0,0 +1,352 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <errno.h>
#include "asio.h"
#ifdef ASIO_USE_SELECT
#include <sys/select.h>
#endif
#ifdef ASIO_USE_EPOLL
#include <sys/epoll.h>
#endif
#ifdef ASIO_USE_POLL
#include <sys/poll.h>
#endif
#ifdef ASIO_USE_RTSIG
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <fcntl.h>
#include <signal.h>
#include <sys/poll.h>
#endif
#if !defined(ASIO_USE_SELECT) && !defined(ASIO_USE_EPOLL) && !defined(ASIO_USE_POLL) && !defined(ASIO_USE_RTSIG)
#error You must define one of ASIO_USE_SELECT, ASIO_USE_EPOLL, ASIO_USE_POLL, and ASIO_USE_RTSIG
#endif
#if defined(ASIO_USE_FIONBIO) && defined(ASIO_USE_RTSIG)
#error You cannot defined both ASIO_USE_FIONBIO and ASIO_USE_RTSIG
#endif
#ifndef MAX
#define MAX(a, b) (((a) > (b))? (a) : (b))
#endif
#ifndef MIN
#define MIN(a, b) (((a) < (b))? (a) : (b))
#endif
#ifdef ASIO_USE_SELECT
fd_set asio_rfds, asio_wfds;
int highest_rfd, highest_wfd;
#endif
#ifdef ASIO_USE_EPOLL
int epoll_fd;
#ifdef ASIO_USE_AKEPOLL
#include "AKepoll.h"
#else
#define AKepoll_create epoll_create
#define AKepoll_ctl epoll_ctl
#define AKepoll_wait epoll_wait
#endif
#endif
#ifdef ASIO_USE_POLL
struct pollfd pollfds[ASIO_MAX_FDS];
unsigned int num_pollfds;
#endif
void asio_init(void)
{
#ifdef ASIO_USE_RTSIG
sigset_t sigs;
#endif
#ifdef ASIO_USE_SELECT
FD_ZERO(&asio_rfds);
FD_ZERO(&asio_wfds);
highest_rfd = highest_wfd = -1;
#endif
#ifdef ASIO_USE_EPOLL
if ((epoll_fd = AKepoll_create(ASIO_MAX_FDS)) == -1) { perror("epoll_create()"); exit(1); }
#endif
#ifdef ASIO_USE_POLL
num_pollfds = 0;
#endif
#ifdef ASIO_USE_RTSIG
sigemptyset(&sigs);
sigaddset(&sigs, SIGRTMIN + 1);
sigprocmask(SIG_BLOCK, &sigs, NULL);
#endif
}
int asio_add_fd(int fd, asio_event_type events)
{
#ifdef ASIO_USE_EPOLL
struct epoll_event ev;
#endif
#ifdef ASIO_USE_POLL
unsigned int i;
#endif
#ifdef ASIO_USE_RTSIG
int flags;
#endif
#ifdef ASIO_USE_FIONBIO
int flags;
#endif
if (fd < 0 || fd >= ASIO_MAX_FDS) return -1;
#ifdef ASIO_USE_SELECT
if (events & ASIO_R)
{
FD_SET(fd, &asio_rfds);
if (fd > highest_rfd) highest_rfd = fd;
}
if (events & ASIO_W)
{
FD_SET(fd, &asio_wfds);
if (fd > highest_wfd) highest_wfd = fd;
}
#endif
#ifdef ASIO_USE_EPOLL
ev.data.fd = fd;
ev.events = 0;
if (events & ASIO_R) ev.events |= EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP;
if (events & ASIO_W) ev.events |= EPOLLOUT | EPOLLERR | EPOLLHUP;
if (AKepoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) != 0) { perror("epoll_ctl()"); return -1; }
#endif
#ifdef ASIO_USE_POLL
if (num_pollfds >= ASIO_MAX_FDS) return -1;
for (i = 0; i < num_pollfds; i ++) if (pollfds[i].fd == fd || pollfds[i].events == 0) break;
pollfds[i].fd = fd;
pollfds[i].revents = 0;
pollfds[i].events = 0;
if (events & ASIO_R) pollfds[i].events |= POLLIN | POLLPRI | POLLERR | POLLHUP;
if (events & ASIO_W) pollfds[i].events |= POLLOUT | POLLERR | POLLHUP;
if (i == num_pollfds) num_pollfds ++;
#endif
#ifdef ASIO_USE_RTSIG
if ((flags = fcntl(fd, F_GETFL, 0)) < 0) { perror("fcntl() F_GETFL"); return -1; }
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK | O_ASYNC | O_RDWR) < 0) { perror("fcntl() F_SETFL"); return -1; }
if (fcntl(fd, F_SETSIG, SIGRTMIN + 1) != 0) { perror("fcntl() F_SETSIG"); return -1; }
if (fcntl(fd, F_SETOWN, getpid()) != 0) { perror("fcntl() F_SETOWN"); return -1; }
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK | O_ASYNC | O_RDWR) < 0) { perror("fcntl() F_SETFL 2"); return -1; }
#endif
#ifdef ASIO_USE_FIONBIO
flags = 1;
if (ioctl(fd, FIONBIO, &flags) != 0) { perror("ioctl()"); return -1; }
#endif
return 0;
}
int asio_set_events(int fd, asio_event_type events)
{
#ifdef ASIO_USE_EPOLL
struct epoll_event ev;
#endif
#ifdef ASIO_USE_POLL
unsigned int i;
#endif
#ifdef ASIO_USE_SELECT
return asio_add_fd(fd, events);
#endif
#ifdef ASIO_USE_EPOLL
ev.data.fd = fd;
ev.events = 0;
if (events & ASIO_R) ev.events |= EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP;
if (events & ASIO_W) ev.events |= EPOLLOUT | EPOLLERR | EPOLLHUP;
if (AKepoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev) != 0) { perror("epoll_ctl()"); return -1; }
return 0;
#endif
#ifdef ASIO_USE_POLL
for (i = 0; i < num_pollfds; i ++) if (pollfds[i].fd == fd) break;
if (i == num_pollfds) return -1;
pollfds[i].events = 0;
if (events & ASIO_R) pollfds[i].events |= POLLIN | POLLPRI | POLLERR | POLLHUP;
if (events & ASIO_W) pollfds[i].events |= POLLOUT | POLLERR | POLLHUP;
return 0;
#endif
#ifdef ASIO_USE_RTSIG
return 0; /*XXX*/
#endif
}
int asio_del_fd(int fd, asio_event_type events)
{
#ifdef ASIO_USE_EPOLL
struct epoll_event ev;
#endif
#ifdef ASIO_USE_POLL
unsigned int i;
#endif
if (fd < 0 || fd >= ASIO_MAX_FDS) return -1;
#ifdef ASIO_USE_SELECT
if (FD_ISSET(fd, &asio_rfds) && fd == highest_rfd) highest_rfd --;
FD_CLR(fd, &asio_rfds);
if (FD_ISSET(fd, &asio_wfds) && fd == highest_wfd) highest_wfd --;
FD_CLR(fd, &asio_wfds);
#endif
#ifdef ASIO_USE_EPOLL
ev.data.fd = fd;
ev.events = 0;
if (events & ASIO_R) ev.events |= EPOLLIN | EPOLLPRI;
if (events & ASIO_W) ev.events |= EPOLLOUT;
AKepoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev);
#endif
#ifdef ASIO_USE_POLL
for (i = 0; i < num_pollfds; i ++) if (pollfds[i].fd == fd) break;
if (i == num_pollfds) return - 1;
pollfds[i].fd = -1;
pollfds[i].events = 0;
if (num_pollfds - 1 == i) num_pollfds --;
#endif
return 0;
}
asio_event_list *asio_wait_for_events(void)
{
static asio_event_list ret;
#ifdef ASIO_USE_SELECT
fd_set event_rfds, event_wfds;
struct timeval tv;
int i;
#endif
#ifdef ASIO_USE_EPOLL
struct epoll_event epoll_events[ASIO_MAX_FDS];
int i, j;
#endif
#ifdef ASIO_USE_POLL
int i, j;
#endif
#ifdef ASIO_USE_RTSIG
sigset_t sigs;
siginfo_t sigi;
struct timespec ts;
int sig;
#endif
ret.num_events = 0;
#ifdef ASIO_USE_SELECT
tv.tv_sec = 1;
tv.tv_usec = 0;
event_rfds = asio_rfds;
event_wfds = asio_wfds;
if (select(MAX(highest_rfd, highest_wfd) + 1, &event_rfds, &event_wfds, NULL, &tv) > 0)
{
for (i = 0; i <= highest_rfd; i ++) if (FD_ISSET(i, &event_rfds))
{
assert(ret.num_events < ASIO_MAX_FDS);
ret.events[ret.num_events].event = ASIO_R;
ret.events[ret.num_events].fd = i;
ret.num_events ++;
}
for (i = 0; i <= highest_wfd; i ++) if (FD_ISSET(i, &event_wfds))
{
assert(ret.num_events < ASIO_MAX_FDS);
ret.events[ret.num_events].event = ASIO_W;
ret.events[ret.num_events].fd = i;
ret.num_events ++;
}
}
#endif
#ifdef ASIO_USE_EPOLL
if ((j = AKepoll_wait(epoll_fd, epoll_events, ASIO_MAX_FDS, 1000)) < 0)
{
return &ret; /*XXX*/
}
if (j > 0)
{
for (i = 0; i < j; i ++)
{
if (epoll_events[i].events & (EPOLLERR | EPOLLHUP))
{
assert(ret.num_events < ASIO_MAX_FDS);
ret.events[ret.num_events].event = ASIO_R | ASIO_W;
ret.events[ret.num_events].fd = epoll_events[i].data.fd;
ret.num_events ++;
} else
{
if (epoll_events[i].events & (EPOLLIN | EPOLLPRI))
{
assert(ret.num_events < ASIO_MAX_FDS);
ret.events[ret.num_events].event = ASIO_R;
ret.events[ret.num_events].fd = epoll_events[i].data.fd;
ret.num_events ++;
}
if (epoll_events[i].events & EPOLLOUT)
{
assert(ret.num_events < ASIO_MAX_FDS);
ret.events[ret.num_events].event = ASIO_W;
ret.events[ret.num_events].fd = epoll_events[i].data.fd;
ret.num_events ++;
}
}
}
}
#endif
#ifdef ASIO_USE_POLL
if ((j = poll(pollfds, num_pollfds, 1000)) < 0)
{
if (errno == EINTR || errno == EAGAIN) return &ret;
perror("poll()"); return NULL;
}
for (i = 0; i < num_pollfds; i ++)
{
if (pollfds[i].revents & (POLLERR | POLLHUP) && pollfds[i].events & (POLLIN | POLLOUT))
{
assert(ret.num_events < ASIO_MAX_FDS);
ret.events[ret.num_events].fd = pollfds[i].fd;
ret.events[ret.num_events].event = 0;
if (pollfds[i].events & POLLIN) ret.events[ret.num_events].event |= ASIO_R;
if (pollfds[i].events & POLLOUT) ret.events[ret.num_events].event |= ASIO_W;
ret.num_events ++;
} else
{
if (pollfds[i].revents & (POLLIN | POLLPRI))
{
assert(ret.num_events < ASIO_MAX_FDS);
ret.events[ret.num_events].event = ASIO_R;
ret.events[ret.num_events].fd = pollfds[i].fd;
ret.num_events ++;
}
if (pollfds[i].revents & POLLOUT)
{
assert(ret.num_events < ASIO_MAX_FDS);
ret.events[ret.num_events].event = ASIO_W;
ret.events[ret.num_events].fd = pollfds[i].fd;
ret.num_events ++;
}
}
}
#endif
#ifdef ASIO_USE_RTSIG
sigemptyset(&sigs);
sigaddset(&sigs, SIGRTMIN + 1);
memset(&sigi, 0, sizeof(sigi));
ts.tv_sec = 1;
ts.tv_nsec = 0;
if ((sig = sigtimedwait(&sigs, &sigi, &ts)) == SIGRTMIN + 1)
{
//if (sigi.si_band & POLLIN) printf("fd %d event IN\n", sigi.si_fd);
//if (sigi.si_band & POLLOUT) printf("fd %d event OUT\n", sigi.si_fd);
assert(ret.num_events < ASIO_MAX_FDS);
ret.events[ret.num_events].fd = sigi.si_fd;
ret.events[ret.num_events].event = 0;
if (sigi.si_band & POLLIN) ret.events[ret.num_events].event |= ASIO_R;
if (sigi.si_band & POLLOUT) ret.events[ret.num_events].event |= ASIO_W;
if (sigi.si_band & (POLLERR | POLLHUP)) ret.events[ret.num_events].event |= ASIO_R | ASIO_W; /*XXX*/
ret.num_events ++;
}
#endif
return &ret;
}

47
asio/asio.h Normal file
View file

@ -0,0 +1,47 @@
/* Use vanilla select()? */
//#define ASIO_USE_SELECT
/* Use poll()? */
//#define ASIO_USE_POLL
/* Use RTSIGs? (experimental, use at your own risk, works somewhat on Linux atleast) */
//#define ASIO_USE_RTSIG
/* Use epoll? (Works well (Linux >= 2.6), read README) */
#define ASIO_USE_EPOLL
/* Set non-blocking IO using FIONBIO (You will want to define this unless you have ASIO_USE_SIGIO) */
#define ASIO_USE_FIONBIO
#ifdef ASIO_USE_SELECT
#define ASIO_MAX_FDS FD_SETSIZE
#else
#define ASIO_MAX_FDS 131072
#endif
#define ASIO_R 0x1
#define ASIO_W 0x2
typedef unsigned int asio_event_type;
struct asio_event_entry_s
{
asio_event_type event;
int fd;
};
typedef struct asio_event_entry_s asio_event_entry;
struct asio_event_list_s
{
unsigned int num_events;
asio_event_entry events[ASIO_MAX_FDS];
};
typedef struct asio_event_list_s asio_event_list;
void asio_init(void);
int asio_add_fd(int, asio_event_type);
int asio_set_events(int, asio_event_type);
int asio_del_fd(int, asio_event_type);
asio_event_list *asio_wait_for_events(void);

261
cfg.c Normal file
View file

@ -0,0 +1,261 @@
#include "hypercube.h"
static akbuf_ctxh cfg_ctx;
FILE *cfg_f = NULL;
hypercube_config cfg;
enum { CFG_NONE, CFG_BOOL, CFG_INT, CFG_STR, CFG_TABLE, CFG_INADDR, CFG_REGEX_TABLE, CFG_FUNC };
int cfg_func_include(unsigned char *, unsigned int, unsigned char **, unsigned int);
int cfg_func_echo(unsigned char *, unsigned int, unsigned char **, unsigned int);
struct hypercube_config_token
{
unsigned char *key;
unsigned int type;
void *val;
} hypercube_config_tokens[] =
{
{ "listen_port", CFG_INT, &cfg.listen_port },
{ "listen_addr", CFG_INADDR, &cfg.listen_addr },
{ "default_root", CFG_STR, &cfg.default_root },
{ "log", CFG_BOOL, &cfg.log },
{ "log_level", CFG_STR, &cfg.log_level },
{ "log_file", CFG_STR, &cfg.log_file },
{ "background", CFG_BOOL, &cfg.background },
{ "run_as_uid", CFG_INT, &cfg.run_as_uid },
{ "run_as_gid", CFG_INT, &cfg.run_as_gid },
{ "chroot_dir", CFG_STR, &cfg.chroot_dir },
{ "vhost", CFG_TABLE, &cfg.vhosts },
{ "mime", CFG_TABLE, &cfg.mime_types },
{ "rewrite", CFG_REGEX_TABLE, &cfg.rewrite_rules },
{ "include", CFG_FUNC, cfg_func_include },
{ "echo", CFG_FUNC, cfg_func_echo },
{ "allow", CFG_REGEX_TABLE, &cfg.allow_clients },
{ "deny", CFG_REGEX_TABLE, &cfg.deny_clients },
/* tracker */
{ "tracker_interval", CFG_INT, &cfg.tracker.interval },
{ "tracker_init_interval", CFG_INT, &cfg.tracker.init_interval },
{ "tracker_timeout", CFG_INT, &cfg.tracker.timeout },
{ "tracker_stopped_timeout", CFG_INT, &cfg.tracker.stopped_timeout },
{ "tracker_respnum", CFG_INT, &cfg.tracker.respnum },
{ "tracker_period", CFG_INT, &cfg.tracker.period },
{ "tracker_sql_stats", CFG_BOOL, &cfg.tracker.sql_stats },
{ "tracker_sql_host", CFG_STR, &cfg.tracker.sql_host },
{ "tracker_sql_db", CFG_STR, &cfg.tracker.sql_db },
{ "tracker_sql_user", CFG_STR, &cfg.tracker.sql_user },
{ "tracker_sql_pass", CFG_STR, &cfg.tracker.sql_pass },
{ "tracker_statslog", CFG_STR, &cfg.tracker.statslog },
{ "tracker_sync", CFG_BOOL, &cfg.tracker.sync },
{ "tracker_sync_interval", CFG_INT, &cfg.tracker.sync_interval },
{ "tracker_sync_size", CFG_INT, &cfg.tracker.sync_size },
{ "tracker_sync_addr", CFG_INADDR, &cfg.tracker.sync_addr },
{ "tracker_sync_port", CFG_INT, &cfg.tracker.sync_port },
{ "tracker_infohash_file", CFG_STR, &cfg.tracker.infohash_file },
{ "tracker_infohash_interval",CFG_INT, &cfg.tracker.infohash_interval },
{ NULL, CFG_NONE, NULL },
};
#define CFG_ERR_HDR() fprintf(stderr, "Error in configuration '%s' on line %u:\n ", filename, curline);
int cfg_load(unsigned char *filename, unsigned int level)
{
FILE *f;
unsigned char readbuf[BUF_SIZE], *p;
#define MAX_CFG_TOKENS 16
unsigned char *tokenv[MAX_CFG_TOKENS];
unsigned int tokenc, i;
unsigned int curline;
unsigned int bool_val;
curline = 0;
if (level == 0)
{
if (cfg_f == NULL || fseek(cfg_f, 0, SEEK_SET) != 0)
{
if ((f = fopen(filename, "r")) == NULL) { fprintf(stderr, "Unable to open config file '%s': %s\n", filename, strerror(errno)); return -1; }
cfg_f = f;
} else
{
f = cfg_f;
}
} else
{
if ((f = fopen(filename, "r")) == NULL) { fprintf(stderr, "Unable to open config file '%s': %s\n", filename, strerror(errno)); return -1; }
}
while (fgets(readbuf, sizeof(readbuf) - 1, f) != NULL)
{
curline ++;
if ((p = strpbrk(readbuf, "\n\r")) != NULL) *p = 0;
tokenc = 0;
p = readbuf;
while (*p == ' ' || *p == '\t') p ++;
if (*p == '#') continue;
bool_val = 1;
DEBUGF("cfg line %s:%u [%s]", filename, curline, p);
if (*p == 0) continue;
p = strtok(p, " \t");
while (tokenc < MAX_CFG_TOKENS && p != NULL && *p != '#')
{
/* handle !foo / no foo */
if (tokenc == 0)
{
if (*p == '!') { bool_val = 0; p ++; }
else if (strcasecmp(p, "no") == 0) { bool_val = 0; p = NULL; }
}
if (p != NULL && *p != 0) tokenv[tokenc ++] = strdup(p);
p = strtok(NULL, " \t");
}
if (tokenc == 0) continue;
for (i = 0; hypercube_config_tokens[i].key != NULL; i ++) if (strcasecmp(hypercube_config_tokens[i].key, tokenv[0]) == 0) break;
if (hypercube_config_tokens[i].key == NULL) { CFG_ERR_HDR(); fprintf(stderr, "Unknown key '%s'\n", tokenv[0]); return -1; }
if (hypercube_config_tokens[i].type == CFG_BOOL)
{
if (tokenc != 1) { CFG_ERR_HDR(); fprintf(stderr, "Invalid number of arguments (expected [!]%s)\n", tokenv[0]); return -1; }
DEBUGF("bool value '%s' = %u", hypercube_config_tokens[i].key, bool_val);
*(int *)hypercube_config_tokens[i].val = bool_val;
} else if (bool_val != 0) switch (hypercube_config_tokens[i].type)
{
case CFG_NONE: break;
case CFG_INT:
{
if (tokenc != 2) { CFG_ERR_HDR(); fprintf(stderr, "Invalid number of arguments (expected %s <number>)\n", tokenv[0]); return -1; }
*(int *)hypercube_config_tokens[i].val = atoi(tokenv[1]);
break;
}
case CFG_STR:
{
unsigned char **dest;
if (tokenc != 2) { CFG_ERR_HDR(); fprintf(stderr, "Invalid number of arguments (expected %s <string>)\n", tokenv[0]); return -1; }
dest = hypercube_config_tokens[i].val;
*dest = strdup(tokenv[1]);
break;
}
case CFG_TABLE:
{
akbuf_table *tbl;
unsigned char **dest;
if (tokenc != 3) { CFG_ERR_HDR(); fprintf(stderr, "Invalid number of arguments (expected %s <key> <value>)\n", tokenv[0]); return -1; }
dest = hypercube_config_tokens[i].val;
tbl = (akbuf_table *)*dest;
akbuf_table_entry_add_str(cfg_ctx, tbl, tokenv[1], tokenv[2]);
break;
}
case CFG_REGEX_TABLE:
{
akbuf_table *tbl;
unsigned char **dest;
akbuf *compbuf, *valbuf;
int ret;
if (tokenc != 2 && tokenc != 3) { CFG_ERR_HDR(); fprintf(stderr, "Invalid number of arguments (expected %s <regex> [arg])\n", tokenv[0]); return -1; }
compbuf = akbuf_init(cfg_ctx, sizeof(regex_t));
if ((ret = regcomp((regex_t *)akbuf_data(compbuf), tokenv[1], REG_EXTENDED)) != 0)
{
unsigned char errbuf[BUF_SIZE];
regerror(ret, (regex_t *)akbuf_data(compbuf), errbuf, sizeof(errbuf));
fprintf(stderr, "Parsing of regex '%s' failed: %s\n", tokenv[1], errbuf);
return -1;
}
akbuf_set_idx(compbuf, sizeof(regex_t));
valbuf = akbuf_init(cfg_ctx, 0);
akbuf_strcpy(valbuf, tokenv[2]);
dest = hypercube_config_tokens[i].val;
tbl = (akbuf_table *)*dest;
akbuf_table_entry_add_buf(cfg_ctx, tbl, compbuf, valbuf);
akbuf_free(cfg_ctx, valbuf);
break;
}
case CFG_INADDR:
{
struct in_addr *addr;
if (tokenc != 2) { CFG_ERR_HDR(); fprintf(stderr, "Invalid number of arguments (expected %s <IP address>)\n", tokenv[0]); return -1; }
addr = hypercube_config_tokens[i].val;
if ((addr->s_addr = inet_addr(tokenv[1])) == -1) { CFG_ERR_HDR(); fprintf(stderr, "Invalid IP address '%s'\n", tokenv[1]); return -1; }
break;
}
case CFG_FUNC:
{
int (*func)();
func = hypercube_config_tokens[i].val;
if (func != NULL) if (func(filename, curline, tokenv, tokenc) != 0) return -1;
break;
}
}
}
return 0;
}
int cfg_func_include(unsigned char *filename, unsigned int curline, unsigned char **tokenv, unsigned int tokenc)
{
if (tokenc != 2)
{
CFG_ERR_HDR();
fprintf(stderr, "Invalid number of arguments (expected %s <filename>)\n", tokenv[0]);
return -1;
}
return cfg_load(tokenv[1], 1);
}
int cfg_func_echo(unsigned char *filename, unsigned int curline, unsigned char **tokenv, unsigned int tokenc)
{
unsigned char *msg;
if (tokenc < 2) msg = ""; else msg = tokenv[1];
printf("%s:%u: %s\n", filename, curline, msg);
return 0;
}
void cfg_init(void)
{
cfg_ctx = akbuf_new_ctx();
cfg.listen_port = 8000;
cfg.listen_addr.s_addr = INADDR_ANY;
cfg.default_root = "dox/";
cfg.log = 1;
cfg.log_level = "request";
cfg.log_file = NULL;
cfg.background = 0;
cfg.run_as_uid = cfg.run_as_gid = -1;
cfg.chroot_dir = NULL;
cfg.tracker.statslog = NULL;
cfg.vhosts = akbuf_table_init(cfg_ctx, AKBUF_TABLE_NOCASE);
cfg.mime_types = akbuf_table_init(cfg_ctx, AKBUF_TABLE_NOCASE);
cfg.rewrite_rules = akbuf_table_init(cfg_ctx, AKBUF_TABLE_BIN);
cfg.allow_clients = akbuf_table_init(cfg_ctx, AKBUF_TABLE_BIN);
cfg.deny_clients = akbuf_table_init(cfg_ctx, AKBUF_TABLE_BIN);
/* tracker */
cfg.tracker.interval = 360;
cfg.tracker.timeout = 360 * 2;
cfg.tracker.stopped_timeout = 300 * 2;
cfg.tracker.respnum = 50;
cfg.tracker.period = 15;
cfg.tracker.sql_stats = 0;
cfg.tracker.sql_host = NULL;
cfg.tracker.sql_db = NULL;
cfg.tracker.sql_user = NULL;
cfg.tracker.sql_pass = NULL;
cfg.tracker.sync = 0;
cfg.tracker.sync_interval = 15;
cfg.tracker.sync_size = 1400;
cfg.tracker.sync_addr.s_addr = INADDR_BROADCAST;
cfg.tracker.sync_port = 4242;
cfg.tracker.infohash_file = NULL;
cfg.tracker.infohash_interval = 30;
if (cfg_load(CFG_FILE, 0) != 0) exit(1);
}
void cfg_reload(void)
{
akbuf_free_ctx(cfg_ctx);
cfg_init();
}

49
cfg.h Normal file
View file

@ -0,0 +1,49 @@
typedef struct tracker_config_s tracker_config;
struct tracker_config_s
{
unsigned int interval;
unsigned int init_interval;
unsigned int timeout;
unsigned int stopped_timeout;
unsigned int respnum;
unsigned int period;
unsigned char *statslog;
unsigned int sql_stats;
unsigned char *sql_host;
unsigned char *sql_db;
unsigned char *sql_user;
unsigned char *sql_pass;
unsigned int sync;
unsigned int sync_interval;
unsigned int sync_size;
struct in_addr sync_addr;
unsigned int sync_port;
unsigned char *infohash_file;
unsigned int infohash_interval;
};
typedef struct hypercube_config_s hypercube_config;
struct hypercube_config_s
{
int listen_port;
struct in_addr listen_addr;
unsigned char *default_root;
unsigned int log;
unsigned char *log_level;
unsigned char *log_file;
unsigned int background;
int run_as_uid;
int run_as_gid;
unsigned char *chroot_dir;
akbuf_table *vhosts;
akbuf_table *mime_types;
akbuf_table *rewrite_rules;
akbuf_table *allow_clients;
akbuf_table *deny_clients;
tracker_config tracker;
};
int cfg_load(unsigned char *, unsigned int);
void cfg_init(void);
void cfg_reload(void);

21
config.h.dist Normal file
View file

@ -0,0 +1,21 @@
//#define DEBUG
#define CFG_FILE "hypercube.cfg"
#define LISTEN_BACKLOG 65535
#define SOCKET_TIMEOUT 15
#define MAX_LINE_LEN (BUF_SIZE * 2)
#define BUF_SIZE 4096
#define SEND_BUF_SIZE 16384
#define HTTP_MAX_HEADERS 16
#define HTTP_MAX_CONTENT_LEN 32768
#define CGI_MIME_TYPE "application/x-cgi"
#define CGI_DEFAULT_PATH "/usr/local/bin:/usr/xpg4/bin:/usr/ccs/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/ucb/bin"
#define GZIP_SUFFIX "HCgz"
/*
#define NUM_RESP_PEERS 50
#define ANNOUNCE_INTERVAL 420
#define PEER_TIMEOUT (ANNOUNCE_INTERVAL * 2)
#define TRACKER_PERIOD_INTERVAL 15
*/

970
http.c Normal file
View file

@ -0,0 +1,970 @@
#include "hypercube.h"
http_fd_entry http_fds[ASIO_MAX_FDS];
static akbuf_ctxh http_ctx;
static akbuf *parsebufs[8];
extern hypercube_config cfg;
void http_init(void)
{
unsigned int i;
http_ctx = akbuf_new_ctx();
for (i = 0; i < sizeof(parsebufs) / sizeof(parsebufs[0]); i ++) parsebufs[i] = akbuf_init(http_ctx, 0);
for (i = 0; i < ASIO_MAX_FDS; i ++) http_fds[i].state = HTTP_FD_UNUSED;
}
void http_unset_fd(int fd)
{
if (http_fds[fd].state != HTTP_FD_UNUSED)
{
if (http_fds[fd].state != HTTP_FD_CGI) aklogf(LOG_CONNECTION, "Client on socket %d disconnected.", fd);
http_fds[fd].state = HTTP_FD_UNUSED;
}
}
void http_handle_sent(int fd, net_fd_entry *net_ent)
{
DEBUGF("send on socket %d done", fd);
if (http_fds[fd].keep_alive == 0)
{
net_unset_fd(fd);
} else
{
http_fds[fd].state = HTTP_FD_NEW_REQ;
}
}
unsigned char *http_status_msg(unsigned int code)
{
switch (code)
{
case HTTP_OK: return "OK";
case HTTP_PARTIAL_CONTENT: return "Partial content";
case HTTP_MOVED_PERM: return "Moved permanently";
case HTTP_BAD_REQUEST: return "Bad request";
case HTTP_FORBIDDEN: return "Forbidden";
case HTTP_NOT_FOUND: return "Not found";
case HTTP_INTERNAL_ERROR: return "Internal error";
case HTTP_SERVER_TOO_BUSY: return "Server too busy";
case HTTP_LOG_HOOK: return "Handled by hook";
case HTTP_FORBIDDEN_BANNED: return "Forbidden (client banned)";
}
return "Unspecified";
}
unsigned char *http_method_str(unsigned int method)
{
switch (method)
{
case HTTP_METHOD_GET: return "GET";
case HTTP_METHOD_HEAD: return "HEAD";
case HTTP_METHOD_POST: return "POST";
}
return "(none)";
}
void http_log_query(int fd, net_fd_entry *net_ent, unsigned int code)
{
unsigned char *ua_p, *ref_p, *host_p;
if ((host_p = akbuf_table_entry_get_str(http_fds[fd].headers, "Host")) == NULL) host_p = "(none)";
if ((ua_p = akbuf_table_entry_get_str(http_fds[fd].headers, "User-agent")) == NULL) ua_p = "(none)";
if ((ref_p = akbuf_table_entry_get_str(http_fds[fd].headers, "Referer")) == NULL) ref_p = "(none)";
akbuf_asciiz(net_ent->peerbuf);
akbuf_asciiz(http_fds[fd].uri);
aklogf(LOG_REQUEST, "HTTP/%u.%u %d %s %s \"%s\" %u \"%s\" \"%s\" \"%s\" \"%s\"",
http_fds[fd].ver_maj, http_fds[fd].ver_min,
fd,
akbuf_data(net_ent->peerbuf),
http_method_str(http_fds[fd].method), akbuf_data(http_fds[fd].uri),
(code == HTTP_LOG_HOOK)? HTTP_OK : code, http_status_msg(code),
host_p, ref_p, ua_p);
}
void http_error(int fd, net_fd_entry *net_ent, unsigned int code)
{
unsigned char *msg;
http_log_query(fd, net_ent, code);
msg = http_status_msg(code);
if (code >= 2000) code -= 2000;
http_fds[fd].keep_alive = 0;
akbuf_sprintf(net_ent->sendbuf,
"HTTP/1.1 %u %s\r\n"
"Server: " SERVER_VERSION "\r\n"
"Content-type: text/html\r\n"
"Connection: close\r\n"
"\r\n"
"<HTML><HEAD><TITLE>%u %s</TITLE></HEAD><BODY><H3>%u %s</H3></BODY></HTML>\r\n",
code, msg,
code, msg, code, msg);
net_send(fd);
}
unsigned char *get_mime_type(unsigned char *filename)
{
unsigned int i;
unsigned char *p;
i = strlen(filename);
while (i >= 1) { if (filename[i - 1] == '.') break; i --; }
if (i > 0)
{
DEBUGF("get_mime_type() extension %s", &filename[i]);
if ((p = akbuf_table_entry_get_str(cfg.mime_types, &filename[i])) != NULL) return p;
}
return "application/octet-stream";
}
void cgi_handle_output(int fd, net_fd_entry *net_ent, akbuf *buf)
{
if (http_fds[fd].rpipe_net_ent->type == HTTP_FD_UNUSED)
{
net_unset_fd(fd);
return;
}
akbuf_append_buf(http_fds[fd].rpipe_net_ent->sendbuf, buf);
net_send(http_fds[fd].rpipe_fd);
}
void cgi_handle_sent_content(int fd, net_fd_entry *net_ent, akbuf *buf)
{
net_unset_fd(fd);
}
int serve_cgi_to_fd(int fd, net_fd_entry *net_ent, unsigned char *cgi_name)
{
int cgi_rpipe[2], cgi_wpipe[2];
/* parsebufs: 0,1,2 == env var tmp */
unsigned int has_content;
DEBUGF("serving cgi [%s]", cgi_name);
cgi_rpipe[0] = cgi_rpipe[1] = -1;
if (pipe(cgi_rpipe) != 0 || !FD_VALID(cgi_rpipe[0]) || !FD_VALID(cgi_rpipe[1]))
{
if (cgi_rpipe[0] != -1) close(cgi_rpipe[0]);
if (cgi_rpipe[1] != -1) close(cgi_rpipe[1]);
http_error(fd, net_ent, HTTP_SERVER_TOO_BUSY);
return -2;
}
if (http_fds[fd].method == HTTP_METHOD_POST && http_fds[fd].content_len > 0)
{
has_content = 1;
cgi_wpipe[0] = cgi_wpipe[1] = -1;
if (pipe(cgi_wpipe) != 0 || !FD_VALID(cgi_wpipe[0]) || !FD_VALID(cgi_wpipe[1]))
{
if (cgi_wpipe[0] != -1) close(cgi_wpipe[0]);
if (cgi_wpipe[1] != -1) close(cgi_wpipe[1]);
http_error(fd, net_ent, HTTP_SERVER_TOO_BUSY);
return -2;
}
} else
{
has_content = 0;
}
if (fork() == 0)
{
unsigned char *envs[64];
unsigned int i;
int j;
if (has_content == 1)
{
close(cgi_wpipe[1]);
dup2(cgi_wpipe[0], 0);
}
close(cgi_rpipe[0]);
dup2(cgi_rpipe[1], 1);
i = 0;
/* We can use strdup() here because the process will end anyway. */
akbuf_sprintf(parsebufs[0], "GATEWAY_INTERFACE=CGI/1.1");
akbuf_asciiz(parsebufs[0]);
envs[i ++] = strdup(akbuf_data(parsebufs[0]));
akbuf_asciiz(http_fds[fd].query);
akbuf_sprintf(parsebufs[0], "QUERY_STRING=%s", akbuf_data(http_fds[fd].query));
akbuf_asciiz(parsebufs[0]);
envs[i ++] = strdup(akbuf_data(parsebufs[0]));
akbuf_sprintf(parsebufs[0], "PATH=" CGI_DEFAULT_PATH);
akbuf_asciiz(parsebufs[0]);
envs[i ++] = strdup(akbuf_data(parsebufs[0]));
akbuf_sprintf(parsebufs[0], "SERVER_SOFTWARE=" SERVER_VERSION);
akbuf_asciiz(parsebufs[0]);
envs[i ++] = strdup(akbuf_data(parsebufs[0]));
akbuf_sprintf(parsebufs[0], "SERVER_PROTOCOL=HTTP/%u.%u", http_fds[fd].ver_maj, http_fds[fd].ver_min);
akbuf_asciiz(parsebufs[0]);
envs[i ++] = strdup(akbuf_data(parsebufs[0]));
akbuf_clone(parsebufs[1], net_ent->peerbuf);
if ((j = akbuf_chr(parsebufs[1], ':')) != -1)
{
akbuf_split(parsebufs[1], parsebufs[2], j);
akbuf_asciiz(parsebufs[1]);
akbuf_sprintf(parsebufs[0], "REMOTE_ADDR=%s", akbuf_data(parsebufs[2]));
akbuf_asciiz(parsebufs[0]);
envs[i ++] = strdup(akbuf_data(parsebufs[0]));
akbuf_sprintf(parsebufs[0], "REMOTE_PORT=%s", akbuf_data(parsebufs[1]));
akbuf_asciiz(parsebufs[0]);
envs[i ++] = strdup(akbuf_data(parsebufs[0]));
}
akbuf_sprintf(parsebufs[0], "SCRIPT_FILENAME=%s", cgi_name);
akbuf_asciiz(parsebufs[0]);
envs[i ++] = strdup(akbuf_data(parsebufs[0]));
akbuf_sprintf(parsebufs[0], "REQUEST_METHOD=%s", http_method_str(http_fds[fd].method));
akbuf_asciiz(parsebufs[0]);
envs[i ++] = strdup(akbuf_data(parsebufs[0]));
akbuf_asciiz(http_fds[fd].uri);
akbuf_sprintf(parsebufs[0], "REQUEST_URI=%s", akbuf_data(http_fds[fd].uri));
akbuf_asciiz(parsebufs[0]);
envs[i ++] = strdup(akbuf_data(parsebufs[0]));
if (has_content == 1)
{
akbuf_sprintf(parsebufs[0], "CONTENT_LENGTH=%u", (unsigned int)http_fds[fd].content_len);
akbuf_asciiz(parsebufs[0]);
envs[i ++] = strdup(akbuf_data(parsebufs[0]));
}
envs[i] = NULL;
execle(cgi_name, cgi_name, NULL, envs);
akperror("execve() CGI script");
exit(1);
}
if (has_content == 1)
{
close(cgi_wpipe[0]);
net_set_fd(cgi_wpipe[1], NET_FD_SEND, NULL, cgi_handle_sent_content, 1);
net_send_buf(cgi_wpipe[1], http_fds[fd].content);
http_fds[fd].wpipe_fd = cgi_wpipe[1];
}
DEBUGF("wpipe_fd %d", http_fds[fd].wpipe_fd);
close(cgi_rpipe[1]);
net_set_fd(cgi_rpipe[0], NET_FD_READ, cgi_handle_output, NULL, 1);
http_fds[cgi_rpipe[0]].rpipe_fd = fd;
http_fds[cgi_rpipe[0]].rpipe_net_ent = net_ent;
http_fds[cgi_rpipe[0]].ctx = http_fds[fd].ctx;
http_fds[cgi_rpipe[0]].state = HTTP_FD_CGI;
http_fds[cgi_rpipe[0]].method = HTTP_METHOD_NONE;
http_fds[cgi_rpipe[0]].uri = NULL;
http_fds[cgi_rpipe[0]].query = NULL;
http_fds[cgi_rpipe[0]].ver_maj = http_fds[fd].ver_min = 0;
http_fds[cgi_rpipe[0]].keep_alive = 0;
http_fds[cgi_rpipe[0]].num_headers = 0;
http_fds[cgi_rpipe[0]].headers = NULL;
http_fds[cgi_rpipe[0]].num_args = 0;
http_fds[cgi_rpipe[0]].args = NULL;
http_fds[fd].state = HTTP_FD_PIPING;
http_fds[fd].rpipe_fd = cgi_rpipe[0];
net_set_callbacks(fd, NULL, NULL);
akbuf_sprintf(net_ent->sendbuf,
"HTTP/1.1 %u OK\r\n"
"Server: " SERVER_VERSION "\r\n"
"Connection: close\r\n",
HTTP_OK);
net_send(fd);
return 0;
}
/* return: 0 == all ok; -1 == error, show errorpage; -2 == error, errorpage shown */
int serve_doc_to_fd(int fd, net_fd_entry *net_ent, unsigned char *orig_doc_name, unsigned char *doc_name, unsigned int doc_off, AKsize_t doc_size, unsigned int add_gzip_header)
{
unsigned char *mime_type;
unsigned char timebuf[512];
unsigned int code;
struct stat st;
DEBUGF("serve_doc_to_fd() orig [%s] real [%s]", orig_doc_name, doc_name);
mime_type = get_mime_type(orig_doc_name);
if (strcasecmp(mime_type, CGI_MIME_TYPE) == 0)
{
return serve_cgi_to_fd(fd, net_ent, orig_doc_name);
}
if (access(doc_name, R_OK) != 0) return -1;
http_log_query(fd, net_ent, HTTP_OK);
if (http_fds[fd].method != HTTP_METHOD_HEAD && (net_ent->send_fd = open(doc_name, O_RDONLY)) < 0) return -1;
if (doc_off >= doc_size) doc_off = (doc_size > 0)? doc_size - 1 : 0;
net_ent->send_fd_off = doc_off;
net_ent->send_fd_len = doc_size - doc_off;
code = (doc_off != 0)? HTTP_PARTIAL_CONTENT : HTTP_OK;
if (http_fds[fd].ver_maj > 0)
{
if (stat(doc_name, &st) == 0)
{
size_t len;
struct tm *tmp;
time_t t;
t = time(NULL);
tmp = gmtime(&t);
if ((len = strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S %Z", tmp)) == 0) timebuf[0] = 0;
} else
{
timebuf[0] = 0;
}
if (code == HTTP_PARTIAL_CONTENT)
{
akbuf_sprintf(net_ent->sendbuf,
"HTTP/1.1 %u %s\r\n"
"Server: " SERVER_VERSION "\r\n"
"Content-type: %s\r\n"
"Content-length: %u\r\n"
"Content-range: bytes %u-%u/%u\r\n"
"Connection: %s\r\n"
"Last-Modified: %s\r\n"
"\r\n",
code, http_status_msg(code),
get_mime_type(orig_doc_name),
doc_size - doc_off,
doc_off, (doc_size == 0)? 0 : doc_size - 1, doc_size,
(http_fds[fd].keep_alive == 1)? "keep-alive" : "close",
timebuf);
} else
{
if (add_gzip_header == 1)
{
akbuf_sprintf(net_ent->sendbuf,
"HTTP/1.1 %u %s\r\n"
"Server: " SERVER_VERSION "\r\n"
"Content-type: %s\r\n"
"Content-length: %u\r\n"
"Connection: %s\r\n"
"Last-Modified: %s\r\n"
"Content-Encoding: gzip\r\n"
"\r\n",
code, http_status_msg(code),
get_mime_type(orig_doc_name),
doc_size - doc_off,
(http_fds[fd].keep_alive == 1)? "keep-alive" : "close",
timebuf);
} else
{
akbuf_sprintf(net_ent->sendbuf,
"HTTP/1.1 %u %s\r\n"
"Server: " SERVER_VERSION "\r\n"
"Content-type: %s\r\n"
"Content-length: %u\r\n"
"Connection: %s\r\n"
"Last-Modified: %s\r\n"
"\r\n",
code, http_status_msg(code),
get_mime_type(orig_doc_name),
doc_size - doc_off,
(http_fds[fd].keep_alive == 1)? "keep-alive" : "close",
timebuf);
}
}
}
net_send(fd);
return 0;
}
void http_dir_index(int fd, net_fd_entry *net_ent, unsigned char *dir_path)
{
DIR *dir;
struct dirent *de;
struct stat st;
/* parsebufs: 4 == generated document, 5 == appended data */
if ((dir = opendir(dir_path)) == NULL)
{
http_error(fd, net_ent, (errno == EPERM)? HTTP_FORBIDDEN : HTTP_NOT_FOUND);
return;
}
http_log_query(fd, net_ent, HTTP_OK);
akbuf_asciiz(http_fds[fd].uri);
akbuf_sprintf(parsebufs[4],
"<HTML>\n"
"<HEAD><TITLE>Index of %s</TITLE></HEAD>\n"
"<BODY>\n"
"<H3>Index of %s</H3>\n",
akbuf_data(http_fds[fd].uri), akbuf_data(http_fds[fd].uri));
while ((de = readdir(dir)) != NULL)
{
akbuf_sprintf(parsebufs[5], "%s/%s", dir_path, de->d_name);
akbuf_asciiz(parsebufs[5]);
if (stat(akbuf_data(parsebufs[5]), &st) == 0)
{
akbuf_sprintf(parsebufs[5], "<A HREF=\"%s%s\">%s%s</A> %u<BR>\n", de->d_name, (st.st_mode & S_IFDIR)? "/" : "", de->d_name, (st.st_mode & S_IFDIR)? "/" : "", st.st_size);
akbuf_append_buf(parsebufs[4], parsebufs[5]);
}
}
closedir(dir); /* :-D */
akbuf_sprintf(parsebufs[5], "</BODY></HTML>\n");
akbuf_append_buf(parsebufs[4], parsebufs[5]);
if (http_fds[fd].ver_maj > 0)
{
akbuf_sprintf(net_ent->sendbuf,
"HTTP/1.1 %u OK\r\n"
"Server: " SERVER_VERSION "\r\n"
"Content-type: text/html\r\n"
"Content-length: %u\r\n"
"Connection: %s\r\n"
"\r\n",
HTTP_OK,
akbuf_idx(parsebufs[4]),
(http_fds[fd].keep_alive == 1)? "keep-alive" : "close");
}
akbuf_append_buf(net_ent->sendbuf, parsebufs[4]);
}
unsigned int http_has_access(akbuf *peerbuf, akbuf *uri)
{
akbuf_table_entry *ent;
akbuf *addrbuf;
akbuf_ctxh ctx;
int i;
ctx = akbuf_new_ctx();
addrbuf = akbuf_init(ctx, 0);
akbuf_clone(addrbuf, peerbuf);
if ((i = akbuf_chr(addrbuf, ':')) != -1) akbuf_set_byte(addrbuf, i, 0);
ent = cfg.allow_clients->head;
while (ent != NULL)
{
if (regexec((regex_t *)akbuf_data(ent->key), akbuf_data(addrbuf), 0, NULL, 0) == 0)
{
akbuf_asciiz(ent->data);
akbuf_asciiz(uri);
if (strncmp(akbuf_data(uri), akbuf_data(ent->data), akbuf_idx(ent->data)) == 0)
{
DEBUGF("check_access match [%s] [%s]", akbuf_data(addrbuf), akbuf_data(uri));
break;
}
}
ent = ent->next;
}
if (ent != NULL) return 1;
ent = cfg.deny_clients->head;
while (ent != NULL)
{
if (regexec((regex_t *)akbuf_data(ent->key), akbuf_data(addrbuf), 0, NULL, 0) == 0)
{
akbuf_asciiz(ent->data);
akbuf_asciiz(uri);
if (strncmp(akbuf_data(uri), akbuf_data(ent->data), akbuf_idx(ent->data)) == 0)
{
DEBUGF("check_access match [%s] [%s]", akbuf_data(addrbuf), akbuf_data(uri));
break;
}
}
ent = ent->next;
}
akbuf_free_ctx(ctx);
return (ent == NULL)? 1 : 0;
}
void http_do_rewrite(akbuf *rbuf)
{
akbuf_table_entry *ent;
unsigned int i, c, rem;
regmatch_t matches[10];
/* parsebufs: 6 == saved original rbuf */
akbuf_asciiz(rbuf);
DEBUGF("doing rewrite on [%s]", akbuf_data(rbuf));
ent = cfg.rewrite_rules->head;
while (ent != NULL)
{
if (regexec((regex_t *)akbuf_data(ent->key), akbuf_data(rbuf), sizeof(matches) / sizeof(matches[0]), matches, 0) == 0)
{
DEBUGF("rewrite regexec() match");
akbuf_clone(parsebufs[6], rbuf);
akbuf_set_idx(rbuf, 0);
i = 0;
rem = akbuf_idx(ent->data);
while (rem > 0)
{
akbuf_get_byte(ent->data, i, c); i ++; rem --;
switch (c)
{
case '\\':
if (rem >= 1)
{
akbuf_get_byte(ent->data, i, c); i ++; rem --;
akbuf_append_byte(rbuf, c);
} else
{
aklogf(LOG_ERROR, "Error in rewrite rule: Stray \\ at end of line.");
}
break;
case '$':
if (rem >= 1)
{
akbuf_get_byte(ent->data, i, c) i ++; rem --;
c -= '0';
if (c >= sizeof(matches) / sizeof(matches[0]) || matches[c].rm_so == -1)
{
aklogf(LOG_ERROR, "Erorr in rewrite rule: Unknown variable $%u.", c);
break;
}
AKassert(matches[c].rm_eo >= matches[c].rm_so);
akbuf_append_data(rbuf, akbuf_data(parsebufs[6]) + matches[c].rm_so, matches[c].rm_eo - matches[c].rm_so);
}
break;
default:
akbuf_append_byte(rbuf, c);
break;
}
}
return;
} else
{
DEBUGF("rewrite regexec() nomatch");
}
ent = ent->next;
}
}
void http_index(int fd, net_fd_entry *net_ent, unsigned char *dir_path, unsigned int doc_off)
{
unsigned int diridx;
unsigned int i;
struct stat st;
unsigned char *indexdocs[] = { "index.html", "index.htm", NULL };
/* parsebufs: 3 == full path to try */
akbuf_strcpy(parsebufs[3], dir_path);
akbuf_append_byte(parsebufs[3], '/');
diridx = akbuf_idx(parsebufs[3]);
i = 0;
while (indexdocs[i] != NULL)
{
akbuf_append_str(parsebufs[3], indexdocs[i]);
akbuf_asciiz(parsebufs[3]);
DEBUGF("trying index '%s'", akbuf_data(parsebufs[3]));
if (stat(akbuf_data(parsebufs[3]), &st) == 0 && st.st_mode & S_IFREG)
{
if (serve_doc_to_fd(fd, net_ent, akbuf_data(parsebufs[3]), akbuf_data(parsebufs[3]), doc_off, st.st_size, 0) == 0) break;
}
akbuf_set_idx(parsebufs[3], diridx);
i ++;
}
if (indexdocs[i] == NULL) http_dir_index(fd, net_ent, dir_path);
}
unsigned int http_parse_range(akbuf *range)
{
int i;
unsigned int cur_val, c;
/* XXX support for ranges other than bytes=###- */
if ((i = akbuf_chr(range, '=')) == -1 || ++ i >= akbuf_idx(range)) return 0;
cur_val = 0;
for (; i < akbuf_idx(range); i ++)
{
akbuf_get_byte(range, i, c);
if (c > '9' || c < '0') break;
c -= '0';
cur_val *= 10;
cur_val += c;
}
DEBUGF("got range %u", cur_val);
return cur_val;
}
int http_serve_hook(int fd, net_fd_entry *net_ent)
{
struct serve_hook_entry
{
unsigned char *uri;
void (*serve_fn)();
} serve_hooks[] =
{
{ "announce", tracker_serve_announce },
{ "announce.php", tracker_serve_announce },
{ "announcephp", tracker_serve_announce },
{ "scrape", tracker_serve_scrape },
{ "scrape.php", tracker_serve_scrape },
{ "scrapephp", tracker_serve_scrape },
{ "status", tracker_serve_status },
{ "peers", tracker_serve_peers },
{ NULL, NULL },
};
unsigned int i;
akbuf_ctxh ctx;
akbuf *uribuf;
akbuf_asciiz(http_fds[fd].uri);
ctx = akbuf_new_ctx();
uribuf = akbuf_init(ctx, 0);
akbuf_clone(uribuf, http_fds[fd].uri);
while (akbuf_idx(uribuf) > 1 && akbuf_data(uribuf)[0] == '/') akbuf_eat_byte(uribuf);
akbuf_asciiz(uribuf);
i = 0;
while (serve_hooks[i].uri != NULL)
{
if (strcmp(akbuf_data(uribuf), serve_hooks[i].uri) == 0)
{
http_log_query(fd, net_ent, HTTP_LOG_HOOK);
serve_hooks[i].serve_fn(fd, &http_fds[fd], net_ent);
akbuf_free_ctx(ctx);
return i;
}
i ++;
}
akbuf_free_ctx(ctx);
return -1;
}
void http_serve_req(int fd, net_fd_entry *net_ent)
{
unsigned char *root_dir, *host_p, *p;
akbuf_ctxh ctx;
akbuf *host_buf, *buf;
unsigned char resolved_path[PATH_MAX + 1], resolved_root[PATH_MAX + 1];
struct stat st, st2;
int i;
unsigned int range_start;
/* parsebufs: 0 == req path 1 == req path */
#define SERVE_ERR(code)\
{\
http_error(fd, net_ent, (code));\
akbuf_free_ctx(ctx);\
return;\
}
DEBUGF("serving req");
ctx = akbuf_new_ctx();
if ((p = akbuf_table_entry_get_str(http_fds[fd].headers, "Connection")) != NULL)
{
DEBUGF("header connection = %s", p);
if (strcasecmp(p, "keep-alive") == 0) http_fds[fd].keep_alive = 1;
else if (strcasecmp(p, "close") == 0) http_fds[fd].keep_alive = 0;
}
if ((buf = akbuf_table_entry_get(http_fds[fd].headers, "Range")) != NULL)
{
range_start = http_parse_range(buf);
} else
{
range_start = 0;
}
if ((buf = akbuf_table_entry_get(http_fds[fd].headers, "Host")) != NULL)
{
host_buf = akbuf_init(ctx, 0);
akbuf_clone(host_buf, buf); akbuf_asciiz(host_buf);
root_dir = akbuf_table_entry_get_str(cfg.vhosts, akbuf_data(host_buf));
if (root_dir == NULL && (i = akbuf_chr(buf, ':')) != -1)
{
akbuf_set_idx(buf, i);
akbuf_asciiz(buf);
root_dir = akbuf_table_entry_get_str(cfg.vhosts, akbuf_data(buf));
}
if (root_dir == NULL) root_dir = cfg.default_root;
host_p = akbuf_data(host_buf);
} else
{
if (http_fds[fd].ver_maj > 0 && (http_fds[fd].ver_maj != 1 || http_fds[fd].ver_min != 0)) SERVE_ERR(HTTP_BAD_REQUEST);
akbuf_asciiz(net_ent->sockbuf);
host_p = akbuf_data(net_ent->sockbuf);
root_dir = cfg.default_root;
}
DEBUGF("host [%s] root [%s]", host_p, root_dir);
http_do_rewrite(http_fds[fd].uri);
akbuf_asciiz(http_fds[fd].uri);
DEBUGF("rewrote => [%s]", akbuf_data(http_fds[fd].uri));
if (http_has_access(net_ent->peerbuf, http_fds[fd].uri) == 0) SERVE_ERR(HTTP_FORBIDDEN_BANNED);
if (http_serve_hook(fd, net_ent) >= 0) { akbuf_free_ctx(ctx); return; }
if (realpath(root_dir, resolved_root) == NULL) SERVE_ERR((errno == EPERM)? HTTP_FORBIDDEN : HTTP_NOT_FOUND);
resolved_root[sizeof(resolved_root) - 1] = 0;
akbuf_strcpy(parsebufs[0], resolved_root);
akbuf_append_buf(parsebufs[0], http_fds[fd].uri);
akbuf_asciiz(parsebufs[0]);
DEBUGF("req path [%s]", akbuf_data(parsebufs[0]));
if (realpath(akbuf_data(parsebufs[0]), resolved_path) == NULL) SERVE_ERR((errno == EPERM)? HTTP_FORBIDDEN : HTTP_NOT_FOUND);
resolved_path[sizeof(resolved_path) - 1] = 0;
DEBUGF("resolved path [%s] root %s", resolved_path, resolved_root);
if (strlen(resolved_path) < strlen(resolved_root) || strncmp(resolved_path, resolved_root, strlen(resolved_root)) != 0) SERVE_ERR(HTTP_NOT_FOUND);
if (stat(resolved_path, &st) != 0) SERVE_ERR((errno == EPERM)? HTTP_FORBIDDEN : HTTP_NOT_FOUND);
if (st.st_mode & S_IFDIR)
{
unsigned int c;
if (akbuf_empty(http_fds[fd].uri)) SERVE_ERR(HTTP_BAD_REQUEST);
if (access(resolved_path, X_OK) != 0) SERVE_ERR((errno == EPERM || errno == EACCES)? HTTP_FORBIDDEN : HTTP_NOT_FOUND);
akbuf_get_byte(http_fds[fd].uri, akbuf_idx(http_fds[fd].uri) - 1, c);
if (c == '/')
{
http_index(fd, net_ent, resolved_path, range_start);
} else
{
http_log_query(fd, net_ent, HTTP_MOVED_PERM);
akbuf_asciiz(http_fds[fd].uri);
buf = akbuf_init(ctx, 0);
akbuf_sprintf(buf,
"<HTML>\r\n"
"<HEAD><TITLE>The document has moved</TITLE></HEAD>\r\n"
"<BODY>New address <A HREF=\"http://%s%s/\">http://%s%s/</a></BODY>\r\n"
"</HTML>\r\n\r\n",
host_p, akbuf_data(http_fds[fd].uri),
host_p, akbuf_data(http_fds[fd].uri));
akbuf_sprintf(net_ent->sendbuf,
"HTTP/1.1 %u Moved permanently\r\n"
"Server: " SERVER_VERSION "\r\n"
"Content-type: text/html\r\n"
"Content-length: %u\r\n"
"Connection: %s\r\n"
"Location: http://%s%s/\r\n"
"\r\n",
HTTP_MOVED_PERM,
akbuf_idx(buf),
(http_fds[fd].keep_alive == 1)? "keep-alive" : "close",
host_p, akbuf_data(http_fds[fd].uri));
akbuf_append_buf(net_ent->sendbuf, buf);
}
net_send(fd);
akbuf_free_ctx(ctx);
return;
}
akbuf_strcpy(parsebufs[0], resolved_path);
akbuf_append_byte(parsebufs[0], '.');
akbuf_append_str(parsebufs[0], GZIP_SUFFIX);
akbuf_asciiz(parsebufs[0]);
if (akbuf_table_entry_get(http_fds[fd].headers, "Accept-Encoding") != NULL && stat(akbuf_data(parsebufs[0]), &st2) == 0 && !(st2.st_mode & S_IFDIR))
{
if (serve_doc_to_fd(fd, net_ent, resolved_path, akbuf_data(parsebufs[0]), range_start, st2.st_size, 1) == -1) SERVE_ERR((errno == EPERM || errno == EACCES)? HTTP_FORBIDDEN : HTTP_NOT_FOUND);
} else
{
if (serve_doc_to_fd(fd, net_ent, resolved_path, resolved_path, range_start, st.st_size, 0) == -1) SERVE_ERR((errno == EPERM || errno == EACCES)? HTTP_FORBIDDEN : HTTP_NOT_FOUND);
}
akbuf_free_ctx(ctx);
}
AKssize_t http_unescape(akbuf *inbuf, akbuf *outbuf)
{
unsigned int rem, i, c, d;
akbuf_ctxh ctx;
akbuf *buf;
ctx = AKbuf_INVALID_CTX;
if (outbuf == NULL)
{
ctx = akbuf_new_ctx();
buf = akbuf_init(ctx, akbuf_idx(inbuf));
} else
{
buf = outbuf;
}
i = 0;
rem = akbuf_idx(inbuf);
while (rem > 0)
{
akbuf_get_byte(inbuf, i, d); i ++; rem --;
if (d == '%' && rem >= 2)
{
akbuf_get_byte(inbuf, i, c); i ++; rem --;
if (c >= 'a') c -= 'a' - 0xa;
else if (c >= 'A') c -= 'A' - 0xa;
else if (c >= '0') c -= '0';
else break;
d = c << 4;
akbuf_get_byte(inbuf, i, c); i ++; rem --;
if (c >= 'a') c -= 'a' - 0xa;
else if (c >= 'A') c -= 'A' - 0xa;
else if (c >= '0') c -= '0';
else break;
d |= c;
if (d > 0xff) break;
}
akbuf_append_byte(buf, d);
}
if (rem > 0) { if (ctx != AKbuf_INVALID_CTX) akbuf_free_ctx(ctx); return -rem; }
if (outbuf == NULL) akbuf_clone(inbuf, buf);
if (ctx != AKbuf_INVALID_CTX) akbuf_free_ctx(ctx);
return i;
}
void http_parse_arg(akbuf *arg, akbuf *var, akbuf *val)
{
int i;
akbuf_set_idx(var, 0);
akbuf_set_idx(val, 0);
if ((i = akbuf_chr(arg, '=')) != -1)
{
akbuf_split(arg, var, i);
akbuf_clone(val, arg);
} else
{
akbuf_clone(var, arg);
}
http_unescape(var, NULL);
http_unescape(val, NULL);
}
void http_parse_args(int fd, net_fd_entry *net_ent, akbuf *args)
{
akbuf_ctxh ctx;
akbuf *splitargs, *splitarg;
akbuf *var, *val;
int i;
ctx = akbuf_new_ctx();
splitargs = akbuf_init(ctx, 0);
splitarg = akbuf_init(ctx, 0);
var = akbuf_init(ctx, 0);
val = akbuf_init(ctx, 0);
akbuf_clone(splitargs, args);
while ((i = akbuf_chr(splitargs, '&')) != -1)
{
akbuf_split(splitargs, splitarg, i);
http_parse_arg(splitarg, var, val);
akbuf_asciiz(var);
#ifdef DEBUG
akbuf_asciiz(val);
DEBUGF("http_parse_args(): var [%s] val [%s]\n", akbuf_data(var), akbuf_data(val));
#endif
akbuf_table_entry_add(http_fds[fd].ctx, http_fds[fd].args, akbuf_data(var), val);
}
if (akbuf_idx(splitargs) > 0)
{
http_parse_arg(splitargs, var, val);
akbuf_asciiz(var);
#ifdef DEBUG
akbuf_asciiz(val);
DEBUGF("http_parse_args(): last var [%s] val [%s]\n", akbuf_data(var), akbuf_data(val));
#endif
akbuf_table_entry_add(http_fds[fd].ctx, http_fds[fd].args, akbuf_data(var), val);
}
akbuf_free_ctx(ctx);
}
void http_handle_action(int fd, net_fd_entry *net_ent, akbuf *linebuf)
{
int i;
unsigned int c;
/* parsebufs: 0 == method, 1 == uri */
#define ACTION_ERR()\
{\
http_error(fd, net_ent, HTTP_BAD_REQUEST);\
return;\
}
DEBUGF("http action");
if ((i = akbuf_chr(linebuf, ' ')) == -1) ACTION_ERR();
akbuf_split(linebuf, parsebufs[0], i);
akbuf_asciiz(parsebufs[0]);
if (strcasecmp(akbuf_data(parsebufs[0]), "GET") == 0) http_fds[fd].method = HTTP_METHOD_GET;
else if (strcasecmp(akbuf_data(parsebufs[0]), "HEAD") == 0) http_fds[fd].method = HTTP_METHOD_HEAD;
else if (strcasecmp(akbuf_data(parsebufs[0]), "POST") == 0) http_fds[fd].method = HTTP_METHOD_POST;
else ACTION_ERR();
if ((i = akbuf_chr(linebuf, ' ')) != -1)
{
unsigned int cur_ver, seen_dot;
akbuf_split(linebuf, parsebufs[1], i);
if ((i = akbuf_chr(linebuf, '/')) == -1) ACTION_ERR();
if (++ i >= akbuf_idx(linebuf)) ACTION_ERR();
cur_ver = seen_dot = 0;
for (; i < akbuf_idx(linebuf); i ++)
{
akbuf_get_byte(linebuf, i, c);
if (c == '.')
{
if (seen_dot == 0) http_fds[fd].ver_maj = cur_ver;
else if (seen_dot == 1) http_fds[fd].ver_min = cur_ver;
seen_dot ++;
cur_ver = 0;
} else
{
if (c > '9' || c < '0') ACTION_ERR();
cur_ver *= 10;
cur_ver += c - '0';
}
}
if (seen_dot == 0) http_fds[fd].ver_maj = cur_ver;
else if (seen_dot == 1) http_fds[fd].ver_min = cur_ver;
} else
{
akbuf_clone(parsebufs[1], linebuf);
http_fds[fd].ver_maj = 0;
http_fds[fd].ver_min = 9;
}
if ((i = akbuf_chr(parsebufs[1], '?')) != -1)
{
if (i == 0) ACTION_ERR();
akbuf_set_data(http_fds[fd].query, akbuf_data(parsebufs[1]) + i + 1, akbuf_idx(parsebufs[1]) - i - 1);
akbuf_set_idx(parsebufs[1], i);
akbuf_asciiz(http_fds[fd].query); akbuf_asciiz(parsebufs[1]);
DEBUGF("got query [%s] rem [%s]", akbuf_data(http_fds[fd].query), akbuf_data(parsebufs[1]));
http_parse_args(fd, net_ent, http_fds[fd].query);
} else
{
akbuf_set_idx(http_fds[fd].query, 0);
}
if (akbuf_empty(parsebufs[1])) ACTION_ERR();
http_unescape(parsebufs[1], http_fds[fd].uri);
if (akbuf_empty(http_fds[fd].uri)) ACTION_ERR();
akbuf_asciiz(http_fds[fd].uri);
DEBUGF("got http version %u.%u", http_fds[fd].ver_maj, http_fds[fd].ver_min);
akbuf_asciiz(net_ent->peerbuf);
aklogf(LOG_DEBUG, "%d: %s HTTP/%u.%u \"%s %s\"", fd, akbuf_data(net_ent->peerbuf), http_fds[fd].ver_maj, http_fds[fd].ver_min, akbuf_data(parsebufs[0]), akbuf_data(http_fds[fd].uri));
if (http_fds[fd].ver_maj > 0) http_fds[fd].state = HTTP_FD_HEADERS; else http_serve_req(fd, net_ent);
}
void http_handle_content(int fd, net_fd_entry *net_ent, akbuf *buf)
{
unsigned int rem;
rem = http_fds[fd].content_len - akbuf_idx(http_fds[fd].content);
akbuf_append_data(http_fds[fd].content, akbuf_data(buf), (akbuf_idx(buf) > rem)? rem : akbuf_idx(buf));
if (akbuf_idx(http_fds[fd].content) >= http_fds[fd].content_len)
{
http_serve_req(fd, net_ent);
return;
}
}
void http_handle_headers(int fd, net_fd_entry *net_ent, akbuf *linebuf)
{
int i;
/* parsebufs: 0 == header name */
#define HEADERS_ERR()\
{\
http_error(fd, net_ent, HTTP_BAD_REQUEST);\
return;\
}
if (akbuf_empty(linebuf) || (i = akbuf_chr(linebuf, ':')) == -1)
{
DEBUGF("content-len %u", http_fds[fd].content_len);
if (http_fds[fd].content_len != 0)
{
http_fds[fd].state = HTTP_FD_CONTENT;
net_set_callbacks(fd, http_handle_content, http_handle_sent);
net_set_type(fd, NET_FD_READ);
} else
{
http_serve_req(fd, net_ent);
}
return;
}
if (http_fds[fd].num_headers >= HTTP_MAX_HEADERS) HEADERS_ERR();
akbuf_split(linebuf, parsebufs[0], i);
for (i = 0; i < akbuf_idx(linebuf); i ++) if (akbuf_data(linebuf)[i] != ' ' && akbuf_data(linebuf)[i] != '\t') break;
akbuf_consume(linebuf, i);
akbuf_asciiz(parsebufs[0]); akbuf_asciiz(linebuf);
DEBUGF("header [%s] : [%s]", akbuf_data(parsebufs[0]), akbuf_data(linebuf));
if (strcasecmp(akbuf_data(parsebufs[0]), "Content-length") == 0)
{
http_fds[fd].content_len = atoi(akbuf_data(linebuf));
if (http_fds[fd].content_len > HTTP_MAX_CONTENT_LEN) HEADERS_ERR();
}
akbuf_table_entry_add(http_fds[fd].ctx, http_fds[fd].headers, akbuf_data(parsebufs[0]), linebuf);
return;
}
void http_handle_action_and_headers(int fd, net_fd_entry *net_ent, akbuf *linebuf)
{
unsigned int i;
for (i = 0; i < sizeof(parsebufs) / sizeof(parsebufs[0]); i ++) if (akbuf_size(parsebufs[i]) > BUF_SIZE) akbuf_set_size(parsebufs[i], BUF_SIZE);
switch (http_fds[fd].state)
{
case HTTP_FD_UNUSED:
case HTTP_FD_NEW_REQ:
http_fds[fd].ctx = net_ent->ctx;
http_fds[fd].state = HTTP_FD_ACTION;
http_fds[fd].method = HTTP_METHOD_NONE;
http_fds[fd].uri = akbuf_init(net_ent->ctx, 0);
http_fds[fd].query = akbuf_init(net_ent->ctx, 0);
http_fds[fd].content = akbuf_init(net_ent->ctx, 0);
http_fds[fd].content_len = 0;
http_fds[fd].ver_maj = http_fds[fd].ver_min = 0;
http_fds[fd].keep_alive = 0;
http_fds[fd].num_headers = 0;
http_fds[fd].headers = akbuf_table_init(net_ent->ctx, AKBUF_TABLE_NOCASE);
http_fds[fd].num_args = 0;
http_fds[fd].args = akbuf_table_init(net_ent->ctx, AKBUF_TABLE_NOCASE);
http_fds[fd].rpipe_fd = -1;
http_fds[fd].rpipe_net_ent = NULL;
http_fds[fd].wpipe_fd = -1;
http_fds[fd].wpipe_net_ent = NULL;
/* fallthrough */
case HTTP_FD_ACTION:
http_handle_action(fd, net_ent, linebuf);
break;
case HTTP_FD_HEADERS:
http_handle_headers(fd, net_ent, linebuf);
break;
}
}

47
http.h Normal file
View file

@ -0,0 +1,47 @@
enum { HTTP_FD_UNUSED, HTTP_FD_NEW_REQ, HTTP_FD_ACTION, HTTP_FD_HEADERS, HTTP_FD_CONTENT, HTTP_FD_PIPING, HTTP_FD_CGI, HTTP_FD_DIR };
enum { HTTP_METHOD_NONE, HTTP_METHOD_GET, HTTP_METHOD_HEAD, HTTP_METHOD_POST };
#define HTTP_OK 200
#define HTTP_PARTIAL_CONTENT 206
#define HTTP_MOVED_PERM 301
#define HTTP_BAD_REQUEST 400
#define HTTP_FORBIDDEN 403
#define HTTP_NOT_FOUND 404
#define HTTP_INTERNAL_ERROR 500
#define HTTP_SERVER_TOO_BUSY 503
#define HTTP_FORBIDDEN_BANNED 2403
#define HTTP_LOG_HOOK 1000
typedef unsigned int http_fd_state;
struct http_fd_entry_s
{
http_fd_state state;
akbuf_ctxh ctx;
unsigned int method;
akbuf *uri;
akbuf *query;
akbuf *content;
unsigned int num_args;
akbuf_table *args;
AKsize_t content_len;
unsigned int ver_maj, ver_min;
unsigned int keep_alive;
unsigned int num_headers;
akbuf_table *headers;
int rpipe_fd;
net_fd_entry *rpipe_net_ent;
int wpipe_fd;
net_fd_entry *wpipe_net_ent;
DIR *listdir;
akbuf *dirpath;
};
typedef struct http_fd_entry_s http_fd_entry;
void http_init(void);
void http_unset_fd(int);
void http_handle_sent(int, net_fd_entry *);
unsigned char *http_status_msg(unsigned int);
void http_handle_action_and_headers(int, net_fd_entry *, akbuf *);

38
hypercube.cfg.dist Normal file
View file

@ -0,0 +1,38 @@
listen_port 8000
listen_addr 127.0.0.1
default_root /2/anakata/
log
log_level all
#log_file log
no background
#chroot_dir rootdir/
#run_as_uid 242
#run_as_gid 242
rewrite ^/~([^/]+)/?(.*) /bar/$1/$2
#vhost localhost /2/anakata/btdl
vhost wintermute dox2/ #foo
vhost bla dox3/
vhost bla:4242 dox4/
mime html text/html
mime htm text/html
mime txt text/plain
mime jpeg image/jpeg
mime jpg image/jpeg
mime gif image/gif
mime png image/png
mime bmp image/bmp
mime wav audio/x-wav
mime torrent application/x-bittorrent
mime cgi application/x-cgi
no mime foo bar
include tracker.cfg
include access.cfg

61
hypercube.h Normal file
View file

@ -0,0 +1,61 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>
#include <assert.h>
#include <limits.h>
#include <signal.h>
#include <dirent.h>
#include <regex.h>
#ifdef WITH_MYSQL
#include <mysql/mysql.h>
#endif
#include "dist.h"
#include "config.h"
#include "akbuf/akbuf.h"
#include "asio/asio.h"
#ifdef USE_LINUX_SENDFILE
#include <sys/sendfile.h>
#endif
#include "log.h"
#include "net.h"
#include "http.h"
#include "cfg.h"
#include "tracker.h"
#ifdef DEBUG
#define DEBUGF(v...) aklogf(LOG_DEBUG, "DEBUG: " v);
#define AKdassert(cond) AKassert(cond)
#else
#define DEBUGF(v...)
#define AKdassert(cond)
#endif
#define AKassert(c) if (!(c)) { aklogf(LOG_ERROR, "Assertion (" __STRING(c) ") failed @ " __FILE__ ":%u", __LINE__); exit(1); }
#define AKstrcpy(dest, src) { strncpy((dest), (src), sizeof(dest) - 1); (dest)[sizeof(dest) - 1] = 0; }
#ifdef SHORT_SERVER_VERSION
#define SERVER_VERSION_STR "hypercube"
#else
#define SERVER_VERSION_STR "hypercube/1.1alpha tracker/0.1alpha (" SERVER_DIST ") by anakata [anakata-hc@prq.se]"
#endif
#ifdef DEBUG
#define SERVER_VERSION SERVER_VERSION_STR " [debug build]"
#else
#define SERVER_VERSION SERVER_VERSION_STR
#endif

3
hypercube.includes Normal file
View file

@ -0,0 +1,3 @@
.
akbuf
asio

36
hypercube.pro Normal file
View file

@ -0,0 +1,36 @@
TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
DEFINES += USE_LINUX_SENDFILE
#DEFINES += ASIO_USE_POLL
DEFINES += SHORT_SERVER_VERSION
#DEFINES += DEBUG
#DEFINES += AKBUF_DEBUG
# AKbuf sources
SOURCES += akbuf/akbuf.c
HEADERS += akbuf/akbuf.h
# ASIO sources
SOURCES += asio/asio.c
HEADERS += asio/asio.h
# Hypercube sources
HEADERS += config.h
HEADERS += dist.h
HEADERS += hypercube.h
SOURCES += cfg.c
HEADERS += cfg.h
SOURCES += log.c
HEADERS += log.h
SOURCES += net.c
HEADERS += net.h
SOURCES += http.c
HEADERS += http.h
SOURCES += tracker.c
HEADERS += tracker.h
SOURCES += main.c

88
log.c Normal file
View file

@ -0,0 +1,88 @@
#include "hypercube.h"
static akbuf_ctxh log_ctx;
static akbuf *log_buf, *date_buf;
static FILE *log_file;
static unsigned int log_level_val;
unsigned int log_initialized = 0;
extern hypercube_config cfg;
void log_init(void)
{
log_ctx = akbuf_new_ctx();
log_buf = akbuf_init(log_ctx, 0);
date_buf = akbuf_init(log_ctx, 32);
log_level_val = LOG_NONE;
if (cfg.log == 1)
{
switch (cfg.log_level[0])
{
case 'e': case 'E': log_level_val = LOG_ERROR; break;
case 'i': case 'I': log_level_val = LOG_INFO; break;
case 'r': case 'R': log_level_val = LOG_REQUEST; break;
case 'c': case 'C': log_level_val = LOG_CONNECTION; break;
case 'd': case 'D': log_level_val = LOG_DEBUG; break;
case 'a': case 'A': log_level_val = LOG_ALL; break;
default:
fprintf(stderr, "Invalid log level '%s'.\n", cfg.log_level);
exit(1);
break;
}
if (cfg.log_file != NULL)
{
if ((log_file = fopen(cfg.log_file, "a+")) == NULL) { fprintf(stderr, "Couldn't open log file (%s): %s\n", cfg.log_file, strerror(errno)); exit(1); }
} else
{
log_file = stdout;
}
}
log_initialized = 1;
}
unsigned char *get_date_str(time_t t)
{
akbuf_strcpy(date_buf, ctime(&t));
if (akbuf_idx(date_buf) > 0) akbuf_consume_end(date_buf, 1);
akbuf_asciiz(date_buf);
return akbuf_data(date_buf);
}
unsigned char *get_now_date_str(void)
{
return get_date_str(time(NULL));
}
void aklogf(unsigned int level, unsigned char *fmt, ...)
{
va_list va;
unsigned int i, c;
if (cfg.log == 0) return;
if (level > log_level_val) return;
va_start(va, fmt);
if (log_initialized == 0)
{
vprintf(fmt, va);
printf("\n");
va_end(va);
return;
}
akbuf_vsprintf(log_buf, fmt, va);
va_end(va);
for (i = 0; i < akbuf_idx(log_buf); i ++)
{
akbuf_get_byte(log_buf, i, c);
if (c < 0x20 || c > 0x7f) akbuf_set_byte(log_buf, i, '.');
}
akbuf_append_byte(log_buf, '\n');
akbuf_asciiz(log_buf);
fprintf(log_file, "Log: %s %s", get_now_date_str(), akbuf_data(log_buf));
fflush(log_file);
if (log_file != stdout && level == LOG_ERROR) fprintf(stderr, "Log: %s %s\n", get_now_date_str(), akbuf_data(log_buf));
if (akbuf_size(log_buf) > BUF_SIZE) akbuf_set_size(log_buf, BUF_SIZE);
}
void akperror(unsigned char *msg)
{
if (cfg.log == 0) return;
if (msg == NULL || msg[0] == 0) msg = "(none)";
aklogf(LOG_ERROR, "Error: %s %s", msg, strerror(errno));
}

7
log.h Normal file
View file

@ -0,0 +1,7 @@
enum { LOG_NONE, LOG_ERROR, LOG_INFO, LOG_REQUEST, LOG_CONNECTION, LOG_DEBUG, LOG_ALL };
void log_init(void);
void aklogf(unsigned int, unsigned char *, ...);
void akperror(unsigned char *);
unsigned char *get_date_str(time_t);
unsigned char *get_now_date_str(void);

68
main.c Normal file
View file

@ -0,0 +1,68 @@
#include "hypercube.h"
extern hypercube_config cfg;
void mainloop(void)
{
DEBUGF("entering mainloop");
while (1)
{
net_wait_for_events();
#ifdef SOCKET_TIMEOUT
net_periodic();
#endif
tracker_periodic();
}
}
void handle_fatal_sig(int sig)
{
aklogf(LOG_ERROR, "Exiting on signal %d.", sig);
exit(sig);
}
void handle_reload_sig(int sig)
{
aklogf(LOG_INFO, "Got signal %d, reloading config...", sig);
cfg_reload();
}
void final_init(void)
{
struct stat ost, st;
if (stat("/", &ost) != 0) { perror("Couldn't stat /"); exit(1); }
if (cfg.chroot_dir != NULL && (chroot(cfg.chroot_dir) != 0 || chdir("/") != 0)) { perror("Changing root directory"); exit(1); }
if (stat(cfg.default_root, &st) == 0)
{
if (st.st_ino == ost.st_ino)
{
fprintf(stderr, "Refusing to start with filesystem root as default root.\n");
exit(1);
}
} else
{
fprintf(stderr, "Warning: Couldn't access default root (%s): %s\n", cfg.default_root, strerror(errno));
}
if (cfg.run_as_gid != -1 && setgid(cfg.run_as_gid) != 0) { perror("Changing GID"); exit(1); }
if (cfg.run_as_uid != -1 && setuid(cfg.run_as_uid) != 0) { perror("Changing UID"); exit(1); }
if (cfg.background == 1)
{
fclose(stdin); fclose(stdout); fclose(stderr);
if (fork() != 0) exit(0);
}
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
signal(SIGTERM, handle_fatal_sig);
signal(SIGINT, handle_fatal_sig);
signal(SIGHUP, handle_reload_sig);
}
int main(int argc, char *argv[])
{
cfg_init();
log_init();
net_init();
http_init();
tracker_init();
net_start_listen();
final_init();
mainloop();
return 0;
}

298
net.c Normal file
View file

@ -0,0 +1,298 @@
#include "hypercube.h"
net_fd_entry net_fds[ASIO_MAX_FDS];
extern hypercube_config cfg;
time_t period_time;
void net_init(void)
{
unsigned int i;
period_time = time(NULL);
for (i = 0; i < ASIO_MAX_FDS; i ++) net_fds[i].type = NET_FD_UNUSED;
asio_init();
DEBUGF("network initialized");
}
void net_set_fd(int fd, net_fd_type type, void (*data_callback)(), void (*sent_callback)(), unsigned int alloc_buf)
{
AKassert(FD_VALID(fd));
net_fds[fd].type = type;
net_fds[fd].data_callback = data_callback;
net_fds[fd].sent_callback = sent_callback;
net_fds[fd].active_time = time(NULL);
net_fds[fd].ctx = akbuf_new_ctx();
if (alloc_buf == 1)
{
net_fds[fd].readbuf = akbuf_init(net_fds[fd].ctx, 0);
net_fds[fd].linebuf = akbuf_init(net_fds[fd].ctx, 0);
net_fds[fd].sendbuf = akbuf_init(net_fds[fd].ctx, 0);
net_fds[fd].peerbuf = akbuf_init(net_fds[fd].ctx, 0);
net_fds[fd].sockbuf = akbuf_init(net_fds[fd].ctx, 0);
} else
{
net_fds[fd].readbuf = net_fds[fd].linebuf = net_fds[fd].sendbuf = net_fds[fd].peerbuf = net_fds[fd].sockbuf = NULL;
}
net_fds[fd].send_fd = -1;
switch (type)
{
case NET_FD_LISTEN: case NET_FD_READ: case NET_FD_READLINE: case NET_FD_RAW:
if (asio_add_fd(fd, ASIO_R) != 0) { akperror("asio_add_fd()"); exit(1); }
break;
case NET_FD_SEND:
if (asio_add_fd(fd, ASIO_W) != 0) { akperror("asio_add_fd()"); exit(1); }
break;
}
}
void net_set_callbacks(int fd, void (*data_callback)(), void (*sent_callback)())
{
AKassert(FD_VALID(fd));
net_fds[fd].data_callback = data_callback;
net_fds[fd].sent_callback = sent_callback;
}
void net_set_type(int fd, net_fd_type type)
{
AKassert(FD_VALID(fd));
net_fds[fd].type = type;
}
void net_send_buf(int fd, akbuf *buf)
{
AKassert(FD_VALID(fd));
akbuf_append_buf(net_fds[fd].sendbuf, buf);
}
void net_unset_fd(int fd)
{
AKassert(FD_VALID(fd));
DEBUGF("unsetting fd %d", fd);
net_unset_fd_callback(fd);
if (net_fds[fd].type != NET_FD_UNUSED)
{
akbuf_free_ctx(net_fds[fd].ctx);
net_fds[fd].ctx = (akbuf_ctxh)0;
if (net_fds[fd].send_fd != -1) close(net_fds[fd].send_fd);
net_fds[fd].type = NET_FD_UNUSED;
}
asio_del_fd(fd, ASIO_R | ASIO_W);
close(fd);
}
void net_start_listen(void)
{
int sock;
unsigned int i;
struct sockaddr_in sin;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 || sock >= ASIO_MAX_FDS) { akperror("socket()"); exit(1); }
i = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&i, sizeof(i)) != 0) { akperror("setsockopt()"); exit(1); }
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = cfg.listen_addr.s_addr;
sin.sin_port = htons(cfg.listen_port);
if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) != 0) { akperror("bind()"); exit(1); }
if (listen(sock, LISTEN_BACKLOG) != 0) { akperror("listen() (decrease LISTEN_BACKLOG?)"); exit(1); }
net_set_fd(sock, NET_FD_LISTEN, NULL, NULL, 0);
aklogf(LOG_INFO, "Listening on port %d.", cfg.listen_port);
}
void test_readline(int fd, net_fd_entry *net_ent, akbuf *line)
{
akbuf_asciiz(line);
printf("TEST: readline [%s]\n", akbuf_data(line));
}
void net_accept_connection(int fd)
{
struct sockaddr_in sin;
size_t sin_len;
int newfd;
sin_len = sizeof(sin);
memset(&sin, 0, sizeof(sin));
if ((newfd = accept(fd, (struct sockaddr *)&sin, &sin_len)) < 0) { akperror("accept()"); return; }
if (!FD_VALID(newfd))
{
aklogf(LOG_INFO, "Client limit reached. Increase ASIO_MAX_FDS?");
close(newfd);
return;
}
net_set_fd(newfd, NET_FD_INIT_TYPE, net_fd_init_data_callback, net_fd_init_sent_callback, 1);
akbuf_sprintf(net_fds[newfd].peerbuf, "%s:%u", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
sin_len = sizeof(sin);
memset(&sin, 0, sizeof(sin));
if (getsockname(newfd, (struct sockaddr *)&sin, &sin_len) == 0)
{
akbuf_sprintf(net_fds[newfd].sockbuf, "%s:%u", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
} else
{
akbuf_strcpy(net_fds[newfd].sockbuf, "");
}
aklogf(LOG_CONNECTION, "%d: Connection from %s", newfd, akbuf_data(net_fds[newfd].peerbuf));
}
void net_send(int fd)
{
DEBUGF("send on fd %d", fd);
asio_set_events(fd, ASIO_W);
net_fds[fd].type = NET_FD_SEND;
}
void net_sent(int fd)
{
DEBUGF("sent on fd %d", fd);
asio_set_events(fd, ASIO_R);
net_fds[fd].type = NET_FD_INIT_TYPE;
if (net_fds[fd].send_fd != -1) { close(net_fds[fd].send_fd); net_fds[fd].send_fd = -1; }
if (net_fds[fd].sent_callback != NULL) net_fds[fd].sent_callback(fd, &net_fds[fd]);
}
void net_handle_send(int fd)
{
AKssize_t n, m;
if (!akbuf_empty(net_fds[fd].sendbuf))
{
if ((n = write(fd, akbuf_data(net_fds[fd].sendbuf), akbuf_idx(net_fds[fd].sendbuf))) <= 0)
{
if (errno != EINTR && errno != EAGAIN) net_unset_fd(fd);
return;
}
akbuf_consume(net_fds[fd].sendbuf, n);
if (akbuf_empty(net_fds[fd].sendbuf) && net_fds[fd].send_fd == -1)
{
net_sent(fd);
return;
}
} else if (net_fds[fd].send_fd != -1)
{
#ifdef USE_LINUX_SENDFILE
if ((m = sendfile(fd, net_fds[fd].send_fd, &net_fds[fd].send_fd_off, (net_fds[fd].send_fd_len >= SEND_BUF_SIZE)? SEND_BUF_SIZE : net_fds[fd].send_fd_len)) < 0)
{
if (errno != EAGAIN && errno != EINTR) { akperror("sendfile()"); net_unset_fd(fd); }
return;
}
net_fds[fd].send_fd_len -= m;
if (net_fds[fd].send_fd_len == 0) net_sent(fd);
#else
unsigned char readbuf[SEND_BUF_SIZE];
if (net_fds[fd].send_fd_off != 0)
{
if (lseek(fd, net_fds[fd].send_fd_off, SEEK_SET) == (off_t)-1) akperror("lseek()");
net_fds[fd].send_fd_off = 0;
}
if ((n = read(net_fds[fd].send_fd, readbuf, sizeof(readbuf))) <= 0)
{
if (errno != EINTR && errno != EAGAIN) net_sent(fd);
return;
}
if ((m = write(fd, readbuf, n)) <= 0)
{
if (errno != EINTR && errno != EAGAIN) net_unset_fd(fd);
return;
}
if (m < n) akbuf_append_data(net_fds[fd].sendbuf, &readbuf[m], n - m);
#endif
} else
{
net_sent(fd);
}
}
void net_handle_readline(int fd)
{
AKssize_t n;
unsigned char readbuf[BUF_SIZE];
int i, j;
if ((n = read(fd, readbuf, sizeof(readbuf))) <= 0)
{
if (errno != EINTR && errno != EAGAIN) net_unset_fd(fd);
return;
}
DEBUGF("read %d bytes", n);
akbuf_append_data(net_fds[fd].readbuf, readbuf, n);
if (akbuf_idx(net_fds[fd].readbuf) > MAX_LINE_LEN)
{
net_unset_fd(fd);
return;
}
while ((i = akbuf_chr(net_fds[fd].readbuf, '\n')) >= 0 && net_fds[fd].type == NET_FD_READLINE)
{
akbuf_split(net_fds[fd].readbuf, net_fds[fd].linebuf, i);
if ((j = akbuf_chr(net_fds[fd].linebuf, '\r')) >= 0) akbuf_set_idx(net_fds[fd].linebuf, j);
if (net_fds[fd].data_callback != NULL) net_fds[fd].data_callback(fd, &net_fds[fd], net_fds[fd].linebuf);
}
if (net_fds[fd].type == NET_FD_READ && akbuf_idx(net_fds[fd].readbuf) > 0)
{
/* State changed with data remaining in buffer, so handle it. */
if (net_fds[fd].data_callback != NULL) net_fds[fd].data_callback(fd, &net_fds[fd], net_fds[fd].readbuf);
akbuf_set_idx(net_fds[fd].readbuf, 0);
}
}
void net_handle_read(int fd)
{
AKssize_t n;
unsigned char readbuf[BUF_SIZE];
if ((n = read(fd, readbuf, sizeof(readbuf))) <= 0)
{
if (errno != EINTR && errno != EAGAIN) net_unset_fd(fd);
return;
}
DEBUGF("read %d bytes", n);
akbuf_set_data(net_fds[fd].readbuf, readbuf, n);
if (net_fds[fd].data_callback != NULL) net_fds[fd].data_callback(fd, &net_fds[fd], net_fds[fd].readbuf);
}
void net_handle_raw(int fd)
{
DEBUGF("raw event on fd %d", fd);
if (net_fds[fd].data_callback != NULL) net_fds[fd].data_callback(fd, &net_fds[fd], NULL);
}
void net_wait_for_events(void)
{
asio_event_list *evs;
unsigned int i;
int fd;
evs = asio_wait_for_events();
AKassert(evs != NULL);
//DEBUGF("got %u events", evs->num_events);
for (i = 0; i < evs->num_events; i ++)
{
fd = evs->events[i].fd;
AKassert(FD_VALID(fd));
DEBUGF("event on fd %d", fd);
net_fds[fd].active_time = time(NULL);
if (evs->events[i].event & ASIO_R)
{
switch (net_fds[fd].type)
{
case NET_FD_LISTEN: net_accept_connection(fd); break;
case NET_FD_READLINE: net_handle_readline(fd); break;
case NET_FD_READ: net_handle_read(fd); break;
case NET_FD_RAW: net_handle_raw(fd); break;
default: DEBUGF("Unknown revent type %u on fd %d", net_fds[fd].type, fd);
}
}
if (evs->events[i].event & ASIO_W)
{
switch (net_fds[fd].type)
{
case NET_FD_SEND: net_handle_send(fd); break;
default: DEBUGF("Unknown wevent type %u on fd %d", net_fds[fd].type, fd);
}
}
}
}
void net_periodic(void)
{
#ifdef SOCKET_TIMEOUT
unsigned int i;
time_t t;
if (time(NULL) - period_time >= SOCKET_TIMEOUT / 2)
{
t = time(NULL);
for (i = 0; i < ASIO_MAX_FDS; i ++) if (net_fds[i].type != NET_FD_UNUSED && net_fds[i].type != NET_FD_LISTEN && net_fds[i].type != NET_FD_RAW && t - net_fds[i].active_time >= SOCKET_TIMEOUT)
{
net_unset_fd(i);
}
period_time = time(NULL);
}
#endif
}

38
net.h Normal file
View file

@ -0,0 +1,38 @@
#define NET_FD_INIT_TYPE NET_FD_READLINE
#define net_fd_init_data_callback http_handle_action_and_headers
#define net_fd_init_sent_callback http_handle_sent
#define net_unset_fd_callback http_unset_fd
#define FD_VALID(fd) ((unsigned int)(fd) < ASIO_MAX_FDS)
enum { NET_FD_UNUSED, NET_FD_LISTEN, NET_FD_READ, NET_FD_READLINE, NET_FD_SEND, NET_FD_RAW };
typedef unsigned int net_fd_type;
struct net_fd_entry_s
{
net_fd_type type;
void (*data_callback)();
void (*sent_callback)();
time_t active_time;
akbuf_ctxh ctx;
akbuf *peerbuf;
akbuf *sockbuf;
akbuf *readbuf, *linebuf;
akbuf *sendbuf;
int send_fd;
AKsize_t send_fd_len;
off_t send_fd_off;
};
typedef struct net_fd_entry_s net_fd_entry;
void net_init(void);
void net_start_listen(void);
void net_set_fd(int, net_fd_type, void (*)(), void (*)(), unsigned int);
void net_set_callbacks(int, void (*)(), void (*)());
void net_set_type(int, net_fd_type);
void net_send_buf(int, akbuf *);
void net_unset_fd(int);
void net_send(int);
void net_wait_for_events(void);
void net_periodic(void);

2
setdist.sh Executable file
View file

@ -0,0 +1,2 @@
#!/bin/sh
echo "#define SERVER_DIST \"`date` `whoami`@`hostname`\"" > dist.h

1095
tracker.c Normal file

File diff suppressed because it is too large Load diff

33
tracker.cfg.dist Normal file
View file

@ -0,0 +1,33 @@
# Announce interval.
tracker_interval 420
# Initial announce interval
tracker_init_interval 120
# Timeout if no activity from peer.
tracker_timeout 666
# Maximum number of peers in an announce response.
tracker_respnum 64
# Tracker period (peer refresh) interval.
tracker_period 600
# Tracker statistics log file
tracker_statslog statslog
no tracker_sql_stats
tracker_sql_host localhost
tracker_sql_db tracker
tracker_sql_user tracker
tracker_sql_pass trawk123
# Synchronize with other trackers?
tracker_sync
# Synchronization interval
tracker_sync_interval 15
# Max size of a synchronization packet
tracker_sync_size 1400
# Address to send synchronization packets to
tracker_sync_addr 192.168.42.42
#tracker_sync_addr 255.255.255.255

48
tracker.h Normal file
View file

@ -0,0 +1,48 @@
#define ID_LEN 20
#define PEER_HASH_SIZE 0x100000
#define PEER_HASH_SEARCH_DELTA 0x4
#define PEER_HASH_FN(info_hash) (hash_buf(info_hash) & (PEER_HASH_SIZE - 1))
enum { EVENT_NONE, EVENT_STARTED, EVENT_COMPLETED, EVENT_STOPPED };
struct ipmask_entry_s
{
unsigned char *ipaddr;
unsigned char *netmask;
};
typedef struct ipmask_entry_s ipmask_entry;
struct peer_entry_s
{
unsigned int hash_idx;
unsigned int peer_hash_idx;
unsigned int num_hits;
unsigned int num_seeders, num_leechers, times_completed;
unsigned int is_seeder;
unsigned char info_hash[ID_LEN];
unsigned char peer_id[ID_LEN];
time_t prev_active;
time_t last_active;
unsigned char ipstr[64];
unsigned int ipnum;
unsigned char ipraw[4];
unsigned int port;
unsigned long long uploaded;
unsigned long long downloaded;
unsigned long long prev_uploaded;
unsigned long long prev_downloaded;
unsigned int lastevent;
unsigned int is_local;
unsigned int is_complete;
struct peer_entry_s *next;
struct peer_entry_s *prev;
};
typedef struct peer_entry_s peer_entry;
void tracker_init(void);
void tracker_periodic(void);
void tracker_serve_announce(int, http_fd_entry *, net_fd_entry *);
void tracker_serve_scrape(int, http_fd_entry *, net_fd_entry *);
void tracker_serve_status(int, http_fd_entry *, net_fd_entry *);
void tracker_serve_peers(int, http_fd_entry *, net_fd_entry *);