new I/O multiplexer api; http echo server example draft to test the mux api; the library is now released as an amalgamation; recv/send now return the error MICROTCP_ERRCODE_WOULDBLOCK in a similar way to the BSD socket api; moved tests/loop2.c to examples/echo_tcp.c
This commit is contained in:
@@ -4,3 +4,4 @@ loop2.exe
|
||||
3p/lib/*
|
||||
3p/src/*
|
||||
3p/include/*
|
||||
build
|
||||
@@ -45,3 +45,6 @@ int main(void)
|
||||
// If you want to use this code, you probably want to
|
||||
// add some checks!
|
||||
```
|
||||
|
||||
## Contributing
|
||||
The build result is a header and a .c obtained as an amalgamation of all the source files. Any header included in the source files must be guarded by a `#ifndef MICROTCP_AMALGAMATION`.
|
||||
@@ -1,59 +0,0 @@
|
||||
#include <microtcp.h>
|
||||
|
||||
typedef struct microhttp_server_t http_server_t;
|
||||
http_server_t *microhttp_server_create(microtcp_t *mtcp, uint16_t port);
|
||||
void microhttp_server_destroy(http_server_t *server);
|
||||
void microhttp_server_serve(microhttp_server_t *server, void *data, void (*callback)(void*, microhttp_request_t*));
|
||||
|
||||
struct microhttp_server_t {
|
||||
microtcp_t *tcp;
|
||||
microtcp_listener_t *listener;
|
||||
};
|
||||
|
||||
microhttp_server_t *microhttp_server_create(microtcp_t *mtcp, uint16_t port)
|
||||
{
|
||||
microhttp_server_t *server = malloc(sizeof(microhttp_server_t));
|
||||
if (!server)
|
||||
return NULL;
|
||||
|
||||
microtcp_listener_t *listener = microtcp_listener_create(mtcp, port);
|
||||
if (!listener) {
|
||||
free(server);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
server->mtcp = mtcp;
|
||||
server->listener = listener;
|
||||
return server;
|
||||
}
|
||||
|
||||
void microhttp_server_serve(microhttp_server_t *server, void *data, void (*callback)(void*, microhttp_request_t*))
|
||||
{
|
||||
char buffer[65536];
|
||||
|
||||
while (1) {
|
||||
|
||||
microtcp_socket_t *socket = microtcp_listener_accept(server->listener);
|
||||
if (!socket)
|
||||
continue;
|
||||
|
||||
int num = microtcp_socket_recv(socket, buffer, sizeof(buffer));
|
||||
if (num >= 0) {
|
||||
hp_error_t error;
|
||||
hp_request_t request;
|
||||
if (!hp_parse(buffer, num, &request, &error)) {
|
||||
..
|
||||
} else {
|
||||
..
|
||||
}
|
||||
}
|
||||
|
||||
microtcp_socket_destroy(socket);
|
||||
}
|
||||
}
|
||||
|
||||
void microhttp_server_destroy(microhttp_server_t *server)
|
||||
{
|
||||
microtcp_listener_destroy(server->listener);
|
||||
free(server);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
#include "net.h"
|
||||
|
||||
static size_t send_callback()
|
||||
{
|
||||
}
|
||||
|
||||
int tun_fd;
|
||||
|
||||
static size_t recv_callback(void *context, void *dst, size_t len)
|
||||
{
|
||||
return read(tun_fd, dst, len);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
net_t net;
|
||||
net_init(&net, ip, mac, NULL, send_callback, recv_callback);
|
||||
net_spawn_thread(&net);
|
||||
|
||||
uint16_t port = 8080;
|
||||
|
||||
net_listener_t *listener = net_listener_create(&net, port);
|
||||
if (listener == NULL) {
|
||||
fprintf(stderr, "Failed to start listening\n");
|
||||
net_free(&net);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
|
||||
net_socket_t *client = net_listener_accept(listener);
|
||||
|
||||
if (client == NULL)
|
||||
continue;
|
||||
|
||||
char message[1024];
|
||||
size_t messlen;
|
||||
|
||||
messlen = net_socket_recv(client, message, sizeof(message));
|
||||
net_socket_send(client, "echo: ", sizeof("echo: "));
|
||||
net_socket_send(client, message, messlen);
|
||||
|
||||
net_socket_destroy(client);
|
||||
}
|
||||
|
||||
net_listener_destroy(listener);
|
||||
net_free(&net);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,883 @@
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "http_parser.h"
|
||||
|
||||
typedef struct {
|
||||
size_t offset;
|
||||
size_t length;
|
||||
} slice_t;
|
||||
|
||||
typedef struct {
|
||||
const unsigned char *src;
|
||||
size_t len, cur;
|
||||
} scanner_t;
|
||||
|
||||
#define EMPTY_SLICE ((slice_t) {0, 0})
|
||||
#define EMPTY_STRING ((hp_string_t) {NULL, 0})
|
||||
|
||||
static bool is_lower_alpha(unsigned char c)
|
||||
{
|
||||
return c >= 'a' && c <= 'z';
|
||||
}
|
||||
|
||||
static bool is_upper_alpha(unsigned char c)
|
||||
{
|
||||
return c >= 'A' && c <= 'Z';
|
||||
}
|
||||
|
||||
static bool is_alpha(unsigned char c)
|
||||
{
|
||||
return is_upper_alpha(c)
|
||||
|| is_lower_alpha(c);
|
||||
}
|
||||
|
||||
static bool is_digit(unsigned char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
static bool is_hex_digit(unsigned char c)
|
||||
{
|
||||
return is_digit(c)
|
||||
|| (c >= 'a' && c <= 'f')
|
||||
|| (c >= 'A' && c <= 'F');
|
||||
}
|
||||
|
||||
static bool is_unreserved(unsigned char c)
|
||||
{
|
||||
return is_alpha(c) || is_digit(c)
|
||||
|| c == '-' || c == '.'
|
||||
|| c == '_' || c == '~';
|
||||
}
|
||||
|
||||
static bool is_subdelim(unsigned char c)
|
||||
{
|
||||
return c == '!' || c == '$'
|
||||
|| c == '&' || c == '\''
|
||||
|| c == '(' || c == ')'
|
||||
|| c == '*' || c == '+'
|
||||
|| c == ',' || c == ';'
|
||||
|| c == '=';
|
||||
}
|
||||
|
||||
static bool is_pchar(unsigned char c)
|
||||
{
|
||||
return is_unreserved(c)
|
||||
|| is_subdelim(c)
|
||||
|| c == ':' || c == '@';
|
||||
}
|
||||
|
||||
static void report(hp_error_t *err, const char *fmt, ...)
|
||||
{
|
||||
if (err != NULL && !err->occurred) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(err->msg, sizeof(err->msg), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
err->occurred = true;
|
||||
}
|
||||
}
|
||||
|
||||
static slice_t slice_up(scanner_t *scanner,
|
||||
bool (*is_head)(unsigned char),
|
||||
bool (*is_body)(unsigned char))
|
||||
{
|
||||
size_t offset = scanner->cur;
|
||||
if (scanner->cur < scanner->len && is_head(scanner->src[scanner->cur]))
|
||||
do
|
||||
scanner->cur++;
|
||||
while (scanner->cur < scanner->len && is_body(scanner->src[scanner->cur]));
|
||||
return (slice_t) {offset, scanner->cur-offset};
|
||||
}
|
||||
|
||||
static bool follows_char(scanner_t scanner, unsigned char c)
|
||||
{
|
||||
return scanner.cur < scanner.len && scanner.src[scanner.cur] == c;
|
||||
}
|
||||
|
||||
static bool follows_pchar(scanner_t scanner)
|
||||
{
|
||||
return scanner.cur < scanner.len && is_pchar(scanner.src[scanner.cur]);
|
||||
}
|
||||
|
||||
static bool follows_pair(scanner_t scanner, char pair[2])
|
||||
{
|
||||
return scanner.cur+1 < scanner.len
|
||||
&& scanner.src[scanner.cur+0] == (unsigned char) pair[0]
|
||||
&& scanner.src[scanner.cur+1] == (unsigned char) pair[1];
|
||||
}
|
||||
|
||||
static bool follows_digit(scanner_t scanner)
|
||||
{
|
||||
return scanner.cur < scanner.len
|
||||
&& is_digit(scanner.src[scanner.cur]);
|
||||
}
|
||||
|
||||
static bool follows_hex_digit(scanner_t scanner)
|
||||
{
|
||||
return scanner.cur < scanner.len
|
||||
&& is_hex_digit(scanner.src[scanner.cur]);
|
||||
}
|
||||
|
||||
static bool consume_char(scanner_t *scanner, unsigned char c)
|
||||
{
|
||||
if (follows_char(*scanner, c)) {
|
||||
scanner->cur++;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void unconsume_char(scanner_t *scanner)
|
||||
{
|
||||
assert(scanner->cur > 0);
|
||||
scanner->cur--;
|
||||
}
|
||||
|
||||
static bool consume_pchar(scanner_t *scanner)
|
||||
{
|
||||
if (follows_pchar(*scanner)) {
|
||||
scanner->cur++;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool consume_pair(scanner_t *scanner, char pair[static 2])
|
||||
{
|
||||
if (follows_pair(*scanner, pair)) {
|
||||
scanner->cur += 2;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool consume_digit(scanner_t *scanner, unsigned char *digit)
|
||||
{
|
||||
if (follows_digit(*scanner)) {
|
||||
*digit = scanner->src[scanner->cur];
|
||||
scanner->cur++;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool consume_hex_digit(scanner_t *scanner, unsigned char *digit)
|
||||
{
|
||||
if (follows_hex_digit(*scanner)) {
|
||||
*digit = scanner->src[scanner->cur];
|
||||
scanner->cur++;
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void consume_spaces(scanner_t *scanner)
|
||||
{
|
||||
while (consume_char(scanner, ' '));
|
||||
}
|
||||
|
||||
static bool consume_u64_base_10(scanner_t *scanner, uint64_t max, uint64_t *out)
|
||||
{
|
||||
if (!follows_digit(*scanner))
|
||||
return false;
|
||||
|
||||
uint64_t num = 0;
|
||||
unsigned char digit;
|
||||
while (consume_digit(scanner, &digit)) {
|
||||
int u = digit - '0';
|
||||
if (num > (max - u) / 10) {
|
||||
unconsume_char(scanner);
|
||||
break;
|
||||
}
|
||||
num = num * 10 + u;
|
||||
}
|
||||
|
||||
*out = num;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int hex_digit_to_int(char c)
|
||||
{
|
||||
assert(is_hex_digit(c));
|
||||
|
||||
if (is_lower_alpha(c))
|
||||
return c - 'a' + 10;
|
||||
|
||||
if (is_upper_alpha(c))
|
||||
return c - 'A' + 10;
|
||||
|
||||
assert(is_digit(c));
|
||||
return c - '0';
|
||||
}
|
||||
|
||||
static bool consume_u64_base_16(scanner_t *scanner, uint64_t max, uint64_t *out)
|
||||
{
|
||||
if (!follows_hex_digit(*scanner))
|
||||
return false;
|
||||
|
||||
uint64_t num = 0;
|
||||
unsigned char digit;
|
||||
while (consume_hex_digit(scanner, &digit)) {
|
||||
int u = hex_digit_to_int(digit);
|
||||
if (num > (max - u) / 16) {
|
||||
unconsume_char(scanner);
|
||||
break;
|
||||
}
|
||||
num = num * 16 + u;
|
||||
}
|
||||
|
||||
*out = num;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool consume_u8_base_10(scanner_t *scanner, uint8_t *out)
|
||||
{
|
||||
uint64_t buffer;
|
||||
bool ok = consume_u64_base_10(scanner, UINT8_MAX, &buffer);
|
||||
assert(!ok || buffer <= UINT8_MAX);
|
||||
*out = buffer;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool consume_u16_base_16(scanner_t *scanner, uint16_t *out)
|
||||
{
|
||||
uint64_t buffer;
|
||||
bool ok = consume_u64_base_16(scanner, UINT16_MAX, &buffer);
|
||||
assert(!ok || buffer <= UINT16_MAX);
|
||||
*out = buffer;
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool consume_u16_base_10(scanner_t *scanner, uint16_t *out)
|
||||
{
|
||||
uint64_t buffer;
|
||||
bool ok = consume_u64_base_10(scanner, UINT16_MAX, &buffer);
|
||||
assert(!ok || buffer <= UINT16_MAX);
|
||||
*out = buffer;
|
||||
return ok;
|
||||
}
|
||||
|
||||
// [<schema> : ] // [ <username> [ : <password> ] @ ] { <name> | <IPv4> | "[" <IPv5> "]" } [ : <port> ] [ </path> ] [ ? <query> ] [ # <fragment> ]
|
||||
|
||||
static bool is_schema_first(unsigned char c)
|
||||
{
|
||||
return is_alpha(c);
|
||||
}
|
||||
|
||||
static bool is_schema(unsigned char c)
|
||||
{
|
||||
return is_alpha(c)
|
||||
|| is_digit(c)
|
||||
|| c == '+'
|
||||
|| c == '-'
|
||||
|| c == '.';
|
||||
}
|
||||
|
||||
static slice_t parse_schema(scanner_t *scanner)
|
||||
{
|
||||
size_t start = scanner->cur;
|
||||
|
||||
slice_t schema = slice_up(scanner, is_schema_first, is_schema);
|
||||
if (schema.length > 0)
|
||||
if (!consume_char(scanner, ':')) {
|
||||
scanner->cur = start;
|
||||
return EMPTY_SLICE;
|
||||
}
|
||||
return schema;
|
||||
}
|
||||
|
||||
static bool is_username(unsigned char c)
|
||||
{
|
||||
return is_unreserved(c) || is_subdelim(c);
|
||||
}
|
||||
|
||||
static bool is_username_first(unsigned char c)
|
||||
{
|
||||
return is_username(c);
|
||||
}
|
||||
|
||||
static bool is_password(unsigned char c)
|
||||
{
|
||||
return is_username(c);
|
||||
}
|
||||
|
||||
static bool is_password_first(unsigned char c)
|
||||
{
|
||||
return is_password(c);
|
||||
}
|
||||
|
||||
static hp_string_t string_from_slice(const unsigned char *src, slice_t slice)
|
||||
{
|
||||
assert(src != NULL);
|
||||
|
||||
if (slice.length == 0)
|
||||
return EMPTY_STRING;
|
||||
else
|
||||
return (hp_string_t) {(char*) src + slice.offset, slice.length};
|
||||
}
|
||||
|
||||
static void parse_userinfo(scanner_t *scanner, slice_t *username, slice_t *password)
|
||||
{
|
||||
size_t start = scanner->cur;
|
||||
|
||||
*password = EMPTY_SLICE;
|
||||
|
||||
*username = slice_up(scanner, is_username_first, is_username);
|
||||
if (username->length > 0) {
|
||||
|
||||
if (consume_char(scanner, ':'))
|
||||
*password = slice_up(scanner, is_password_first, is_password);
|
||||
|
||||
if (!consume_char(scanner, '@')) {
|
||||
*username = EMPTY_SLICE;
|
||||
*password = EMPTY_SLICE;
|
||||
scanner->cur = start; // Rollback changes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_ipv4(scanner_t *scanner, uint32_t *out, hp_error_t *err)
|
||||
{
|
||||
uint8_t byte;
|
||||
uint32_t ipv4 = 0;
|
||||
|
||||
for (int u = 0; u < 3; u++) {
|
||||
|
||||
if (!consume_u8_base_10(scanner, &byte)) {
|
||||
if (u == 0)
|
||||
report(err, "Missing IPv4");
|
||||
else
|
||||
report(err, "Missing IPv4 byte");
|
||||
return false;
|
||||
}
|
||||
ipv4 = (ipv4 << 8) + byte;
|
||||
|
||||
if (!consume_char(scanner, '.'))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!consume_u8_base_10(scanner, &byte)) {
|
||||
report(err, "Missing IPv4 byte");
|
||||
return false;
|
||||
}
|
||||
ipv4 = (ipv4 << 8) + byte;
|
||||
|
||||
*out = ipv4;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_ipv6(scanner_t *scanner, uint16_t ipv6[static 8], hp_error_t *err)
|
||||
{
|
||||
uint16_t tail[8];
|
||||
size_t head_count = 0;
|
||||
size_t tail_count = 0;
|
||||
|
||||
if (!consume_pair(scanner, "::")) {
|
||||
|
||||
do {
|
||||
uint16_t word;
|
||||
if (!consume_u16_base_16(scanner, &word)) {
|
||||
if (scanner->cur == scanner->len) {
|
||||
if (head_count == 0)
|
||||
report(err, "Missing IPv6");
|
||||
else
|
||||
report(err, "Missing IPv6 hex value");
|
||||
} else
|
||||
report(err, "Invalid IPv6");
|
||||
return false;
|
||||
}
|
||||
|
||||
ipv6[head_count++] = word;
|
||||
|
||||
if (head_count == 8)
|
||||
break;
|
||||
|
||||
if (!consume_char(scanner, ':')) {
|
||||
report(err, "Missing ':' after IPv6 hex value");
|
||||
return false;
|
||||
}
|
||||
|
||||
} while (!consume_char(scanner, ':'));
|
||||
}
|
||||
|
||||
if (head_count + tail_count < 8) {
|
||||
while (follows_hex_digit(*scanner)) {
|
||||
|
||||
// We know the current character is a
|
||||
// hex digit, therefore [parse_ipv6_word]
|
||||
// won't fail.
|
||||
uint16_t word;
|
||||
(void) consume_u16_base_16(scanner, &word);
|
||||
|
||||
tail[tail_count++] = word;
|
||||
|
||||
if (head_count + tail_count == 8)
|
||||
break;
|
||||
|
||||
if (!consume_char(scanner, ':'))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(head_count + tail_count <= 8);
|
||||
|
||||
for (size_t p = 0; p < 8 - head_count - tail_count; p++)
|
||||
ipv6[head_count + p] = 0;
|
||||
|
||||
for (size_t p = 0; p < tail_count; p++)
|
||||
ipv6[8 - tail_count + p] = tail[p];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_hostname(unsigned char c)
|
||||
{
|
||||
return is_unreserved(c) || is_subdelim(c);
|
||||
}
|
||||
|
||||
static bool is_hostname_first(unsigned char c)
|
||||
{
|
||||
return is_hostname(c);
|
||||
}
|
||||
|
||||
static bool parse_host(scanner_t *scanner, hp_host_t *host, hp_error_t *err)
|
||||
{
|
||||
if (consume_char(scanner, '[')) {
|
||||
if (!parse_ipv6(scanner, host->ipv6, err))
|
||||
return false;
|
||||
if (!consume_char(scanner, ']')) {
|
||||
report(err, "Missing ']' after IPv6");
|
||||
return false;
|
||||
}
|
||||
host->mode = HP_HOSTMODE_IPV6;
|
||||
} else {
|
||||
|
||||
uint32_t ipv4;
|
||||
bool is_ipv4;
|
||||
|
||||
if (follows_digit(*scanner)) {
|
||||
size_t start = scanner->cur;
|
||||
is_ipv4 = parse_ipv4(scanner, &ipv4, NULL);
|
||||
if (!is_ipv4)
|
||||
scanner->cur = start;
|
||||
} else
|
||||
is_ipv4 = false;
|
||||
|
||||
if (is_ipv4) {
|
||||
host->ipv4 = ipv4;
|
||||
host->mode = HP_HOSTMODE_IPV4;
|
||||
} else {
|
||||
|
||||
slice_t hostname = slice_up(scanner, is_hostname_first, is_hostname);
|
||||
if (hostname.length == 0) {
|
||||
report(err, "Missing host");
|
||||
return false;
|
||||
}
|
||||
|
||||
host->mode = HP_HOSTMODE_NAME;
|
||||
host->name = string_from_slice(scanner->src, hostname);
|
||||
}
|
||||
}
|
||||
|
||||
host->no_port = !consume_u16_base_10(scanner, &host->port);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_path(scanner_t *scanner, slice_t *out, hp_error_t *err)
|
||||
{
|
||||
out->offset = scanner->cur;
|
||||
|
||||
if (!consume_char(scanner, '/'))
|
||||
if (!follows_pchar(*scanner)) {
|
||||
report(err, "Missing path");
|
||||
return false;
|
||||
}
|
||||
|
||||
while (consume_pchar(scanner)) {
|
||||
while (consume_pchar(scanner));
|
||||
if (!consume_char(scanner, '/'))
|
||||
break;
|
||||
}
|
||||
|
||||
out->length = scanner->cur - out->offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_query(unsigned char c)
|
||||
{
|
||||
return is_pchar(c) || c == '/' || c == '?';
|
||||
}
|
||||
|
||||
static bool is_fragment(unsigned char c)
|
||||
{
|
||||
return is_pchar(c) || c == '/';
|
||||
}
|
||||
|
||||
static bool parse_url(scanner_t *scanner, hp_url_t *url, hp_error_t *err)
|
||||
{
|
||||
url->schema = string_from_slice(scanner->src, parse_schema(scanner));
|
||||
|
||||
if (consume_pair(scanner, "//")) {
|
||||
|
||||
slice_t username, password;
|
||||
parse_userinfo(scanner, &username, &password);
|
||||
url->username = string_from_slice(scanner->src, username);
|
||||
url->password = string_from_slice(scanner->src, password);
|
||||
|
||||
if (!parse_host(scanner, &url->host, err))
|
||||
return false;
|
||||
|
||||
if (follows_char(*scanner, '/')) {
|
||||
/* absolute path */
|
||||
// The parsing of the path can't fail
|
||||
// because we already know there's at
|
||||
// leat a '/' for it.
|
||||
slice_t path;
|
||||
(void) parse_path(scanner, &path, err);
|
||||
url->path = string_from_slice(scanner->src, path);
|
||||
} else
|
||||
url->path = EMPTY_STRING;
|
||||
|
||||
} else {
|
||||
|
||||
url->host.mode = HP_HOSTMODE_NAME;
|
||||
url->host.name = EMPTY_STRING;
|
||||
url->host.no_port = true;
|
||||
url->host.port = 0;
|
||||
|
||||
url->username = EMPTY_STRING;
|
||||
url->password = EMPTY_STRING;
|
||||
|
||||
// TODO: Since there was no authority,
|
||||
// the path is non optional.
|
||||
|
||||
if (follows_char(*scanner, '?')) {
|
||||
report(err, "Missing path before query");
|
||||
return false;
|
||||
}
|
||||
if (follows_char(*scanner, '#')) {
|
||||
report(err, "Missing path before fragment");
|
||||
return false;
|
||||
}
|
||||
|
||||
slice_t path;
|
||||
if (!parse_path(scanner, &path, err))
|
||||
return false;
|
||||
url->path = string_from_slice(scanner->src, path);
|
||||
}
|
||||
|
||||
url->query = consume_char(scanner, '?')
|
||||
? string_from_slice(scanner->src, slice_up(scanner, is_query, is_query))
|
||||
: EMPTY_STRING;
|
||||
|
||||
url->fragment = consume_char(scanner, '#')
|
||||
? string_from_slice(scanner->src, slice_up(scanner, is_fragment, is_fragment))
|
||||
: EMPTY_STRING;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_header_name_body(unsigned char c)
|
||||
{
|
||||
return is_alpha(c) || is_digit(c) || c == '-';
|
||||
}
|
||||
|
||||
static bool is_header_name_head(unsigned char c)
|
||||
{
|
||||
return is_header_name_body(c);
|
||||
}
|
||||
|
||||
static bool is_header_body_body(unsigned char c)
|
||||
{
|
||||
return c != '\r';
|
||||
}
|
||||
|
||||
static bool is_header_body_head(unsigned char c)
|
||||
{
|
||||
return is_header_body_body(c);
|
||||
}
|
||||
|
||||
static bool parse_header(scanner_t *scanner, hp_header_t *header, hp_error_t *err)
|
||||
{
|
||||
slice_t name, body;
|
||||
|
||||
name = slice_up(scanner, is_header_name_head, is_header_name_body);
|
||||
if (name.length == 0) {
|
||||
report(err, "Missing header name");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!consume_char(scanner, ':')) {
|
||||
report(err, "Missing ':' after header name");
|
||||
return false;
|
||||
}
|
||||
|
||||
body = slice_up(scanner, is_header_body_head, is_header_body_body);
|
||||
|
||||
if (!consume_pair(scanner, "\r\n")) {
|
||||
report(err, "Missing CRLF after header");
|
||||
return false;
|
||||
}
|
||||
|
||||
header->name = string_from_slice(scanner->src, name);
|
||||
header->body = string_from_slice(scanner->src, body);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_version(scanner_t *scanner, int *major, int *minor, hp_error_t *err)
|
||||
{
|
||||
unsigned char char_major = '0';
|
||||
unsigned char char_minor = '0';
|
||||
|
||||
if (!consume_char(scanner, 'H') ||
|
||||
!consume_char(scanner, 'T') ||
|
||||
!consume_char(scanner, 'T') ||
|
||||
!consume_char(scanner, 'P') ||
|
||||
!consume_char(scanner, '/') ||
|
||||
!consume_digit(scanner, &char_major)) {
|
||||
report(err, "Invalid version token");
|
||||
return false;
|
||||
}
|
||||
if (consume_char(scanner, '.'))
|
||||
if (!consume_digit(scanner, &char_minor)) {
|
||||
report(err, "Invalid version token");
|
||||
return false;
|
||||
}
|
||||
*major = char_major - '0';
|
||||
*minor = char_minor - '0';
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool get_method_id(hp_string_t str, hp_method_t *method)
|
||||
{
|
||||
// CONNECT OPTIONS TRACE PATCH
|
||||
switch (str.len) {
|
||||
case 3:
|
||||
if (str.str[0] == 'G' &&
|
||||
str.str[1] == 'E' &&
|
||||
str.str[2] == 'T') {
|
||||
*method = HP_METHOD_GET;
|
||||
return true;
|
||||
}
|
||||
if (str.str[0] == 'P' &&
|
||||
str.str[1] == 'U' &&
|
||||
str.str[2] == 'T') {
|
||||
*method = HP_METHOD_PUT;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (str.str[0] == 'P' &&
|
||||
str.str[1] == 'O' &&
|
||||
str.str[2] == 'S' &&
|
||||
str.str[3] == 'T') {
|
||||
*method = HP_METHOD_POST;
|
||||
return true;
|
||||
}
|
||||
if (str.str[0] == 'H' &&
|
||||
str.str[1] == 'E' &&
|
||||
str.str[2] == 'A' &&
|
||||
str.str[3] == 'D') {
|
||||
*method = HP_METHOD_HEAD;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if (str.str[0] == 'T' &&
|
||||
str.str[1] == 'R' &&
|
||||
str.str[2] == 'A' &&
|
||||
str.str[3] == 'C' &&
|
||||
str.str[4] == 'E') {
|
||||
*method = HP_METHOD_TRACE;
|
||||
return true;
|
||||
}
|
||||
if (str.str[0] == 'P' &&
|
||||
str.str[1] == 'A' &&
|
||||
str.str[2] == 'T' &&
|
||||
str.str[3] == 'C' &&
|
||||
str.str[4] == 'H') {
|
||||
*method = HP_METHOD_PATCH;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 6:
|
||||
if (str.str[0] == 'D' &&
|
||||
str.str[1] == 'E' &&
|
||||
str.str[2] == 'L' &&
|
||||
str.str[3] == 'E' &&
|
||||
str.str[4] == 'T' &&
|
||||
str.str[5] == 'E') {
|
||||
*method = HP_METHOD_DELETE;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_method_body(unsigned char c)
|
||||
{
|
||||
return is_upper_alpha(c);
|
||||
}
|
||||
|
||||
static bool is_method_head(unsigned char c)
|
||||
{
|
||||
return is_method_body(c);
|
||||
}
|
||||
|
||||
static bool parse_method(scanner_t *scanner, hp_method_t *method, hp_error_t *err)
|
||||
{
|
||||
slice_t method_slice = slice_up(scanner, is_method_head, is_method_body);
|
||||
if (method_slice.length == 0) {
|
||||
report(err, "Missing method");
|
||||
return false;
|
||||
}
|
||||
hp_string_t method_string = string_from_slice(scanner->src, method_slice);
|
||||
if (!get_method_id(method_string, method)) {
|
||||
report(err, "Invalid method %.*s", (int) method_string.len, method_string.str);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_status_line(scanner_t *scanner,
|
||||
hp_method_t *method,
|
||||
hp_url_t *url,
|
||||
int *major, int *minor,
|
||||
hp_error_t *err)
|
||||
{
|
||||
if (!parse_method(scanner, method, err))
|
||||
return false;
|
||||
|
||||
if (!consume_char(scanner, ' ')) {
|
||||
report(err, "Missing space after method");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parse_url(scanner, url, err))
|
||||
return false;
|
||||
|
||||
if (!consume_char(scanner, ' ')) {
|
||||
report(err, "Missing space after URL");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parse_version(scanner, major, minor, err))
|
||||
return false;
|
||||
|
||||
if (!consume_pair(scanner, "\r\n")) {
|
||||
report(err, "Missing CRLF after version token");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void append_header(hp_request_t *req,
|
||||
hp_header_t header)
|
||||
{
|
||||
if (req->num_headers < HP_MAX_HEADERS)
|
||||
req->headers[req->num_headers++] = header;
|
||||
}
|
||||
|
||||
bool hp_parse(const char *src, size_t len,
|
||||
hp_request_t *out, hp_error_t *err)
|
||||
{
|
||||
scanner_t scanner = {(unsigned char*) src, len, 0};
|
||||
|
||||
if (!parse_status_line(&scanner, &out->method,
|
||||
&out->url, &out->major,
|
||||
&out->minor, err))
|
||||
return false;
|
||||
|
||||
out->num_headers = 0;
|
||||
while (!consume_pair(&scanner, "\r\n")) {
|
||||
|
||||
hp_header_t header;
|
||||
if (!parse_header(&scanner, &header, err))
|
||||
return false;
|
||||
|
||||
append_header(out, header);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hp_parse_url(const char *src, size_t len,
|
||||
hp_url_t *url, hp_error_t *err)
|
||||
{
|
||||
scanner_t scanner = {(unsigned char*) src, len, 0};
|
||||
return parse_url(&scanner, url, err);
|
||||
}
|
||||
|
||||
static char to_lower(char c)
|
||||
{
|
||||
if (is_upper_alpha(c))
|
||||
return c - 'A' + 'a';
|
||||
else
|
||||
return c;
|
||||
}
|
||||
|
||||
static bool case_insensitive_string_compare(hp_string_t s1, hp_string_t s2)
|
||||
{
|
||||
if (s1.len != s2.len)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < s1.len; i++)
|
||||
if (to_lower(s1.str[i]) != to_lower(s2.str[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
hp_header_t *hp_get_header(hp_request_t req, const char *name)
|
||||
{
|
||||
hp_string_t name2 = {name, strlen(name)};
|
||||
for (size_t i = 0; i < req.num_headers; i++) {
|
||||
hp_header_t *header = req.headers + i;
|
||||
if (case_insensitive_string_compare(name2, header->name))
|
||||
return header;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool parse_content_length(const char *src, size_t len, size_t *out, hp_error_t *error)
|
||||
{
|
||||
scanner_t scanner = {(unsigned char*) src, len, 0};
|
||||
consume_spaces(&scanner);
|
||||
|
||||
if (!follows_digit(scanner)) {
|
||||
report(error, "Non-digit character in Content-Length header");
|
||||
return false;
|
||||
}
|
||||
|
||||
*out = 0;
|
||||
unsigned char digit;
|
||||
while (consume_digit(&scanner, &digit)) {
|
||||
int k = digit - '0';
|
||||
if (*out > (SIZE_MAX - k) / 10) {
|
||||
report(error, "Unsigned integer is too big");
|
||||
return false;
|
||||
}
|
||||
*out = *out * 10 + k;
|
||||
}
|
||||
|
||||
if (scanner.cur < scanner.len) {
|
||||
report(error, "Invalid character '%c'", scanner.src[scanner.cur]);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hp_get_content_length(hp_request_t req, size_t *out, hp_error_t *error)
|
||||
{
|
||||
hp_header_t *header = hp_get_header(req, "Content-Length");
|
||||
if (header == NULL)
|
||||
return 0;
|
||||
return parse_content_length(header->body.str, header->body.len, out, error);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define HP_MAX_HEADERS 8
|
||||
|
||||
typedef struct {
|
||||
const char *str;
|
||||
size_t len;
|
||||
} hp_string_t;
|
||||
|
||||
typedef struct {
|
||||
bool occurred;
|
||||
char msg[256];
|
||||
} hp_error_t;
|
||||
|
||||
typedef enum {
|
||||
HP_METHOD_GET,
|
||||
HP_METHOD_PUT,
|
||||
HP_METHOD_POST,
|
||||
HP_METHOD_HEAD,
|
||||
HP_METHOD_PATCH,
|
||||
HP_METHOD_TRACE,
|
||||
HP_METHOD_DELETE,
|
||||
} hp_method_t;
|
||||
|
||||
typedef enum {
|
||||
HP_HOSTMODE_NAME,
|
||||
HP_HOSTMODE_IPV4,
|
||||
HP_HOSTMODE_IPV6,
|
||||
} hp_hostmode_t;
|
||||
|
||||
typedef struct {
|
||||
hp_hostmode_t mode;
|
||||
union {
|
||||
uint32_t ipv4;
|
||||
uint16_t ipv6[8];
|
||||
hp_string_t name;
|
||||
};
|
||||
bool no_port;
|
||||
uint16_t port;
|
||||
} hp_host_t;
|
||||
|
||||
typedef struct {
|
||||
hp_host_t host;
|
||||
hp_string_t path;
|
||||
hp_string_t query;
|
||||
hp_string_t schema;
|
||||
hp_string_t fragment;
|
||||
hp_string_t username;
|
||||
hp_string_t password;
|
||||
} hp_url_t;
|
||||
|
||||
typedef struct {
|
||||
hp_string_t name;
|
||||
hp_string_t body;
|
||||
} hp_header_t;
|
||||
|
||||
typedef struct {
|
||||
int major, minor;
|
||||
hp_url_t url;
|
||||
hp_method_t method;
|
||||
hp_header_t headers[HP_MAX_HEADERS];
|
||||
size_t num_headers;
|
||||
} hp_request_t;
|
||||
|
||||
bool hp_parse(const char *src, size_t len, hp_request_t *out, hp_error_t *err);
|
||||
bool hp_parse_url(const char *src, size_t len, hp_url_t *url, hp_error_t *err);
|
||||
hp_header_t *hp_get_header(hp_request_t req, const char *name);
|
||||
bool hp_get_content_length(hp_request_t req, size_t *out, hp_error_t *error);
|
||||
@@ -0,0 +1,81 @@
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <tuntap.h>
|
||||
#include "xhttp.h"
|
||||
|
||||
static xh_handle handle;
|
||||
|
||||
static void callback(xh_request *req, xh_response *res, void *userp)
|
||||
{
|
||||
(void) req;
|
||||
(void) userp;
|
||||
|
||||
res->status = 200;
|
||||
res->body.str = "Hello, world!";
|
||||
xh_header_add(res, "Content-Type", "text/plain");
|
||||
}
|
||||
/*
|
||||
static void handle_sigterm(int signum)
|
||||
{
|
||||
(void) signum;
|
||||
xh_quit(handle);
|
||||
}
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
/*
|
||||
signal(SIGTERM, handle_sigterm);
|
||||
signal(SIGINT, handle_sigterm);
|
||||
|
||||
#ifndef _WIN32
|
||||
signal(SIGQUIT, handle_sigterm);
|
||||
#endif
|
||||
*/
|
||||
char ip[] = "10.0.0.4";
|
||||
char mac[] = "00:01:00:01:00:00";
|
||||
|
||||
struct device *dev = tuntap_init();
|
||||
if (!dev) {
|
||||
fprintf(stderr, "Error: Couldn't initialize the TAP library\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// This must be set AFTER tuntap_init because
|
||||
// it sets the callback function to the default
|
||||
// callback which writes to stderr.
|
||||
//tuntap_log_set_cb(NULL);
|
||||
|
||||
int netmask = 24; // TODO: Make this configurable
|
||||
|
||||
if (tuntap_start(dev, TUNTAP_MODE_ETHERNET, TUNTAP_ID_ANY)) {
|
||||
fprintf(stderr, "Error: Couldn't set up the TAP device\n");
|
||||
tuntap_release(dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tuntap_set_ip(dev, ip, netmask);
|
||||
tuntap_set_hwaddr(dev, mac);
|
||||
|
||||
if (tuntap_up(dev)) {
|
||||
fprintf(stderr, "Error: Couldn't activate the TAP device\n");
|
||||
tuntap_release(dev);
|
||||
}
|
||||
|
||||
microhttp_config_t config = {
|
||||
.userp = dev,
|
||||
.ip = ip,
|
||||
.mac = mac,
|
||||
.recv_frame = (int(*)(void*, void*, size_t)) tuntap_read,
|
||||
.send_frame = (int(*)(void*, const void*, size_t)) tuntap_write,
|
||||
};
|
||||
|
||||
const char *error = xhttp(80, callback, NULL, &handle, config);
|
||||
tuntap_release(dev);
|
||||
if(error != NULL) {
|
||||
fprintf(stderr, "Error: %s\n", error);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,81 @@
|
||||
#ifndef XHTTP_H
|
||||
#define XHTTP_H
|
||||
|
||||
#define MICROHTTP_MAX_CLIENTS 64
|
||||
|
||||
typedef struct {
|
||||
void *userp;
|
||||
const char *ip;
|
||||
const char *mac;
|
||||
int (*send_frame)(void *userp, const void *src, size_t len);
|
||||
int (*recv_frame)(void *userp, void *dst, size_t len);
|
||||
} microhttp_config_t;
|
||||
|
||||
typedef void *xh_handle;
|
||||
|
||||
typedef struct {
|
||||
char *str; int len;
|
||||
} xh_string;
|
||||
|
||||
typedef struct {
|
||||
xh_string key, val;
|
||||
} xh_pair;
|
||||
|
||||
typedef struct {
|
||||
xh_pair *list;
|
||||
int count;
|
||||
} xh_table;
|
||||
|
||||
typedef enum {
|
||||
XH_GET = 1,
|
||||
XH_HEAD = 2,
|
||||
XH_POST = 4,
|
||||
XH_PUT = 8,
|
||||
XH_DELETE = 16,
|
||||
XH_CONNECT = 32,
|
||||
XH_OPTIONS = 64,
|
||||
XH_TRACE = 128,
|
||||
XH_PATCH = 256,
|
||||
} xh_method;
|
||||
|
||||
typedef struct {
|
||||
xh_method method_id;
|
||||
xh_string method;
|
||||
xh_string params;
|
||||
xh_string URL;
|
||||
unsigned int version_minor;
|
||||
unsigned int version_major;
|
||||
xh_table headers;
|
||||
xh_string body;
|
||||
} xh_request;
|
||||
|
||||
typedef struct {
|
||||
int status;
|
||||
xh_table headers;
|
||||
xh_string body;
|
||||
_Bool close;
|
||||
} xh_response;
|
||||
|
||||
typedef void (*xh_callback)(xh_request*, xh_response*, void*);
|
||||
|
||||
const char *xhttp(unsigned short port, xh_callback callback,
|
||||
void *userp, xh_handle *handle,
|
||||
microhttp_config_t config);
|
||||
void xh_quit(xh_handle handle);
|
||||
|
||||
void xh_header_add(xh_response *res, const char *name, const char *valfmt, ...);
|
||||
void xh_header_rem(xh_response *res, const char *name);
|
||||
const char *xh_header_get(void *req_or_res, const char *name);
|
||||
_Bool xh_header_cmp(const char *a, const char *b);
|
||||
|
||||
int xh_urlcmp(const char *URL, const char *fmt, ...);
|
||||
int xh_vurlcmp(const char *URL, const char *fmt, va_list va);
|
||||
|
||||
|
||||
#define xh_string_new(s, l) \
|
||||
((xh_string) { (s), ((int) (l)) < 0 ? (int) strlen(s) : (int) (l) })
|
||||
|
||||
#define xh_string_from_literal(s) \
|
||||
((xh_string) { (s), sizeof(s)-1 })
|
||||
|
||||
#endif // #ifndef XHTTP_H
|
||||
@@ -1,302 +0,0 @@
|
||||
#include "queue.h"
|
||||
|
||||
typedef struct queue_entry_t queue_entry_t;
|
||||
typedef struct queue_event_t queue_event_t;
|
||||
|
||||
struct queue_entry_t {
|
||||
queue_entry_t *prev;
|
||||
queue_entry_t *next;
|
||||
void *data;
|
||||
int events;
|
||||
microtcp_queue_t *queue;
|
||||
microtcp_socket_t *socket;
|
||||
queue_event_t *event_entry;
|
||||
};
|
||||
|
||||
struct queue_event_t {
|
||||
|
||||
queue_event_t *prev;
|
||||
queue_event_t *next;
|
||||
|
||||
int events;
|
||||
queue_entry_t *socket_entry;
|
||||
};
|
||||
|
||||
struct microtcp_queue_t {
|
||||
|
||||
microtcp_t *mtcp;
|
||||
|
||||
pthread_cond_t something_happened;
|
||||
|
||||
queue_event_t *queue_head;
|
||||
queue_event_t *queue_tail;
|
||||
|
||||
queue_entry_t *entry_free_list;
|
||||
queue_entry_t entry_pool[MICROTCP_QUEUE_ENTRIES_MAX];
|
||||
|
||||
queue_event_t *event_free_list;
|
||||
queue_event_t event_pool[MICROTCP_QUEUE_ENTRIES_MAX];
|
||||
};
|
||||
|
||||
static event_entry_t*
|
||||
pop_event_entry_from_queue(microtcp_queue_t *queue)
|
||||
{
|
||||
event_entry_t *event;
|
||||
|
||||
if (queue->queue_tail) {
|
||||
|
||||
// An event is present in the queue. Pop it.
|
||||
|
||||
event = queue->queue_tail;
|
||||
|
||||
if (event->prev)
|
||||
event->prev->next = NULL;
|
||||
else
|
||||
queue->queue_head = NULL;
|
||||
queue->queue_tail = event->prev;
|
||||
|
||||
} else
|
||||
event = NULL;
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
microtcp_event_t microtcp_queue_next(microtcp_queue_t *queue, bool no_block)
|
||||
{
|
||||
microtcp_t *mtcp;
|
||||
|
||||
event_entry_t *event = pop_event_entry_from_queue(queue);
|
||||
|
||||
while (!event && !no_block) {
|
||||
pthread_cond_wait(&queue->something_happened, &mtcp->lock);
|
||||
event = pop_event_entry_from_queue(queue);
|
||||
}
|
||||
|
||||
microtcp_event_t result;
|
||||
|
||||
if (event) {
|
||||
queue_entry_t *socket_entry = event->socket_entry;
|
||||
|
||||
result.type = event->events;
|
||||
result.data = socket_entry->data;
|
||||
result.socket = socket_entry->socket;
|
||||
|
||||
} else {
|
||||
result.type = 0;
|
||||
result.data = NULL;
|
||||
result.socket = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void signal_socket_events_to_queues(microtcp_socket_t *socket, int events)
|
||||
{
|
||||
queue_entry_t *entry = socket->queue_entry;
|
||||
while (entry) {
|
||||
|
||||
int interesting_events = events & entry->events;
|
||||
|
||||
if (interesting_events) {
|
||||
// Some events that are of interest to
|
||||
// this queue are being signaled.
|
||||
// Push them onto the queue's event queue
|
||||
|
||||
microtcp_queue_t *queue = entry->queue;
|
||||
|
||||
if (entry->event_entry) {
|
||||
// At least one event was already signaled, so
|
||||
// an event structure was already created.
|
||||
// We just need to add to it the newly signaled
|
||||
// events.
|
||||
event_entry_t *event = entry->event_entry;
|
||||
event->events |= interesting_events;
|
||||
} else {
|
||||
|
||||
// No events related to this socket are in the
|
||||
// queue, so we need to create a new event structure.
|
||||
//
|
||||
// Unused event structure are organized in a free
|
||||
// list, so we just need to pop one from there.
|
||||
//
|
||||
// We know for sure that a free event structure is
|
||||
// available at this point because there is one for
|
||||
// each socket registered into the queue.
|
||||
assert(queue->event_free_list);
|
||||
|
||||
event_entry_t *event = queue->event_free_list;
|
||||
queue->event_free_list = event->next;
|
||||
|
||||
// Set-up the event structure
|
||||
event->prev = NULL;
|
||||
event->next = NULL;
|
||||
event->events = interesting_events;
|
||||
event->socket_entry = entry;
|
||||
|
||||
// Now push the event structure into the queue's queue
|
||||
event->next = queue->queue_head;
|
||||
if (queue->queue_head)
|
||||
queue->queue_head->prev = event;
|
||||
else
|
||||
queue->queue_tail = event;
|
||||
queue->queue_head = event;
|
||||
}
|
||||
|
||||
pthread_cond_signal(&queue->something_happened);
|
||||
}
|
||||
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
microtcp_queue_t *microtcp_queue_create(microtcp_t *mtcp)
|
||||
{
|
||||
microtcp_queue_t *queue = malloc(sizeof(microtcp_queue_t));
|
||||
if (!queue)
|
||||
return NULL;
|
||||
|
||||
queue->mtcp = mtcp;
|
||||
queue->queue_head = NULL;
|
||||
queue->queue_tail = NULL;
|
||||
queue->entry_free_list = queue->entry_pool;
|
||||
queue->event_free_list = queue->event_pool;
|
||||
|
||||
for (size_t i = 0; i < MICROTCP_QUEUE_ENTRIES_MAX-1; i++) {
|
||||
mtcp->entry_pool[i].mtcp = NULL;
|
||||
mtcp->entry_pool[i].prev = NULL;
|
||||
mtcp->entry_pool[i].next = mtcp->entry_pool + i+1;
|
||||
|
||||
mtcp->event_pool[i].mtcp = NULL;
|
||||
mtcp->event_pool[i].prev = NULL;
|
||||
mtcp->event_pool[i].next = mtcp->event_pool + i+1;
|
||||
}
|
||||
mtcp->entry_pool[MICROTCP_QUEUE_ENTRIES_MAX-1].mtcp = NULL;
|
||||
mtcp->entry_pool[MICROTCP_QUEUE_ENTRIES_MAX-1].prev = NULL;
|
||||
mtcp->entry_pool[MICROTCP_QUEUE_ENTRIES_MAX-1].next = NULL;
|
||||
|
||||
mtcp->event_pool[MICROTCP_QUEUE_ENTRIES_MAX-1].prev = NULL;
|
||||
mtcp->event_pool[MICROTCP_QUEUE_ENTRIES_MAX-1].next = NULL;
|
||||
|
||||
if (pthread_cond_init(&queue->something_happened, NULL)) {
|
||||
free(queue);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
void microtcp_queue_destroy(microtcp_queue_t *queue)
|
||||
{
|
||||
for (size_t i = 0; i < MICROTCP_QUEUE_ENTRIES_MAX; i++) {
|
||||
if (queue->entries[i].mtcp == NULL)
|
||||
continue;
|
||||
microtcp_queue_unregister(queue, queue->entries[i].socket, MICROTCP_EVENT_ALL);
|
||||
}
|
||||
free(queue);
|
||||
}
|
||||
|
||||
bool microtcp_queue_register(microtcp_queue_t *queue, microtcp_socket_t *socket, void *data, int events)
|
||||
{
|
||||
if (!events)
|
||||
return true; // No events registered
|
||||
|
||||
if (!queue->free_list)
|
||||
return false; // Registration limit reached
|
||||
|
||||
queue_entry_t *entry = queue->free_list;
|
||||
queue->free_list = entry->next;
|
||||
|
||||
entry->prev = NULL;
|
||||
entry->next = NULL;
|
||||
entry->data = data;
|
||||
entry->queue = queue;
|
||||
entry->events = events;
|
||||
entry->socket = socket;
|
||||
|
||||
entry->next = socket->queue_entry;
|
||||
if (socket->queue_entry)
|
||||
socket->queue_entry->prev = entry;
|
||||
socket->queue_entry = entry;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static queue_entry_t *find_entry(microtcp_queue_t *queue, microtcp_socket_t *socket)
|
||||
{
|
||||
queue_entry_t *entry = socket->queue_entry;
|
||||
while (entry) {
|
||||
if (entry->queue == queue)
|
||||
return entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
unregister_socket_from_all_queues(microtcp_socket_t *socket)
|
||||
{
|
||||
while (socket->queue_entry)
|
||||
microtcp_queue_unregister(socket->queue_entry->queue, socket, MICROTCP_EVENT_ALL);
|
||||
}
|
||||
|
||||
bool microtcp_queue_unregister(microtcp_queue_t *queue, microtcp_socket_t *socket, int unregister_events)
|
||||
{
|
||||
if (!queue || !socket || queue->mtcp != socket->mtcp)
|
||||
return false;
|
||||
|
||||
queue_entry_t *entry = find_entry(queue, socket);
|
||||
if (!entry)
|
||||
return false; // Socket wasn't registered in this queue
|
||||
|
||||
int remaining_events = entry->events & ~unregister_events;
|
||||
|
||||
if (!remaining_events) {
|
||||
|
||||
// All events were unregistered, so the
|
||||
// socket must be removed from the queue.
|
||||
|
||||
// Unlink any events related to this socket
|
||||
// from the queue
|
||||
queue_event_t *event = entry->event_entry;
|
||||
if (event) {
|
||||
|
||||
// Remove the event structure from the queue
|
||||
if (event->prev)
|
||||
event->prev->next = event->next;
|
||||
else
|
||||
queue->queue_head = event->next;
|
||||
|
||||
if (event->next)
|
||||
event->next->prev = event->prev;
|
||||
else
|
||||
queue->queue_tail = event->prev;
|
||||
|
||||
// Push the now unlinked event structure onto
|
||||
// the free list
|
||||
event->prev = NULL;
|
||||
event->next = queue->event_free_list;
|
||||
queue->event_free_list = event;
|
||||
}
|
||||
|
||||
// Unlink the entry from the socket's list
|
||||
if (entry->prev)
|
||||
entry->prev->next = entry->next;
|
||||
else
|
||||
socket->queue_entry = entry->next;
|
||||
|
||||
if (entry->next)
|
||||
entry->next->prev = entry->prev;
|
||||
|
||||
// Put the entry structure back into the
|
||||
// free list of the queue
|
||||
entry->mtcp = NULL; // It's important to set this to NULL because
|
||||
// this way the queue knows it's not used anymore.
|
||||
entry->prev = NULL;
|
||||
entry->next = queue->free_list;
|
||||
queue->free_list = entry;
|
||||
|
||||
} else
|
||||
entry->events = remaining_events;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
enum {
|
||||
MICROTCP_EVENT_NONE = 0,
|
||||
MICROTCP_EVENT_SEND = 1,
|
||||
MICROTCP_EVENT_RECV = 2,
|
||||
MICROTCP_EVENT_ACCEPT = 4,
|
||||
|
||||
MICROTCP_EVENT_ALL = MICROTCP_EVENT_RECV
|
||||
| MICROTCP_EVENT_SEND
|
||||
| MICROTCP_EVENT_ACCEPT,
|
||||
};
|
||||
|
||||
typedef struct microtcp_queue_t microtcp_queue_t;
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
void *data;
|
||||
microtcp_socket_t *socket;
|
||||
} microtcp_event_t;
|
||||
|
||||
microtcp_queue_t *microtcp_queue_create(microtcp_t *mtcp);
|
||||
void microtcp_queue_destroy(microtcp_queue_t *queue);
|
||||
microtcp_event_t microtcp_queue_next(microtcp_queue_t *queue, bool no_block);
|
||||
bool microtcp_queue_register(microtcp_queue_t *queue, microtcp_socket_t *socket, void *data, int events);
|
||||
bool microtcp_queue_unregister(microtcp_queue_t *queue, microtcp_socket_t *socket, int unregister_events);
|
||||
@@ -1,54 +0,0 @@
|
||||
|
||||
typedef struct {
|
||||
size_t size;
|
||||
size_t used;
|
||||
uint8_t *data;
|
||||
} buffer_t;
|
||||
|
||||
typedef struct client_t client_t;
|
||||
struct client_t {
|
||||
client_t *prev;
|
||||
client_t *next;
|
||||
microtcp_socket_t *socket;
|
||||
buffer_t input;
|
||||
buffer_t output;
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
microtcp_t *mtcp = microtcp_create();
|
||||
|
||||
microtcp_socket_t *server = microtcp_open(mtcp, 80);
|
||||
|
||||
microtcp_queue_t *queue = microtcp_queue_create(mtcp);
|
||||
microtcp_queue_register(queue, server, NULL, MICROTCP_EVENT_ACCEPT);
|
||||
while (1) {
|
||||
microtcp_event_t event = microtcp_queue_next(queue, no_block);
|
||||
switch (event.type) {
|
||||
|
||||
case MICROTCP_EVENT_NONE:
|
||||
break;
|
||||
|
||||
case MICROTCP_EVENT_READ:
|
||||
break;
|
||||
|
||||
case MICROTCP_EVENT_RECV:
|
||||
{
|
||||
microtcp_socket_t *client_socket = event.socket;
|
||||
microtcp_recv(client_socket, );
|
||||
}
|
||||
break;
|
||||
|
||||
case MICROTCP_EVENT_ACCEPT:
|
||||
{
|
||||
microtcp_socket_t *client_socket = microtcp_accept(event.socket, true);
|
||||
microtcp_queue_register(queue, client_socket, NULL, MICROTCP_EVENT_SEND | MICROTCP_EVENT_RECV);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
microtcp_queue_destroy(queue);
|
||||
|
||||
microtcp_destroy(mtcp);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "mutcp.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
mutcp_t state;
|
||||
mutcp_create(&state);
|
||||
|
||||
mutcp_listener_socket_t *listener =
|
||||
mutcp_listener_socket_create(&state, 8080);
|
||||
|
||||
while (1) {
|
||||
mutcp_socket_t *socket =
|
||||
mutcp_listener_socket_accept(listener);
|
||||
|
||||
char buffer[1024];
|
||||
size_t num = mutcp_socket_read(socket, buffer, sizeof(buffer));
|
||||
mutcp_socket_write(socket, "echo: ", 6);
|
||||
mutcp_socket_write(socket, buffer, num);
|
||||
|
||||
mutcp_socket_destroy(socket);
|
||||
}
|
||||
|
||||
mutcp_listener_socket_destroy(listener);
|
||||
mutcp_destroy(&state);
|
||||
return 0;
|
||||
}
|
||||
@@ -9,6 +9,7 @@ typedef struct microtcp_socket_t microtcp_socket_t;
|
||||
|
||||
#define MICROTCP_MAX_BUFFERS 8
|
||||
#define MICROTCP_MAX_SOCKETS 32
|
||||
#define MICROTCP_MAX_MUX_ENTRIES 32
|
||||
|
||||
typedef enum {
|
||||
|
||||
@@ -28,6 +29,9 @@ typedef enum {
|
||||
// Returned by microtcp_recv and microtcp_send
|
||||
MICROTCP_ERRCODE_NOTCONNECTION,
|
||||
|
||||
// Returned by microtcp_accept, microtcp_recv and microtcp_send
|
||||
MICROTCP_ERRCODE_WOULDBLOCK,
|
||||
|
||||
} microtcp_errcode_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -52,3 +56,25 @@ size_t microtcp_send(microtcp_socket_t *socket, const void *src, siz
|
||||
size_t microtcp_recv(microtcp_socket_t *socket, void *dst, size_t len, bool no_block, microtcp_errcode_t *errcode);
|
||||
void microtcp_step(microtcp_t *mtcp);
|
||||
void microtcp_process_packet(microtcp_t *mtcp, const void *packet, size_t len);
|
||||
|
||||
#ifdef MICROTCP_USING_MUX
|
||||
typedef enum {
|
||||
MICROTCP_MUX_ACCEPT = 1,
|
||||
MICROTCP_MUX_RECV = 2,
|
||||
MICROTCP_MUX_SEND = 4,
|
||||
} microtcp_muxeventid_t;
|
||||
|
||||
typedef struct {
|
||||
void *userp;
|
||||
int events;
|
||||
microtcp_socket_t *socket;
|
||||
} microtcp_muxevent_t;
|
||||
|
||||
typedef struct microtcp_mux_t microtcp_mux_t;
|
||||
microtcp_mux_t *microtcp_mux_create(microtcp_t *mtcp);
|
||||
void microtcp_mux_destroy(microtcp_mux_t *mux);
|
||||
bool microtcp_mux_register(microtcp_mux_t *mux, microtcp_socket_t *sock, int events, void *userp);
|
||||
bool microtcp_mux_unregister(microtcp_mux_t *mux, microtcp_socket_t *sock, int events);
|
||||
bool microtcp_mux_poll(microtcp_mux_t *mux, microtcp_muxevent_t *ev);
|
||||
bool microtcp_mux_wait(microtcp_mux_t *mux, microtcp_muxevent_t *ev);
|
||||
#endif
|
||||
@@ -45,10 +45,10 @@ endif
|
||||
# One of: drmemory, valgrind
|
||||
MEMDBG=valgrind
|
||||
|
||||
LIBDIR = 3p/lib/
|
||||
INCDIR = 3p/include/
|
||||
LIBDIR = 3p/lib
|
||||
INCDIR = 3p/include
|
||||
|
||||
CFLAGS = $(CFLAGS_PLATFORM) -I$(INCDIR) -Iinclude/ -Wall -Wextra -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_BACKGROUND_THREAD -DMICROTCP_USING_TAP
|
||||
CFLAGS = $(CFLAGS_PLATFORM) -I$(INCDIR) -Iinclude/ -Wall -Wextra -DARP_DEBUG -DMICROTCP_DEBUG -DIP_DEBUG -DICMP_DEBUG -DTCP_DEBUG -DMICROTCP_BACKGROUND_THREAD -DMICROTCP_USING_TAP -DMICROTCP_USING_MUX
|
||||
LFLAGS = -ltuntap $(LFLAGS_PLATFORM) -L$(LIBDIR)
|
||||
|
||||
ifeq ($(MEMDBG),drmemory)
|
||||
@@ -57,12 +57,9 @@ else
|
||||
CFLAGS += -g
|
||||
endif
|
||||
|
||||
INCDIR=3p/include
|
||||
LIBDIR=3p/lib
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: loop2
|
||||
all: build/microtcp.h build/microtcp.c build/echo_tcp build/echo_http
|
||||
|
||||
3p/lib/libtuntap.a: 3p/libtuntap/build/lib/libtuntap.a
|
||||
cp 3p/libtuntap/build/lib/libtuntap.a $(LIBDIR)
|
||||
@@ -90,10 +87,55 @@ all: loop2
|
||||
3p/src/tinycthread.c: 3p/tinycthread/source/tinycthread.c
|
||||
cp 3p/tinycthread/source/tinycthread.c 3p/src
|
||||
|
||||
loop2: $(LIBDIR)/libtuntap.a 3p/include/tuntap.h 3p/include/tuntap-export.h 3p/src/tinycthread.c 3p/include/tinycthread.h
|
||||
gcc src/endian.c src/arp.c src/ip.c src/icmp.c src/tcp.c src/microtcp.c test/loop2.c 3p/src/tinycthread.c -o loop2 $(CFLAGS) $(LFLAGS)
|
||||
build/echo_tcp: examples/echo_tcp.c $(LIBDIR)/libtuntap.a 3p/include/tuntap.h 3p/include/tuntap-export.h build/microtcp.c build/microtcp.h
|
||||
mkdir -p $(@D)
|
||||
gcc build/microtcp.c examples/echo_tcp.c -o $@ $(CFLAGS) $(LFLAGS)
|
||||
|
||||
build/microtcp.h: include/microtcp.h
|
||||
mkdir -p $(@D)
|
||||
[ ! -e $@ ] || rm $@
|
||||
echo "#define MICROTCP_AMALGAMATION" >> $@
|
||||
cat include/microtcp.h >> $@
|
||||
|
||||
build/microtcp.c: 3p/include/tinycthread.h 3p/src/tinycthread.c $(wildcard src/*.c src/*.h)
|
||||
mkdir -p $(@D)
|
||||
[ ! -e $@ ] || rm $@
|
||||
printf "#include \"microtcp.h\"\n" > $@
|
||||
printf "#ifdef MICROTCP_BACKGROUND_THREAD" >> $@
|
||||
printf "\n#line 1 \"3p/include/tinycthread.h\"\n" >> $@
|
||||
cat 3p/include/tinycthread.h >> $@
|
||||
printf "\n#line 1 \"3p/src/tinycthread.c\"\n" >> $@
|
||||
cat 3p/src/tinycthread.c >> $@
|
||||
printf "\n#endif /* MICROTCP_BACKGROUND_THREAD */" >> $@
|
||||
printf "\n#line 1 \"src/defs.h\"\n" >> $@
|
||||
cat src/defs.h >> $@
|
||||
printf "\n#line 1 \"src/endian.h\"\n" >> $@
|
||||
cat src/endian.h >> $@
|
||||
printf "\n#line 1 \"src/arp.h\"\n" >> $@
|
||||
cat src/arp.h >> $@
|
||||
printf "\n#line 1 \"src/icmp.h\"\n" >> $@
|
||||
cat src/icmp.h >> $@
|
||||
printf "\n#line 1 \"src/ip.h\"\n" >> $@
|
||||
cat src/ip.h >> $@
|
||||
printf "\n#line 1 \"src/tcp.h\"\n" >> $@
|
||||
cat src/tcp.h >> $@
|
||||
printf "\n#line 1 \"src/endian.c\"\n" >> $@
|
||||
cat src/endian.c >> $@
|
||||
printf "\n#line 1 \"src/arp.c\"\n" >> $@
|
||||
cat src/arp.c >> $@
|
||||
printf "\n#line 1 \"src/icmp.c\"\n" >> $@
|
||||
cat src/icmp.c >> $@
|
||||
printf "\n#line 1 \"src/ip.c\"\n" >> $@
|
||||
cat src/ip.c >> $@
|
||||
printf "\n#line 1 \"src/tcp.c\"\n" >> $@
|
||||
cat src/tcp.c >> $@
|
||||
printf "\n#line 1 \"src/microtcp.c\"\n" >> $@
|
||||
cat src/microtcp.c >> $@
|
||||
|
||||
build/echo_http: $(LIBDIR)/libtuntap.a 3p/include/tuntap.h 3p/include/tuntap-export.h build/microtcp.h build/microtcp.c
|
||||
gcc examples/microhttp/main.c examples/microhttp/xhttp.c build/microtcp.c -o $@ $(CFLAGS) $(LFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f loop2 loop2.exe
|
||||
rm -fr build
|
||||
rm -fr 3p/libtuntap/build
|
||||
rm -f 3p/lib/* 3p/include/*
|
||||
@@ -115,8 +115,11 @@ struct ethhdr {
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef MICROTCP_AMALGAMATION
|
||||
#include "endian.h"
|
||||
#include "arp.h"
|
||||
#endif
|
||||
|
||||
#ifdef ARP_DEBUG
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef MICROTCP_AMALGAMATION
|
||||
#include "defs.h"
|
||||
#endif
|
||||
|
||||
#define ARP_MAX_PENDING_REQUESTS 32
|
||||
#define ARP_TRANSLATION_TABLE_SIZE 128
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#ifndef MICROTCP_AMALGAMATION
|
||||
#include "endian.h"
|
||||
#endif
|
||||
|
||||
bool cpu_is_little_endian(void)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include <string.h>
|
||||
|
||||
#ifndef MICROTCP_AMALGAMATION
|
||||
#include "endian.h"
|
||||
#include "icmp.h"
|
||||
#endif
|
||||
|
||||
#ifdef ICMP_DEBUG
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef MICROTCP_AMALGAMATION
|
||||
#include "defs.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
void *output_ptr;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include <string.h>
|
||||
|
||||
#ifndef MICROTCP_AMALGAMATION
|
||||
#include "endian.h"
|
||||
#include "ip.h"
|
||||
#endif
|
||||
|
||||
#ifdef IP_DEBUG
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef MICROTCP_AMALGAMATION
|
||||
#include "defs.h"
|
||||
#include "icmp.h"
|
||||
#endif
|
||||
|
||||
#define IP_PLUGGED_PROTOCOLS_MAX 4
|
||||
|
||||
|
||||
+438
-33
@@ -4,20 +4,22 @@
|
||||
#include <string.h> // strerror()
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "ip.h"
|
||||
#include "arp.h"
|
||||
#include "tcp.h"
|
||||
#include "endian.h"
|
||||
#include <microtcp.h>
|
||||
|
||||
#ifndef MICROTCP_AMALGAMATION
|
||||
# include "ip.h"
|
||||
# include "arp.h"
|
||||
# include "tcp.h"
|
||||
# include "endian.h"
|
||||
# include "microtcp.h"
|
||||
# ifdef MICROTCP_BACKGROUND_THREAD
|
||||
# include "tinycthread.h"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef MICROTCP_USING_TAP
|
||||
#include <tuntap.h>
|
||||
#endif
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
#include "tinycthread.h"
|
||||
#endif
|
||||
|
||||
#ifdef MICROTCP_DEBUG
|
||||
#include <stdio.h>
|
||||
#define MICROTCP_DEBUG_LOG(fmt, ...) do { fprintf(stderr, "MICROTCP :: " fmt "\n", ## __VA_ARGS__); } while (0);
|
||||
@@ -33,6 +35,35 @@
|
||||
#define UNLOCK_WHEN_THREADED(mtcp) do { (void) (mtcp); } while (0);
|
||||
#endif
|
||||
|
||||
#ifdef MICROTCP_USING_MUX
|
||||
typedef struct mux_entry_t mux_entry_t;
|
||||
struct mux_entry_t {
|
||||
mux_entry_t **mux_prev;
|
||||
mux_entry_t *mux_next;
|
||||
mux_entry_t **sock_prev;
|
||||
mux_entry_t *sock_next;
|
||||
microtcp_mux_t *mux; // This is set on initialization
|
||||
// of the parent microtcp_mux_t
|
||||
// and never changed.
|
||||
microtcp_socket_t *sock;
|
||||
void *userp;
|
||||
int triggered_events;
|
||||
int events_of_interest;
|
||||
};
|
||||
|
||||
struct microtcp_mux_t {
|
||||
microtcp_t *mtcp;
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
cnd_t queue_not_empty;
|
||||
#endif
|
||||
mux_entry_t *free_list;
|
||||
mux_entry_t *idle_list;
|
||||
mux_entry_t *ready_queue_head;
|
||||
mux_entry_t *ready_queue_tail;
|
||||
mux_entry_t entries[MICROTCP_MAX_MUX_ENTRIES];
|
||||
};
|
||||
#endif
|
||||
|
||||
typedef struct buffer_t buffer_t;
|
||||
struct buffer_t {
|
||||
microtcp_t *mtcp;
|
||||
@@ -56,6 +87,7 @@ struct microtcp_socket_t {
|
||||
tcp_listener_t *listener;
|
||||
tcp_connection_t *connection;
|
||||
};
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
union {
|
||||
cnd_t something_to_accept;
|
||||
@@ -65,6 +97,10 @@ struct microtcp_socket_t {
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef MICROTCP_USING_MUX
|
||||
mux_entry_t *mux_list;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct microtcp_t {
|
||||
@@ -105,6 +141,7 @@ const char *microtcp_strerror(microtcp_errcode_t errcode)
|
||||
case MICROTCP_ERRCODE_BADCONDVAR: return "Condition variable error";
|
||||
case MICROTCP_ERRCODE_NOTLISTENER: return "Invalid operation on a non-listener socket";
|
||||
case MICROTCP_ERRCODE_CANTBLOCK: return "Can't execute a blocking call for this function";
|
||||
case MICROTCP_ERRCODE_WOULDBLOCK: return "Can't executa e non-blocking call for this function";
|
||||
case MICROTCP_ERRCODE_NOTCONNECTION: return "Invalid operation on a non-connection socket";
|
||||
}
|
||||
return "???";
|
||||
@@ -671,9 +708,9 @@ bool microtcp_callbacks_create_for_tap(const char *ip, const char *mac,
|
||||
|
||||
*callbacks = (microtcp_callbacks_t) {
|
||||
.data = dev,
|
||||
.free = tuntap_release,
|
||||
.recv = tuntap_read,
|
||||
.send = tuntap_write,
|
||||
.free = (void(*)(void*)) tuntap_release,
|
||||
.recv = (int(*)(void*, void*, size_t)) tuntap_read,
|
||||
.send = (int(*)(void*, const void*, size_t)) tuntap_write,
|
||||
};
|
||||
|
||||
return true;
|
||||
@@ -758,13 +795,22 @@ push_unlinked_socket_into_free_list(microtcp_t *mtcp, microtcp_socket_t *socket)
|
||||
mtcp->free_socket_list = socket;
|
||||
}
|
||||
|
||||
#ifdef MICROTCP_USING_MUX
|
||||
static void
|
||||
signal_events_to_muxes_associated_to_socket(microtcp_socket_t *socket, int events);
|
||||
#endif
|
||||
|
||||
static void ready_to_accept(void *data)
|
||||
{
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
microtcp_socket_t *socket = data;
|
||||
(void) socket;
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
cnd_signal(&socket->something_to_accept);
|
||||
#else
|
||||
(void) data;
|
||||
#endif
|
||||
|
||||
#ifdef MICROTCP_USING_MUX
|
||||
signal_events_to_muxes_associated_to_socket(socket, MICROTCP_MUX_ACCEPT);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -795,6 +841,10 @@ microtcp_socket_t *microtcp_open(microtcp_t *mtcp, uint16_t port,
|
||||
socket->type = SOCKET_LISTENER;
|
||||
socket->listener = listener;
|
||||
|
||||
#ifdef MICROTCP_USING_MUX
|
||||
socket->mux_list = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
if (cnd_init(&socket->something_to_accept) != thrd_success) {
|
||||
errcode2 = MICROTCP_ERRCODE_BADCONDVAR;
|
||||
@@ -819,6 +869,8 @@ void microtcp_close(microtcp_socket_t *socket)
|
||||
|
||||
microtcp_t *mtcp = socket->mtcp;
|
||||
|
||||
#warning "sockets should unregister from all multiplexers"
|
||||
|
||||
LOCK_WHEN_THREADED(mtcp);
|
||||
{
|
||||
switch (socket->type) {
|
||||
@@ -843,21 +895,29 @@ void microtcp_close(microtcp_socket_t *socket)
|
||||
|
||||
static void ready_to_recv(void *data)
|
||||
{
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
microtcp_socket_t *socket = data;
|
||||
(void) socket;
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
cnd_signal(&socket->something_to_recv);
|
||||
#else
|
||||
(void) data;
|
||||
#endif
|
||||
|
||||
#ifdef MICROTCP_USING_MUX
|
||||
signal_events_to_muxes_associated_to_socket(socket, MICROTCP_MUX_RECV);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ready_to_send(void *data)
|
||||
{
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
microtcp_socket_t *socket = data;
|
||||
(void) socket;
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
cnd_signal(&socket->something_to_send);
|
||||
#else
|
||||
(void) data;
|
||||
#endif
|
||||
|
||||
#ifdef MICROTCP_USING_MUX
|
||||
signal_events_to_muxes_associated_to_socket(socket, MICROTCP_MUX_SEND);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -886,14 +946,20 @@ microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
while (!connection && !no_block) {
|
||||
if (cnd_wait(&socket->something_to_accept, &mtcp->lock) != thrd_success)
|
||||
abort();
|
||||
if (cnd_wait(&socket->something_to_accept, &mtcp->lock) != thrd_success) {
|
||||
errcode2 = MICROTCP_ERRCODE_BADCONDVAR;
|
||||
push_unlinked_socket_into_free_list(mtcp, socket2);
|
||||
goto unlock_and_exit;
|
||||
}
|
||||
connection = tcp_listener_accept(socket->listener, socket2, ready_to_recv, ready_to_send);
|
||||
}
|
||||
#else
|
||||
if (!connection && !no_block) {
|
||||
if (!connection) {
|
||||
if (no_block)
|
||||
errcode2 = MICROTCP_ERRCODE_WOULDBLOCK;
|
||||
else
|
||||
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
|
||||
push_unlinked_socket_into_free_list(mtcp, socket2);
|
||||
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
|
||||
goto unlock_and_exit;
|
||||
}
|
||||
#endif
|
||||
@@ -904,6 +970,10 @@ microtcp_socket_t *microtcp_accept(microtcp_socket_t *socket,
|
||||
socket2->type = SOCKET_CONNECTION;
|
||||
socket2->connection = connection;
|
||||
|
||||
#ifdef MICROTCP_USING_MUX
|
||||
socket2->mux_list = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
if (cnd_init(&socket2->something_to_recv) != thrd_success) {
|
||||
errcode2 = MICROTCP_ERRCODE_BADCONDVAR;
|
||||
@@ -953,13 +1023,18 @@ size_t microtcp_recv(microtcp_socket_t *socket,
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
while (num == 0 && !no_block) {
|
||||
if (cnd_wait(&socket->something_to_recv, &mtcp->lock) != thrd_success)
|
||||
abort();
|
||||
if (cnd_wait(&socket->something_to_recv, &mtcp->lock) != thrd_success) {
|
||||
errcode2 = MICROTCP_ERRCODE_BADCONDVAR;
|
||||
goto unlock_and_exit;
|
||||
}
|
||||
num = tcp_connection_recv(socket->connection, dst, len);
|
||||
}
|
||||
#else
|
||||
if (num == 0 && !no_block) {
|
||||
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
|
||||
if (num == 0) {
|
||||
if (no_block)
|
||||
errcode2 = MICROTCP_ERRCODE_WOULDBLOCK;
|
||||
else
|
||||
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
|
||||
goto unlock_and_exit;
|
||||
}
|
||||
#endif
|
||||
@@ -994,18 +1069,348 @@ size_t microtcp_send(microtcp_socket_t *socket,
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
while (num == 0 && !no_block) {
|
||||
if (cnd_wait(&socket->something_to_send, &mtcp->lock) != thrd_success)
|
||||
abort();
|
||||
if (cnd_wait(&socket->something_to_send, &mtcp->lock) != thrd_success) {
|
||||
errcode2 = MICROTCP_ERRCODE_BADCONDVAR;
|
||||
goto unlock_and_exit;
|
||||
}
|
||||
num = tcp_connection_send(socket->connection, src, len);
|
||||
}
|
||||
#else
|
||||
if (num == 0 && !no_block)
|
||||
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
|
||||
if (num == 0) {
|
||||
if (no_block)
|
||||
errcode2 = MICROTCP_ERRCODE_WOULDBLOCK;
|
||||
else
|
||||
errcode2 = MICROTCP_ERRCODE_CANTBLOCK;
|
||||
goto unlock_and_exit;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
unlock_and_exit:
|
||||
UNLOCK_WHEN_THREADED(mtcp);
|
||||
|
||||
if (errcode)
|
||||
*errcode = errcode2;
|
||||
return num;
|
||||
}
|
||||
|
||||
#ifdef MICROTCP_USING_MUX
|
||||
microtcp_mux_t *microtcp_mux_create(microtcp_t *mtcp)
|
||||
{
|
||||
microtcp_mux_t *mux = malloc(sizeof(microtcp_mux_t));
|
||||
if (!mux)
|
||||
return NULL;
|
||||
|
||||
mux->mtcp = mtcp;
|
||||
|
||||
// Build the free list
|
||||
static_assert(MICROTCP_MAX_MUX_ENTRIES > 1);
|
||||
const int max = MICROTCP_MAX_MUX_ENTRIES;
|
||||
for (int i = 1; i < max-1; i++) {
|
||||
mux->entries[i].mux = mux; // This will be never changed
|
||||
mux->entries[i].mux_prev = &mux->entries[i-1].mux_next;
|
||||
mux->entries[i].mux_next = &mux->entries[i+1];
|
||||
}
|
||||
mux->entries[0].mux = mux; // Never changed
|
||||
mux->entries[0].mux_prev = &mux->free_list;
|
||||
mux->entries[0].mux_next = &mux->entries[1];
|
||||
mux->entries[max-1].mux = mux; // Never changed
|
||||
mux->entries[max-1].mux_prev = &mux->entries[max-2].mux_next;
|
||||
mux->entries[max-1].mux_next = NULL;
|
||||
|
||||
mux->idle_list = NULL;
|
||||
mux->free_list = mux->entries;
|
||||
mux->ready_queue_head = NULL;
|
||||
mux->ready_queue_tail = NULL;
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
if (cnd_init(&mux->queue_not_empty) != thrd_success) {
|
||||
free(mux);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
return mux;
|
||||
}
|
||||
|
||||
void microtcp_mux_destroy(microtcp_mux_t *mux)
|
||||
{
|
||||
// Unregister all idle sockets
|
||||
// Idle entries don't have pending events
|
||||
// to deliver so by unregistering them the
|
||||
// entry is unlinked.
|
||||
while (mux->idle_list)
|
||||
microtcp_mux_unregister(mux, mux->idle_list->sock, ~0);
|
||||
|
||||
// Consume all previously reported events
|
||||
// to make sure that when unregistering
|
||||
// the entries are actually removed
|
||||
while (microtcp_mux_poll(mux, NULL));
|
||||
|
||||
// Unreagister all sockets that have events
|
||||
while (mux->ready_queue_head) {
|
||||
mux_entry_t *entry = mux->ready_queue_head;
|
||||
microtcp_mux_unregister(mux, entry->sock, ~0);
|
||||
|
||||
// Since all events were consumed beforehand
|
||||
// we're sure the entry was removed.
|
||||
assert(entry != mux->ready_queue_head);
|
||||
}
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
cnd_destroy(&mux->queue_not_empty);
|
||||
#endif
|
||||
|
||||
free(mux);
|
||||
}
|
||||
|
||||
static mux_entry_t*
|
||||
find_socket_and_mux_entry(microtcp_mux_t *mux, microtcp_socket_t *sock)
|
||||
{
|
||||
mux_entry_t *entry = sock->mux_list;
|
||||
while (entry) {
|
||||
if (entry->mux == mux)
|
||||
break;
|
||||
entry = entry->sock_next;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void
|
||||
move_mux_entry_to_free_list(mux_entry_t *entry)
|
||||
{
|
||||
microtcp_mux_t *mux = entry->mux;
|
||||
|
||||
// If the entry is in a list, unlink it
|
||||
if (entry->mux_prev)
|
||||
*entry->mux_prev = entry->mux_next;
|
||||
if (entry->sock_prev)
|
||||
*entry->sock_prev = entry->sock_next;
|
||||
|
||||
// Put the structure into the free list
|
||||
entry->mux_prev = &mux->free_list;
|
||||
entry->mux_next = mux->free_list;
|
||||
if (mux->free_list)
|
||||
mux->free_list->mux_prev = &entry->mux_next;
|
||||
mux->free_list = entry;
|
||||
}
|
||||
|
||||
static void
|
||||
move_mux_entry_to_idle_list(mux_entry_t *entry)
|
||||
{
|
||||
// To be moved to the idle list the entry
|
||||
// must be associated to a socket so it
|
||||
// must be in a socket mux list, therefore
|
||||
// it must be true that
|
||||
assert(entry->sock_prev); // not null iff the entry is in a mux list
|
||||
|
||||
// Make sure the entry is unlinked relative
|
||||
// to the lists in the mux
|
||||
if (entry->mux_prev)
|
||||
*entry->mux_prev = entry->mux_next;
|
||||
|
||||
// Now actually insert it into the idle list
|
||||
microtcp_mux_t *mux = entry->mux;
|
||||
entry->mux_prev = &mux->idle_list;
|
||||
entry->mux_next = mux->idle_list;
|
||||
if (mux->idle_list)
|
||||
mux->idle_list->mux_prev = &entry->mux_next;
|
||||
mux->idle_list = entry;
|
||||
}
|
||||
|
||||
bool microtcp_mux_unregister(microtcp_mux_t *mux, microtcp_socket_t *sock, int events)
|
||||
{
|
||||
LOCK_WHEN_THREADED(mux->mtcp);
|
||||
|
||||
// There's no need to check that mux
|
||||
// and socket have the same mtcp because
|
||||
// if it's different it will result that
|
||||
// the socket isn't registered into the
|
||||
// mux.
|
||||
|
||||
mux_entry_t *entry = find_socket_and_mux_entry(mux, sock);
|
||||
if (!entry) {
|
||||
// This socket wasn't registered into the mux
|
||||
UNLOCK_WHEN_THREADED(mux->mtcp);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unset the events of interest
|
||||
entry->events_of_interest &= ~events;
|
||||
|
||||
if (entry->triggered_events) {
|
||||
// NOTE: Since we modified "events_of_interest"
|
||||
// but not "triggered_events", any previously
|
||||
// triggered events that were now unregistered
|
||||
// will still be delivered to the user.
|
||||
//
|
||||
// Though when events are delivered, if all
|
||||
// events registered were all unregistered,
|
||||
// the socket is removed from the mux.
|
||||
} else
|
||||
// No events were previously reported so we can
|
||||
// move the entry to the free list.
|
||||
move_mux_entry_to_free_list(entry);
|
||||
|
||||
UNLOCK_WHEN_THREADED(mux->mtcp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool microtcp_mux_register(microtcp_mux_t *mux, microtcp_socket_t *sock, int events, void *userp)
|
||||
{
|
||||
LOCK_WHEN_THREADED(mux->mtcp);
|
||||
|
||||
if (mux->mtcp != sock->mtcp) {
|
||||
UNLOCK_WHEN_THREADED(mux->mtcp);
|
||||
return false; // mux and socket are associated to different microtcp stacks
|
||||
}
|
||||
|
||||
if (events == 0) {
|
||||
UNLOCK_WHEN_THREADED(mux->mtcp);
|
||||
return true; // Nothing to be done
|
||||
}
|
||||
|
||||
mux_entry_t *entry = find_socket_and_mux_entry(mux, sock);
|
||||
if (!entry) {
|
||||
// This is the first time that the socket is registered.
|
||||
// Create an entry for it
|
||||
if (mux->free_list == NULL) {
|
||||
// The entry limit was reached.
|
||||
// It's impossible to register the socket at this time
|
||||
UNLOCK_WHEN_THREADED(mux->mtcp);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pop from the free list
|
||||
entry = mux->free_list;
|
||||
*entry->mux_prev = entry->mux_next;
|
||||
|
||||
// Push it into the idle list of the mux
|
||||
entry->mux_prev = &mux->idle_list;
|
||||
entry->mux_next = mux->idle_list;
|
||||
if (mux->idle_list)
|
||||
mux->idle_list->mux_prev = &entry->mux_next;
|
||||
|
||||
// Push it into the socket mux list
|
||||
entry->sock_prev = &sock->mux_list;
|
||||
entry->sock_next = sock->mux_list;
|
||||
if (sock->mux_list)
|
||||
sock->mux_list->sock_prev = &entry->sock_next;
|
||||
|
||||
// Initialize the entry
|
||||
entry->sock = sock;
|
||||
entry->userp = userp;
|
||||
entry->triggered_events = 0;
|
||||
entry->events_of_interest = 0;
|
||||
// entry->mux = mux; This isn't necessary because the mux field
|
||||
// is initialized once with the mux and never
|
||||
// changed.
|
||||
}
|
||||
|
||||
entry->events_of_interest |= events;
|
||||
|
||||
UNLOCK_WHEN_THREADED(mux->mtcp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool microtcp_mux_poll(microtcp_mux_t *mux, microtcp_muxevent_t *ev)
|
||||
{
|
||||
LOCK_WHEN_THREADED(mux->mtcp);
|
||||
|
||||
// Get the tail of the queue (without popping it)
|
||||
mux_entry_t *entry = mux->ready_queue_tail;
|
||||
|
||||
if (!entry) {
|
||||
UNLOCK_WHEN_THREADED(mux->mtcp);
|
||||
return false; // No events occurred
|
||||
}
|
||||
|
||||
// If this socket was in the ready queue
|
||||
// it must have triggered events
|
||||
assert(entry->triggered_events);
|
||||
|
||||
if (ev) {
|
||||
ev->userp = entry->userp;
|
||||
ev->events = entry->triggered_events;
|
||||
ev->socket = entry->sock;
|
||||
}
|
||||
|
||||
// Unmark events as triggered
|
||||
entry->triggered_events = 0;
|
||||
|
||||
if (entry->events_of_interest == 0)
|
||||
// All events were unregistered.
|
||||
// We can remove the socket from the mux.
|
||||
move_mux_entry_to_free_list(entry);
|
||||
else
|
||||
// The socket wasn't unregistered or
|
||||
// wasn't unregistered completely so
|
||||
// we put the entry into the idle list
|
||||
move_mux_entry_to_idle_list(entry);
|
||||
|
||||
UNLOCK_WHEN_THREADED(mux->mtcp);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool microtcp_mux_wait(microtcp_mux_t *mux, microtcp_muxevent_t *ev)
|
||||
{
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
LOCK_WHEN_THREADED(mux->mtcp);
|
||||
while (!microtcp_mux_poll(mux, ev))
|
||||
if (cnd_wait(&mux->queue_not_empty, &mux->mtcp->lock) != thrd_success)
|
||||
abort();
|
||||
UNLOCK_WHEN_THREADED(mux->mtcp);
|
||||
return true;
|
||||
#else
|
||||
return microtcp_mux_poll(mux, ev);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
signal_events_to_muxes_associated_to_socket(microtcp_socket_t *socket, int events)
|
||||
{
|
||||
// (This function is called by the socket and not the mux)
|
||||
|
||||
assert(events); // If no events need to be signaled then
|
||||
// this function has no reason to be called.
|
||||
|
||||
mux_entry_t *entry = socket->mux_list;
|
||||
while (entry) {
|
||||
|
||||
microtcp_mux_t *mux = entry->mux;
|
||||
|
||||
bool first_event_of_socket = (entry->triggered_events == 0);
|
||||
entry->triggered_events |= events & entry->events_of_interest;
|
||||
|
||||
if (first_event_of_socket) {
|
||||
|
||||
bool queue_was_empty = (mux->ready_queue_head == NULL);
|
||||
|
||||
// No entries were already triggered so
|
||||
// it's necessary to move the entry from
|
||||
// the mux's idle list to ready queue
|
||||
|
||||
// Unlink it from the idle queue
|
||||
*entry->mux_prev = entry->mux_next;
|
||||
if (entry->mux_next)
|
||||
entry->mux_next->mux_prev = entry->mux_prev;
|
||||
|
||||
// Add it to the queue
|
||||
entry->mux_prev = &mux->ready_queue_head;
|
||||
entry->mux_next = mux->ready_queue_head;
|
||||
if (mux->ready_queue_head)
|
||||
mux->ready_queue_head->mux_prev = &entry->mux_next;
|
||||
else
|
||||
mux->ready_queue_tail = entry;
|
||||
mux->ready_queue_head = entry;
|
||||
|
||||
#ifdef MICROTCP_BACKGROUND_THREAD
|
||||
if (queue_was_empty)
|
||||
cnd_signal(&mux->queue_not_empty);
|
||||
#else
|
||||
(void) queue_was_empty;
|
||||
#endif
|
||||
}
|
||||
entry = entry->sock_next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,14 +1,17 @@
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdalign.h>
|
||||
#include "endian.h"
|
||||
#include "tcp.h"
|
||||
|
||||
#ifndef MICROTCP_AMALGAMATION
|
||||
# include "endian.h"
|
||||
# include "tcp.h"
|
||||
#endif
|
||||
|
||||
#ifdef TCP_DEBUG
|
||||
#include <stdio.h>
|
||||
#define TCP_DEBUG_LOG(fmt, ...) fprintf(stderr, "TCP :: " fmt "\n", ## __VA_ARGS__)
|
||||
# include <stdio.h>
|
||||
# define TCP_DEBUG_LOG(fmt, ...) fprintf(stderr, "TCP :: " fmt "\n", ## __VA_ARGS__)
|
||||
#else
|
||||
#define TCP_DEBUG_LOG(...)
|
||||
# define TCP_DEBUG_LOG(...)
|
||||
#endif
|
||||
|
||||
#define SEGMENT_OFFSET(seg) (cpu_is_little_endian() ? (seg)->offset2 : (seg)->offset1)
|
||||
@@ -532,6 +535,8 @@ tcp_listener_create(tcp_state_t *state, uint16_t port, void *callback_data,
|
||||
|
||||
void tcp_listener_destroy(tcp_listener_t *listener)
|
||||
{
|
||||
#warning "The previously accepted connections should't be closed with their listener.. I think"
|
||||
|
||||
// TODO: Close all connections
|
||||
|
||||
tcp_state_t *state = listener->state;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef MICROTCP_AMALGAMATION
|
||||
#include "defs.h"
|
||||
#endif
|
||||
|
||||
#define TCP_MAX_LISTENERS 32
|
||||
#define TCP_MAX_SOCKETS 1024
|
||||
@@ -43,7 +46,6 @@ struct tcp_listener_t {
|
||||
tcp_connection_t *non_established_list;
|
||||
tcp_connection_t *non_accepted_queue_head;
|
||||
tcp_connection_t *non_accepted_queue_tail;
|
||||
|
||||
void (*callback_ready_to_accept)(void*);
|
||||
void *callback_data;
|
||||
};
|
||||
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
#include <stdio.h>
|
||||
#include <microtcp.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
microtcp_errcode_t errcode;
|
||||
|
||||
microtcp_t *mtcp = microtcp_create();
|
||||
if (mtcp == NULL)
|
||||
return -1;
|
||||
|
||||
uint16_t port = 80;
|
||||
microtcp_socket_t *server = microtcp_open(mtcp, port, &errcode);
|
||||
if (errcode) {
|
||||
fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode));
|
||||
microtcp_destroy(mtcp);
|
||||
return -1;
|
||||
}
|
||||
assert(server);
|
||||
|
||||
fprintf(stderr, "Listening on port %d\n", port);
|
||||
|
||||
while (1) {
|
||||
fprintf(stderr, "About to accept\n");
|
||||
microtcp_socket_t *client = microtcp_accept(server, false, &errcode);
|
||||
if (errcode && errcode != MICROTCP_ERRCODE_NOTHINGTOACCEPT) {
|
||||
fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode));
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Accepted a connection\n");
|
||||
|
||||
char buffer[1024];
|
||||
size_t num = microtcp_recv(client, buffer, sizeof(buffer), &errcode);
|
||||
if (errcode) {
|
||||
fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode));
|
||||
goto handled;
|
||||
}
|
||||
microtcp_send(client, "echo: ", 6, &errcode);
|
||||
if (errcode) {
|
||||
fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode));
|
||||
goto handled;
|
||||
}
|
||||
microtcp_send(client, buffer, num, &errcode);
|
||||
if (errcode) {
|
||||
fprintf(stderr, "Error: %s\n", microtcp_strerror(errcode));
|
||||
goto handled;
|
||||
}
|
||||
handled:
|
||||
microtcp_close(client);
|
||||
}
|
||||
|
||||
microtcp_close(server);
|
||||
microtcp_destroy(mtcp);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
Reference in New Issue
Block a user