diff options
| author | Paweł Redman <pawel.redman@gmail.com> | 2017-04-05 23:09:06 +0200 | 
|---|---|---|
| committer | Paweł Redman <pawel.redman@gmail.com> | 2017-04-05 23:09:06 +0200 | 
| commit | da4626cb5a741a9b7861dd54f8570a00753a5d92 (patch) | |
| tree | 3731ac96db396ceaf83d0c867aff06810a08daa3 | |
Initial commit.
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | Makefile | 41 | ||||
| -rw-r--r-- | src/cache.c | 74 | ||||
| -rw-r--r-- | src/eli.h | 84 | ||||
| -rw-r--r-- | src/lexer.c | 254 | ||||
| -rw-r--r-- | src/lists.c | 153 | ||||
| -rw-r--r-- | src/main.c | 137 | ||||
| -rw-r--r-- | src/shared.h | 140 | ||||
| -rw-r--r-- | src/worker.c | 271 | 
9 files changed, 1157 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d8066d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.list +obj/ +schachtmeister2 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1fee50d --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +CC = gcc +CFLAGS += -g -Wall -pthread +CPPFLAGS += -MMD +LDFLAGS += -pthread + +PP_BOLD := $(shell tput bold) +PP_RESET := $(shell tput sgr0) +PP_CC := $(PP_BOLD)$(shell tput setf 6)CC$(PP_RESET) +PP_LD := $(PP_BOLD)$(shell tput setf 2)LD$(PP_RESET) +PP_RM := $(PP_BOLD)$(shell tput setf 4)RM$(PP_RESET) + +SRC := src/cache.c \ +       src/lexer.c \ +       src/lists.c \ +       src/main.c \ +       src/worker.c +        +OBJ := $(SRC:src/%.c=obj/%.o) +OUT := schachtmeister2 + +all: $(OUT) + +-include $(OBJ:.o=.d) + +obj/%.o : src/%.c +	@echo "$(PP_CC) src/$*.c" +	@mkdir -p $(@D) +	@$(CC) $(CFLAGS) $(CPPFLAGS) -c src/$*.c -o obj/$*.o + +$(OUT): $(OBJ) +	@echo "$(PP_LD) $(OUT)" +	@$(CC) $(OBJ) -o $(OUT) $(LDFLAGS) + +clean: +	@echo "${PP_RM} obj" +	@rm -rf obj +	@echo "${PP_RM} ${OUT}" +	@rm -rf ${OUT} + +.PHONY: clean + diff --git a/src/cache.c b/src/cache.c new file mode 100644 index 0000000..6a31a16 --- /dev/null +++ b/src/cache.c @@ -0,0 +1,74 @@ +/* +Copyright (C) 2017 Paweł Redman + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 3 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA +*/ + +#include "shared.h" + +#define HASH_MAX 4095 + +static size_t cache_hash(uint32_t ipv4) +{ +	// FIXME: Use an academically acclaimed hashing algorithm. +	return (((ipv4 >> 16) ^ ipv4) * 3137) & HASH_MAX; +} + +static cache_entry_t *cache_ht[HASH_MAX]; +static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER; + +// Returns an entry with its mutex locked. +cache_entry_t *cache_find(uint32_t ipv4) +{ +	cache_entry_t *entry; +	size_t hash; + +	hash = cache_hash(ipv4); + +	pthread_mutex_lock(&cache_mutex); +	eli_for (entry, cache_ht[hash], ht_list) +		if (entry->ipv4 == ipv4) +			break; + +	if (!entry) { +		entry = calloc(1, sizeof(cache_entry_t)); +		entry->ipv4 = ipv4; +		pthread_mutex_init(&entry->mutex, NULL); +		eli_append(cache_ht + hash, entry, ht_list); +	} + +	pthread_mutex_lock(&entry->mutex); +	pthread_mutex_unlock(&cache_mutex); + +	return entry; +} + +void cache_destroy(void) +{ +	size_t i; +	cache_entry_t *entry, *next; + +	for (i = 0; i < HASH_MAX; i++) { +		for (entry = cache_ht[i]; entry; entry = next) { +			next = entry->ht_list.next; + +			free(entry->revdns); +			free(entry->whois); +			free(entry); +		} + +		cache_ht[i] = NULL; +	} +} diff --git a/src/eli.h b/src/eli.h new file mode 100644 index 0000000..8cd75a6 --- /dev/null +++ b/src/eli.h @@ -0,0 +1,84 @@ +/* +Copyright (C) 2017 Paweł Redman + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 3 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA +*/ + +#include <stddef.h> // size_t + +#define eli_rebase(x, xm, y) \ +((void*)((char*)(y) + ((char*)(xm) - (char*)(x)))) + +typedef struct { +	void *prev, *next; +} eli_header; + +// Link an element to the end of a list. +static inline void eli_append_real(void **head, void *entry, +                                   eli_header *head_h, eli_header *entry_h) +{ +	eli_header *last_h; + +	// Empty list. +	if (__builtin_expect(!*head, 0)) { +		*head = entry; +		entry_h->prev = entry; +		entry_h->next = NULL; +		return; +	} + +	entry_h->prev = head_h->prev; +	entry_h->next = NULL; + +	last_h = eli_rebase(entry, entry_h, head_h->prev); +	last_h->next = entry; + +	head_h->prev = entry; +} + +#define eli_append(pphead, entry, member) \ +eli_append_real((void**)(pphead), (entry), &((*(pphead))->member), &((entry)->member)) + +// Unlink an element (from anywhere in a list). +static inline void eli_unlink_real(void **head, void *entry, +                                   eli_header *head_h, eli_header *entry_h) +{ +	if (entry_h->prev && *head != entry) { +		eli_header *prev_h; + +		prev_h = eli_rebase(entry, entry_h, head_h->prev); +		prev_h->next = entry_h->next; +	} + +	if (entry_h->next) { +		eli_header *next_h; + +		next_h = eli_rebase(entry, entry_h, head_h->next); +		next_h->prev = entry_h->prev; +	} else { +		head_h->prev = entry_h->prev; +	} + +	if (*head == entry) +		*head = entry_h->next; + +} + +#define eli_unlink(pphead, entry, member) \ +eli_unlink_real((void**)(pphead), (entry), &((*(pphead))->member), &((entry)->member)) + +// Loop over a list +#define eli_for(i, head, member) \ +for ((i) = (head); (i); (i) = (i)->member.next) diff --git a/src/lexer.c b/src/lexer.c new file mode 100644 index 0000000..5863ef6 --- /dev/null +++ b/src/lexer.c @@ -0,0 +1,254 @@ +/* +Copyright (C) 2017 Paweł Redman + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 3 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA +*/ + +#include "shared.h" +#include <ctype.h> + +void vstr_init(vstr_t *vstr) +{ +	memset(vstr, 0, sizeof(*vstr)); +} + +void vstr_destroy(vstr_t *vstr) +{ +	free(vstr->data); +} + +void vstr_clear(vstr_t *vstr) +{ +	vstr->size = 0; +} + +static int vstr_enlarge(vstr_t *vstr) +{ +	size_t new_alloc; + +	new_alloc = (vstr->alloc + 4) * 3 / 2; + +	vstr->data = realloc(vstr->data, new_alloc); +	if (!vstr->data) +		return 1; + +	vstr->alloc = new_alloc; +	return 0; +} + +int vstr_putc(vstr_t *vstr, char ch) +{ +	// note: keep at least one character free at all times for vstr_termz +	if (vstr->size + 2 > vstr->alloc) +		if (vstr_enlarge(vstr)) +			return -ENOMEM; + +	vstr->data[vstr->size] = ch; +	vstr->size++; +	return 0; +} + +int vstr_cmp(vstr_t *vstr, const char *str) +{ +	size_t len; + +	len = strlen(str); +	if (vstr->size < len) +		len = vstr->size; + +	return memcmp(vstr->data, str, len); +} + +char *vstr_strdup(vstr_t *vstr) +{ +	char *str; + +	str = malloc(vstr->size + 1); +	if (!str) +		return NULL; + +	memcpy(str, vstr->data, vstr->size); +	str[vstr->size] = 0; + +	return str; +} + +char *vstr_to_cstr(vstr_t *vstr) +{ +	vstr->data[vstr->size] = '\0'; +	return vstr->data; +} + +int lexer_open(lexer_state_t *ls, const char *path, vstr_t *token) +{ +	ls->error = 0; +	ls->path = path; + +	ls->fp = fopen(path, "r"); +	if (!ls->fp) +		return -errno; + +	ls->eof = false; + +	ls->token = token; +	ls->buf_e = ls->buf_c = ls->buf; +	ls->cc = ls->lc = ls->Cc = 0; + +	ls->in_token = false; +	ls->in_quote = false; +	ls->in_comment = false; + +	return 0; +} + +void lexer_close(lexer_state_t *ls) +{ +	vstr_destroy(ls->token); +	fclose(ls->fp); +} + +//RETURN VALUES +//	<0 on error +//	0 on success +//	note: sets ls->eof to true if there's no more data left +static int fill_buffer(lexer_state_t *ls) +{ +	size_t read; + +	read = fread(ls->buf, 1, sizeof(ls->buf), ls->fp); +	if (read < sizeof(ls->buf)) { +		if (ferror(ls->fp)) +			return -errno; + +		ls->eof = true; +	} + +	ls->buf_c = ls->buf; +	ls->buf_e = ls->buf + read; +	return 0; +} + +//RETURN VALUES +//	-ENOMEM +//	-EAGAIN when the buffer runs out +//	0 on success +//	1 when no data is left +static int read_buffer(lexer_state_t *ls) +{ +	while (ls->buf_c < ls->buf_e) { +		bool ret_token = false; + +		if (*ls->buf_c == '\n') { +			ls->lc++; +			ls->Cc = 0; +		} + +		if (ls->in_comment) { +			if (*ls->buf_c == '\n') +				ls->in_comment = false; +		} else if (isspace(*ls->buf_c) && !ls->in_quote) { +			if (ls->in_token) { +				ls->in_token = false; +				ret_token = true; +			} +		} else if (*ls->buf_c == '/' && (ls->cc && ls->last == '/')) { +			ls->in_comment = true; +			ls->in_token = false; + +			ls->token->size--; // remove the first slash +			if (ls->token->size) +				ret_token = true; +		} else if (*ls->buf_c == '\"' && +		           (ls->cc && ls->last != '\\')) { +			ls->in_quote = !ls->in_quote; + +			if (!ls->in_quote) { +				ls->in_token = false; +				ret_token = true; +			} +		} else { +			if (!ls->in_token) { +				ls->in_token = true; +			} +		} + +		if (ls->in_token) +			if (vstr_putc(ls->token, *ls->buf_c)) { +				ls->error = ENOMEM; +				return -ENOMEM; +			} + +		ls->last = *ls->buf_c; +		ls->buf_c++; +		ls->cc++; +		ls->Cc++; + +		if (ret_token) +			return 0; +	} + +	if (ls->eof) { +		if (ls->token->size > 0) +			return 0; +		return 1; +	} + +	return -EAGAIN; +} + +//RETURN VALUES +//	<0 on error +//	0 on success +//	1 when no data is left +int lexer_get_token(lexer_state_t *ls) +{ +	int ret; + +	vstr_clear(ls->token); + +	while (1) { +		ret = read_buffer(ls); +		if (ret != -EAGAIN) +			return ret; + +		ret = fill_buffer(ls); +		if (ret < 0) +			return ret; +	} +} + +void lexer_perror(lexer_state_t *ls, const char *fmt, ...) +{ +	va_list vl; + +	eprintf("%s:%zu:%zu: ", ls->path, ls->lc + 1, ls->Cc + 1); + +	if (ls->error) { +		perror(NULL); +	} else { +		va_start(vl, fmt); +		vfprintf(stderr, fmt, vl); +		va_end(vl); +	} +} + +void lexer_perror_eg(lexer_state_t *ls, const char *expected) +{ +	if (ls->eof && ls->buf_c == ls->buf_e) +		lexer_perror(ls, "expected %s, got EOF\n", expected); +	else +		lexer_perror(ls, "expected %s, got \"%s\"\n", expected, +		             vstr_to_cstr(ls->token)); +} diff --git a/src/lists.c b/src/lists.c new file mode 100644 index 0000000..00e29ba --- /dev/null +++ b/src/lists.c @@ -0,0 +1,153 @@ +/* +Copyright (C) 2017 Paweł Redman + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 3 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA +*/ + +#include "shared.h" + +typedef enum { +	ENTRY_REVDNS, +	ENTRY_WHOIS +} entry_type_t; + +typedef struct { +	entry_type_t type; + +	char *pattern; +	int niceness; + +	eli_header list; +} entry_t; + +static entry_t *entry_list; +static size_t total_entries; + +static int parse_entry(lexer_state_t *lexer, vstr_t *token, entry_type_t type) +{ +	entry_t *entry; +	int niceness; +	char *pattern; + +	if (lexer_get_token(lexer)) { +		lexer_perror_eg(lexer, "the niceness adjustment"); +		return 1; +	} + +	niceness = atoi(vstr_to_cstr(token)); + +	if (lexer_get_token(lexer)) { +		lexer_perror_eg(lexer, "the pattern"); +		return 1; +	} + +	pattern = vstr_strdup(token); +	if (!pattern) { +		lexer_perror(lexer, "out of memory\n"); +		return 1; +	} + +	entry = malloc(sizeof(entry_t)); +	if (!entry) { +		free(pattern); +		lexer_perror(lexer, "out of memory\n"); +		return 1; +	} + +	entry->type = type; +	entry->niceness = niceness; +	entry->pattern = pattern; +	eli_append(&entry_list, entry, list); +	total_entries++; + +	return 0; +} + +// FIXME: Make this MT-safe (lock the list before adding shit to it) +int lists_load(const char *file, size_t depth) +{ +	int error = 1, rv; +	lexer_state_t lexer; +	vstr_t token = VSTR_INITIALIZER; + +	if (depth > 20) { +		eprintf("%s: error: deep recursion (probably a circular dependency)\n", file); +		return 1; +	} + +	if ((rv = lexer_open(&lexer, file, &token))) { +		perror(file); +		return 1; +	} + +	while (!lexer_get_token(&lexer)) { +		if (!vstr_cmp(&token, "include")) { +			if (lexer_get_token(&lexer)) { +				lexer_perror_eg(&lexer, "the filename"); +				goto out; +			} + +			// FIXME: relative paths +			if (lists_load(vstr_to_cstr(&token), depth + 1)) { +				lexer_perror(&lexer, "included from here\n"); +				goto out; +			} +		} else if (!vstr_cmp(&token, "revdns")) { +			if (parse_entry(&lexer, &token, ENTRY_REVDNS)) +				goto out; +		} else if (!vstr_cmp(&token, "whois")) { +			if (parse_entry(&lexer, &token, ENTRY_WHOIS)) +				goto out; +		} else { +			lexer_perror_eg(&lexer, "'whois' or 'revdns'"); +			goto out; +		} +	} + +	if (depth == 0) +		DEBUG("loaded entries: %zu\n", total_entries); + +	error = 0; +out: +	lexer_close(&lexer); +	return error; +} + +void lists_destroy(void) +{ +	// TODO... +} + +int lists_test(const char *revdns, const char *whois) +{ +	entry_t *entry; +	int total = 0; + +	eli_for(entry, entry_list, list) { +		// TODO: regexps +		if (entry->type == ENTRY_REVDNS && +		    !strstr(revdns, entry->pattern)) +			continue; + +		if (entry->type == ENTRY_WHOIS && +		    !strstr(whois, entry->pattern)) +			continue; + +		// TODO: avoid overflows +		total += entry->niceness; +	} + +	return total; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..69653c7 --- /dev/null +++ b/src/main.c @@ -0,0 +1,137 @@ +/* +Copyright (C) 2017 Paweł Redman + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 3 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA +*/ + +#include "shared.h" + +int server_sockfd; + +static int parse_req_addr(char *begin, char *end, uint32_t *out) +{ +	char *p; +	size_t i = 0, parts[4] = {0, 0, 0, 0}; + +	for (p = begin; p < end; p++) { +		if (*p >= '0' && *p <= '9') { +			parts[i] *= 10; +			parts[i] += *p - '0'; + +			if (parts[i] > 255) +				return 1; +		} else if (*p == '.') { +			i++; + +			if (i > 3) +				return 1; +		} else +			break; +	} + +	if (i != 3) +		return 1; + +	*out = (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]; +	return 0; +} + +#define NUM_WORKERS 8 // FIXME: shouldn't be hardcoded + +int main(void) +{ +	int error = 0; // return value +	struct sockaddr_in sockaddr; +	pthread_t workers[NUM_WORKERS]; +	size_t i; + +	if (lists_load("schachts.list", 0)) { +		eprintf("fatal error: couldn't load the lists\n"); +		goto error_lists; +	} + +	server_sockfd = socket(AF_INET, SOCK_DGRAM, 0); +	if (server_sockfd == -1) { +		perror("socket"); +		error = 1; +		goto error_socket; +	} + +	sockaddr.sin_family = AF_INET; +	sockaddr.sin_addr.s_addr = INADDR_ANY; +	sockaddr.sin_port = htons(1337); + +	if (bind(server_sockfd, (void*)&sockaddr, sizeof(sockaddr)) == -1) { +		perror("bind"); +		error = 1; +		goto error_bind; +	} + +	for (i = 0; i < NUM_WORKERS; i++) +		pthread_create(workers + i, NULL, worker_main, NULL); + +	while (1) { +		char buffer[256]; +		struct sockaddr_in addr; +		socklen_t addrlen = sizeof(addr); +		ssize_t size; +		uint32_t query; +		job_t *job; + +		size = recvfrom(server_sockfd, buffer, sizeof(buffer), 0, +		                (void*)&addr, &addrlen); +		if (size == -1) { +			perror("recvfrom"); +			goto error_recvfrom; +		} + +		if ((ntohl(addr.sin_addr.s_addr) & LOCALHOST_MASK) +		    != LOCALHOST_NETWORK) +			continue; + +		if (size < REQUEST_HEADER_LEN) +			continue; + +		if (memcmp(buffer, REQUEST_HEADER, REQUEST_HEADER_LEN)) +			continue; + +		if (parse_req_addr(buffer + REQUEST_HEADER_LEN, buffer + size, +		                   &query)) +			continue; + +		job = job_create(); +		job->type = JOB_TEST; +		job->query = query; +		memcpy(&job->reply_to, &addr, sizeof(addr)); +		job_enqueue(job); +	} + +	job_quit(); + +	for (i = 0; i < NUM_WORKERS; i++) +		pthread_join(workers[i], NULL); + +	cache_destroy(); +	// todo: clear all jobs + +error_recvfrom: +error_bind: +	shutdown(server_sockfd, SHUT_RDWR); +	close(server_sockfd); +error_socket: +	lists_destroy(); +error_lists: +	return error; +} diff --git a/src/shared.h b/src/shared.h new file mode 100644 index 0000000..a615f83 --- /dev/null +++ b/src/shared.h @@ -0,0 +1,140 @@ +/* +Copyright (C) 2017 Paweł Redman + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 3 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA +*/ + +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> + +#include <netinet/in.h> +#include <netinet/udp.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <pthread.h> + +#include "eli.h" + +#define eprintf(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) +#define DEBUG(fmt, ...) fprintf(stderr, "%s: " fmt, __func__, ##__VA_ARGS__) + +#define LOCALHOST_NETWORK 0x7F000000 +#define LOCALHOST_MASK    0xFF000000 + +#define REQUEST_HEADER "query " +#define REQUEST_HEADER_LEN (sizeof(REQUEST_HEADER) - 1) +#define RESPONSE_HEADER "reply " + +// main.c + +extern int server_sockfd; + +// worker.c + +typedef enum { +	JOB_TEST, +	JOB_REVDNS, +	JOB_WHOIS +} job_type_t; + +typedef struct job_s job_t; +struct job_s { +	job_type_t type; + +	uint32_t query; +	struct sockaddr_in reply_to; + +	eli_header list, dep_list; +	job_t *parent, *deps; +	size_t num_deps, deps_done; +	pthread_mutex_t mutex; +}; + +job_t *job_create(void); +void job_enqueue(job_t *job); +void job_quit(void); +void *worker_main(void *unused); + +// cache.c + +typedef struct { +	uint32_t ipv4; + +	char *revdns; +	uint64_t revdns_exptime; // FIXME: implement this already +	char *whois; +	uint64_t whois_exptime; + +	eli_header ht_list; +	pthread_mutex_t mutex; +} cache_entry_t; + +cache_entry_t *cache_find(uint32_t ipv4); +void cache_destroy(void); + +// lexer.c + +typedef struct { +	char *data; +	size_t size, alloc; +} vstr_t; + +#define VSTR_INITIALIZER ((vstr_t){NULL, 0, 0}) + +void vstr_init(vstr_t *vstr); +void vstr_destroy(vstr_t *vstr); +void vstr_clear(vstr_t *vstr); +int vstr_putc(vstr_t *vstr, char ch); +int vstr_cmp(vstr_t *vstr, const char *str); +char *vstr_strdup(vstr_t *vstr); +char *vstr_to_cstr(vstr_t *vstr); + +#define LEXER_BUFFER 1024 + +typedef struct { +	int error; +	const char *path; +	FILE *fp; +	bool eof; + +	vstr_t *token; +	char buf[LEXER_BUFFER]; +	char *buf_c, *buf_e; + +	size_t cc, lc, Cc; // character, line, and column counters +	char last; + +	bool in_token; +	bool in_quote; +	bool in_comment; +} lexer_state_t; + +int lexer_open(lexer_state_t *ls, const char *path, vstr_t *token); +void lexer_close(lexer_state_t *ls); +int lexer_get_token(lexer_state_t *ls); +void lexer_perror(lexer_state_t *ls, const char *fmt, ...); +void lexer_perror_eg(lexer_state_t *ls, const char *expected); + +// lists.c + +int lists_load(const char *file, size_t depth); +void lists_destroy(void); +int lists_test(const char *revdns, const char *whois); diff --git a/src/worker.c b/src/worker.c new file mode 100644 index 0000000..59a19cb --- /dev/null +++ b/src/worker.c @@ -0,0 +1,271 @@ +/* +Copyright (C) 2017 Paweł Redman + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 3 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA +*/ + +#include "shared.h" +#include <netdb.h> // reverse DNS + +static job_t *job_queue; +static pthread_cond_t job_queue_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER; +static int job_quit_flag = 0; + +job_t *job_create(void) +{ +	job_t *job; + +	job = calloc(1, sizeof(job_t)); +	if (!job) { +		fprintf(stderr, "job_create: calloc failed\n"); +		abort(); +	} + +	pthread_mutex_init(&job->mutex, NULL); +	return job; +} + +void job_enqueue(job_t *job) +{ +	pthread_mutex_lock(&job_queue_mutex); +	eli_append(&job_queue, job, list); +	pthread_mutex_unlock(&job_queue_mutex); +	pthread_cond_signal(&job_queue_cond); +} + +static void job_add_dep(job_t *parent, job_t *dep) +{ +	eli_append(&parent->deps, dep, dep_list); +	dep->parent = parent; +	parent->num_deps++; +} + +void job_quit(void) +{ +	pthread_mutex_lock(&job_queue_mutex); +	job_quit_flag = 1; +	pthread_mutex_unlock(&job_queue_mutex); +	pthread_cond_broadcast(&job_queue_cond); +} + +int _host = 0, _whois = 0; + +int read_everything(FILE *fp, char **buffer) +{ +	size_t read = 0, alloc = 128, partial; + +	alloc = 128; +	*buffer = malloc(alloc); +	if (!*buffer) +		goto oom; + +	while (1) { +		size_t left = alloc - read; + +		if (left == 0) { +			alloc *= 2; +			*buffer = realloc(*buffer, alloc); +			if (!buffer) +				goto oom; + +			continue; +		} + +		partial = fread((*buffer) + read, 1, left, fp); +		if (partial == 0) { +			if (ferror(fp)) +				return 1; + +			break; +		} + +		read += partial; +	} + + +	*buffer = realloc(*buffer, read + 1); +	(*buffer)[read] = '\0'; +	return 0; + +oom: +	fprintf(stderr, "read_everything: out of memory\n"); +	abort(); +} + +int schachtest(const char *revdns, const char *whois) +{ +	return 0; +} + +static void worker_job_test(job_t *job) +{ +	cache_entry_t *entry; +	job_t *dep; +	int result; +	char reply[1000]; + +	entry = cache_find(job->query); + +	if (!entry->revdns) { +		DEBUG("cache miss on revdns\n"); +		dep = job_create(); +		dep->type = JOB_REVDNS; +		dep->query = job->query; +		job_add_dep(job, dep); +	} + +	if (!entry->whois) { +		DEBUG("cache miss on whois\n"); +		dep = job_create(); +		dep->type = JOB_WHOIS; +		dep->query = job->query; +		job_add_dep(job, dep); +	} + +	if (!entry->revdns || !entry->whois) { +		pthread_mutex_unlock(&entry->mutex); +		return; +	} + +	DEBUG("ready for schachtest\n"); + +	result = lists_test(entry->revdns, entry->whois); +	pthread_mutex_unlock(&entry->mutex); + +	snprintf(reply, sizeof(reply), "%s%hhu.%hhu.%hhu.%hhu %i\n", +	         RESPONSE_HEADER, +	         (job->query >> 24) & 0xFF, (job->query >> 16) & 0xFF, +	         (job->query >> 8) & 0xFF, job->query & 0xFF, result); +	// FIXME: handle sendto errors +	sendto(server_sockfd, reply, strlen(reply), 0, (void*)&job->reply_to, +	       sizeof(job->reply_to)); +} + + +static void worker_job_revdns(job_t *job) +{ +	struct sockaddr_in addr; +	char buffer[NI_MAXHOST], *revdns; +	int rv; +	cache_entry_t *entry; + +	addr.sin_family = AF_INET; +	addr.sin_addr.s_addr = htonl(job->query); +	addr.sin_port = 0; + +	rv = getnameinfo((void*)&addr, sizeof(addr), buffer, sizeof(buffer), +	                 NULL, 0, NI_NAMEREQD); +	switch (rv) { +	case 0: +		revdns = strdup(buffer); +		break; + +	case EAI_NONAME: +		// FIXME: handle errors and missing revDNS entries better +		revdns = strdup("<none>"); +		break; + +	case EAI_SYSTEM: +		revdns = strdup("<error>"); +		perror("getnameinfo"); +		break; + +	default: +		revdns = strdup("<error>"); +		fprintf(stderr, "getnameinfo: %s\n", gai_strerror(rv)); +		break; +	} + +	entry = cache_find(job->query); +	free(entry->revdns); +	entry->revdns = revdns; +	DEBUG("revdns is done\n"); +	pthread_mutex_unlock(&entry->mutex); +} + +static void worker_job_whois(job_t *job) +{ +	char command[80]; +	FILE *fp; +	char *out; +	cache_entry_t *entry; + +	snprintf(command, sizeof(command), "whois \"%hhu.%hhu.%hhu.%hhu\"", +	         (job->query >> 24) & 0xFF, (job->query >> 16) & 0xFF, +	         (job->query >> 8) & 0xFF, job->query & 0xFF); + +	fp = popen(command, "r"); +	read_everything(fp, &out); +	fclose(fp); + +	entry = cache_find(job->query); +	free(entry->whois); +	entry->whois = out; +	DEBUG("whois is done\n"); +	pthread_mutex_unlock(&entry->mutex); +} + +void *worker_main(void *unused) +{ +	for (;;) { +		job_t *job; + +		pthread_mutex_lock(&job_queue_mutex); +		while (!job_queue && !job_quit_flag) +			pthread_cond_wait(&job_queue_cond, &job_queue_mutex); + +		if (job_quit_flag) +			return NULL; + +		job = job_queue; +		eli_unlink(&job_queue, job, list); +		pthread_mutex_unlock(&job_queue_mutex); + +		switch (job->type) { +		case JOB_TEST: +			worker_job_test(job); +			break; + +		case JOB_REVDNS: +			worker_job_revdns(job); +			break; + +		case JOB_WHOIS: +			worker_job_whois(job); +			break; +		} + +		// Handle the dependencies first. Keep this job off the queue. +		if (job->deps) { +			job_t *dep; + +			eli_for(dep, job->deps, dep_list) +				job_enqueue(dep); +			memset(&job->deps, 0, sizeof(job->deps)); +			continue; +		} + +		if (job->parent) { +			pthread_mutex_lock(&job->parent->mutex); +			job->parent->deps_done++; +			if (job->parent->deps_done == job->parent->num_deps) +				job_enqueue(job->parent); +			pthread_mutex_unlock(&job->parent->mutex); +		} + +		free(job); +	} +}  | 
