Alex 'AdUser' Z
4 years ago
26 changed files with 827 additions and 1248 deletions
@ -1,217 +0,0 @@ |
|||||||
/* Copyright 2016 Alex 'AdUser' Z (ad_user@runbox.com)
|
|
||||||
* |
|
||||||
* This program is free software; you can redistribute it and/or modify |
|
||||||
* it under the terms of the GNU General Public License version 2 as |
|
||||||
* published by the Free Software Foundation. |
|
||||||
*/ |
|
||||||
#include <assert.h> |
|
||||||
#include <errno.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <string.h> |
|
||||||
#include <stdbool.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <sys/types.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <netinet/in.h> |
|
||||||
#include <arpa/inet.h> |
|
||||||
#include <net/if.h> |
|
||||||
#include <netdb.h> |
|
||||||
|
|
||||||
#include "../strlcpy.h" |
|
||||||
#include "../commands.h" |
|
||||||
#include "../cmsg.h" |
|
||||||
#include "../csocket.h" |
|
||||||
|
|
||||||
#include "backend.h" |
|
||||||
#include "shared.c" |
|
||||||
|
|
||||||
#define DEFAULT_MCAST_ADDR "239.255.186.1" |
|
||||||
#define DEFAULT_MCAST_PORT "3370" |
|
||||||
|
|
||||||
struct _config { |
|
||||||
char name[ID_MAX + 1]; |
|
||||||
char error[256]; |
|
||||||
bool shared; |
|
||||||
char maddr[INET_ADDRSTRLEN]; /**< multicast address */ |
|
||||||
char mport[6]; /**< multicast port */ |
|
||||||
char iface[IF_NAMESIZE]; /**< bind interface */ |
|
||||||
int sock; |
|
||||||
struct sockaddr_storage sa; |
|
||||||
socklen_t sa_len; |
|
||||||
}; |
|
||||||
|
|
||||||
cfg_t * |
|
||||||
create(const char *id) { |
|
||||||
cfg_t *cfg = NULL; |
|
||||||
|
|
||||||
assert(id != NULL); |
|
||||||
|
|
||||||
if ((cfg = calloc(1, sizeof(cfg_t))) == NULL) |
|
||||||
return NULL; |
|
||||||
strlcpy(cfg->name, id, sizeof(cfg->name)); |
|
||||||
strlcpy(cfg->maddr, DEFAULT_MCAST_ADDR, sizeof(cfg->maddr)); |
|
||||||
strlcpy(cfg->mport, DEFAULT_MCAST_PORT, sizeof(cfg->mport)); |
|
||||||
|
|
||||||
return cfg; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
config(cfg_t *cfg, const char *key, const char *value) { |
|
||||||
assert(cfg != NULL); |
|
||||||
assert(key != NULL); |
|
||||||
assert(value != NULL); |
|
||||||
|
|
||||||
if (strcmp(key, "group") == 0) { |
|
||||||
if (strncmp(value, "239.255.", 8) != 0) { |
|
||||||
strlcpy(cfg->error, "mcast group address should be inside 239.255.0.0/16 block", sizeof(cfg->error)); |
|
||||||
return false; |
|
||||||
} |
|
||||||
strlcpy(cfg->maddr, value, sizeof(cfg->maddr)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
if (strcmp(key, "port") == 0) { |
|
||||||
strlcpy(cfg->mport, value, sizeof(cfg->mport)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
if (strcmp(key, "iface") == 0) { |
|
||||||
strlcpy(cfg->iface, value, sizeof(cfg->iface)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
ready(cfg_t *cfg) { |
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
if (cfg->maddr[0] && cfg->mport[0]) |
|
||||||
return true; |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
char * |
|
||||||
error(cfg_t *cfg) { |
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
return cfg->error; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
start(cfg_t *cfg) { |
|
||||||
struct addrinfo hints; |
|
||||||
struct addrinfo *result; |
|
||||||
assert(cfg != NULL); |
|
||||||
int ret, sock = -1; |
|
||||||
|
|
||||||
if (cfg->shared && usage_inc(cfg->name) > 1) |
|
||||||
return true; |
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(struct addrinfo)); |
|
||||||
hints.ai_family = AF_INET; |
|
||||||
hints.ai_socktype = SOCK_DGRAM; |
|
||||||
hints.ai_protocol = 0; |
|
||||||
|
|
||||||
if ((ret = getaddrinfo(cfg->maddr, cfg->mport, &hints, &result)) < 0) { |
|
||||||
snprintf(cfg->error, sizeof(cfg->error), "can't create socket: %s", gai_strerror(ret)); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next) { |
|
||||||
if (sock >= 0) { |
|
||||||
close(sock); /* from prev iteration */ |
|
||||||
sock = -1; |
|
||||||
} |
|
||||||
if ((sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0) { |
|
||||||
snprintf(cfg->error, sizeof(cfg->error), "can't create socket: %s", strerror(errno)); |
|
||||||
continue; |
|
||||||
} |
|
||||||
memcpy(&cfg->sa, rp->ai_addr, rp->ai_addrlen); |
|
||||||
cfg->sa_len = rp->ai_addrlen; |
|
||||||
break; |
|
||||||
} |
|
||||||
freeaddrinfo(result); |
|
||||||
|
|
||||||
if (sock < 0) |
|
||||||
return false; |
|
||||||
|
|
||||||
cfg->sock = sock; |
|
||||||
return true; |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
stop(cfg_t *cfg) { |
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
if (cfg->shared && usage_dec(cfg->name) > 0) |
|
||||||
return true; |
|
||||||
|
|
||||||
close(cfg->sock); |
|
||||||
cfg->sock = -1; |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
ban(cfg_t *cfg, const char *ip) { |
|
||||||
f2b_cmsg_t cmsg; |
|
||||||
int ret; |
|
||||||
|
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
memset(&cmsg, 0x0, sizeof(cmsg)); |
|
||||||
|
|
||||||
strncpy(cmsg.magic, "F2B", sizeof(cmsg.magic)); |
|
||||||
cmsg.version = F2B_PROTO_VER; |
|
||||||
cmsg.type = CMD_JAIL_IP_BAN; |
|
||||||
f2b_cmd_append_arg(cmsg.data, sizeof(cmsg.data), cfg->name); |
|
||||||
f2b_cmd_append_arg(cmsg.data, sizeof(cmsg.data), ip); |
|
||||||
cmsg.size = strlen(cmsg.data); |
|
||||||
cmsg.data[cmsg.size] = '\0'; |
|
||||||
f2b_cmsg_convert_args(&cmsg); |
|
||||||
|
|
||||||
ret = f2b_csocket_send(cfg->sock, &cmsg, &cfg->sa, &cfg->sa_len); |
|
||||||
if (ret <= 0) { |
|
||||||
strlcpy(cfg->error, f2b_csocket_error(ret), sizeof(cfg->error)); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
unban(cfg_t *cfg, const char *ip) { |
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
(void)(cfg); /* suppress warning for unused variable 'cfg' */ |
|
||||||
(void)(ip); /* suppress warning for unused variable 'ip' */ |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
check(cfg_t *cfg, const char *ip) { |
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
(void)(cfg); /* suppress warning for unused variable 'cfg' */ |
|
||||||
(void)(ip); /* suppress warning for unused variable 'ip' */ |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
ping(cfg_t *cfg) { |
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
(void)(cfg); /* silence warning about unused variable */ |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
destroy(cfg_t *cfg) { |
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
free(cfg); |
|
||||||
} |
|
@ -0,0 +1,88 @@ |
|||||||
|
#include "common.h" |
||||||
|
#include "buf.h" |
||||||
|
|
||||||
|
bool |
||||||
|
f2b_buf_alloc(f2b_buf_t *buf, size_t size) { |
||||||
|
assert(buf != NULL); |
||||||
|
assert(size > 0); |
||||||
|
|
||||||
|
memset(buf, 0x0, sizeof(f2b_buf_t)); |
||||||
|
if ((buf->data = malloc(size)) == NULL) |
||||||
|
return false; /* can't allocate memory */ |
||||||
|
|
||||||
|
buf->size = size; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void |
||||||
|
f2b_buf_free(f2b_buf_t *buf) { |
||||||
|
assert(buf != NULL); |
||||||
|
|
||||||
|
free(buf->data); |
||||||
|
memset(buf, 0x0, sizeof(f2b_buf_t)); |
||||||
|
} |
||||||
|
|
||||||
|
size_t |
||||||
|
f2b_buf_append(f2b_buf_t *buf, const char *str, size_t len) { |
||||||
|
assert(buf != NULL); |
||||||
|
assert(str != NULL); |
||||||
|
|
||||||
|
if (len == 0) |
||||||
|
len = strlen(str); |
||||||
|
if ((buf->used + len) > buf->size) { |
||||||
|
/* not enough space, append as much as possible */ |
||||||
|
len = buf->size - buf->used; |
||||||
|
} |
||||||
|
|
||||||
|
memcpy(&buf->data[buf->used], str, len); |
||||||
|
buf->used += len; |
||||||
|
buf->data[buf->used] = '\0'; |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extracts line terminated by delimiter |
||||||
|
* @return Pointer to extracted string on success or NULL otherwise |
||||||
|
* @note Use only with 'read' buffer type |
||||||
|
*/ |
||||||
|
char * |
||||||
|
f2b_buf_extract(f2b_buf_t *buf, const char *end) { |
||||||
|
char *s = NULL; |
||||||
|
size_t len = 0; |
||||||
|
|
||||||
|
assert(buf != NULL); |
||||||
|
assert(end != NULL); |
||||||
|
|
||||||
|
if (buf->data == NULL || buf->used == 0) |
||||||
|
return NULL; /* no data */ |
||||||
|
|
||||||
|
if ((s = strstr(buf->data, end)) == NULL) |
||||||
|
return NULL; /* not found */ |
||||||
|
|
||||||
|
/* copy the data before modifying buffer */ |
||||||
|
len = s - buf->data; |
||||||
|
if ((s = strndup(buf->data, len)) == NULL) |
||||||
|
return NULL; /* malloc error */ |
||||||
|
|
||||||
|
/* shift data inside buffer */ |
||||||
|
len += strlen(end); |
||||||
|
f2b_buf_splice(buf, len); |
||||||
|
|
||||||
|
return s; |
||||||
|
} |
||||||
|
|
||||||
|
size_t |
||||||
|
f2b_buf_splice(f2b_buf_t *buf, size_t len) { |
||||||
|
assert(buf != NULL); |
||||||
|
|
||||||
|
if (len == 0) |
||||||
|
return len; |
||||||
|
|
||||||
|
if (buf->used <= len) |
||||||
|
len = buf->used; |
||||||
|
|
||||||
|
buf->used -= len, |
||||||
|
memmove(buf->data, &buf->data[len], buf->used); |
||||||
|
buf->data[buf->used] = '\0'; |
||||||
|
return len; |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
#ifndef F2B_BUF_H_ |
||||||
|
#define F2B_BUF_H_ |
||||||
|
|
||||||
|
typedef struct f2b_buf_t { |
||||||
|
size_t used; /**< bytes used in buffer */ |
||||||
|
size_t size; /**< available size in data */ |
||||||
|
char *data; /**< allocated buffer */ |
||||||
|
} f2b_buf_t; |
||||||
|
|
||||||
|
bool f2b_buf_alloc(f2b_buf_t *buf, size_t max); |
||||||
|
void f2b_buf_free(f2b_buf_t *buf); |
||||||
|
size_t f2b_buf_append(f2b_buf_t *buf, const char *str, size_t size); |
||||||
|
char * f2b_buf_extract(f2b_buf_t *buf, const char *end); |
||||||
|
size_t f2b_buf_splice(f2b_buf_t *buf, size_t len); |
||||||
|
|
||||||
|
#endif /* F2B_BUF_H_ */ |
@ -1,42 +0,0 @@ |
|||||||
#include "common.h" |
|
||||||
#include "commands.h" |
|
||||||
#include "cmsg.h" |
|
||||||
|
|
||||||
#include <sys/uio.h> |
|
||||||
|
|
||||||
void |
|
||||||
f2b_cmsg_convert_args(f2b_cmsg_t *msg) { |
|
||||||
assert(msg != NULL); |
|
||||||
|
|
||||||
for (size_t i = 0; i < msg->size && i < sizeof(msg->data); i++) { |
|
||||||
if (msg->data[i] == '\n') |
|
||||||
msg->data[i] = '\0'; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int |
|
||||||
f2b_cmsg_extract_args(const f2b_cmsg_t *msg, const char **argv) { |
|
||||||
char prev = '\0'; |
|
||||||
size_t argc = 0; |
|
||||||
|
|
||||||
assert(msg != NULL); |
|
||||||
assert(argv != NULL); |
|
||||||
|
|
||||||
if (msg->size == 0) |
|
||||||
return 0; /* no args */ |
|
||||||
|
|
||||||
if (msg->data[msg->size - 1] != '\0') |
|
||||||
return -1; /* message data not null-terminated */ |
|
||||||
|
|
||||||
for (size_t i = 0; i < msg->size; i++) { |
|
||||||
if (prev == '\0' && msg->data[i] != '\0') |
|
||||||
argv[argc] = &msg->data[i], argc++; |
|
||||||
if (argc >= DATA_ARGS_MAX) { |
|
||||||
argc = -1; |
|
||||||
break; |
|
||||||
} |
|
||||||
prev = msg->data[i]; |
|
||||||
} |
|
||||||
|
|
||||||
return argc; |
|
||||||
} |
|
@ -1,68 +0,0 @@ |
|||||||
#ifndef F2B_CMSG_H_ |
|
||||||
#define F2B_CMSG_H_ |
|
||||||
|
|
||||||
/**
|
|
||||||
* @file |
|
||||||
* This header contains definitions of control messages format and routines |
|
||||||
*/ |
|
||||||
|
|
||||||
/**
|
|
||||||
* @def DATA_LEN_MAX |
|
||||||
* Maximum length of data in packet |
|
||||||
*/ |
|
||||||
#define DATA_LEN_MAX 1476 /* 1500 - (16 bytes of cmsg header + 8 bytes of udp) */ |
|
||||||
/**
|
|
||||||
* @def DATA_ARGS_MAX |
|
||||||
* Maximum count of data pieces in packet |
|
||||||
*/ |
|
||||||
#define DATA_ARGS_MAX 6 /* number of args in data */ |
|
||||||
/**
|
|
||||||
* @def F2B_PROTO_VER |
|
||||||
* Protocol version of control message |
|
||||||
*/ |
|
||||||
#define F2B_PROTO_VER 1 |
|
||||||
|
|
||||||
/**
|
|
||||||
* @def CMSG_FLAG_NEED_REPLY |
|
||||||
* Server should reply to this control message |
|
||||||
*/ |
|
||||||
#define CMSG_FLAG_NEED_REPLY 0x01 |
|
||||||
/**
|
|
||||||
* @def CMSG_FLAG_AUTH_PASS |
|
||||||
* This control message contains password |
|
||||||
*/ |
|
||||||
#define CMSG_FLAG_AUTH_PASS 0x02 |
|
||||||
|
|
||||||
/**
|
|
||||||
* f2b control message |
|
||||||
* @note Use sendmsg/recvmsg and iovec structs to pack/unpack |
|
||||||
*/ |
|
||||||
typedef struct f2b_cmsg_t { |
|
||||||
char magic[3]; /**< magic string "F2B" */ |
|
||||||
uint8_t version; /**< protocol version */ |
|
||||||
/* 4 bytes */ |
|
||||||
uint8_t type; /**< command type, cast from enum f2b_cmd_type */ |
|
||||||
uint8_t flags; /**< CMSG_FLAG_* */ |
|
||||||
uint16_t size; /**< payload length */ |
|
||||||
/* 8 bytes */ |
|
||||||
char pass[8]; /**< auth data */ |
|
||||||
/* 16 bytes */ |
|
||||||
/* end of header */ |
|
||||||
char data[DATA_LEN_MAX]; /**< set of "\n"-terminated strings */ |
|
||||||
/* end of data */ |
|
||||||
} f2b_cmsg_t; |
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Convert every '\n' in data to '\0' |
|
||||||
* @param msg Pointer to control message |
|
||||||
*/ |
|
||||||
void f2b_cmsg_convert_args(f2b_cmsg_t *msg); |
|
||||||
/**
|
|
||||||
* @brief Fill @a argv array with pointers to found data pieces |
|
||||||
* @param msg Pointer to control message |
|
||||||
* @param argv Array of pointers |
|
||||||
* @returns Number of found args |
|
||||||
*/ |
|
||||||
int f2b_cmsg_extract_args(const f2b_cmsg_t *msg, const char **argv); |
|
||||||
|
|
||||||
#endif /* F2B_CMSG_H_ */ |
|
@ -0,0 +1,42 @@ |
|||||||
|
#include "common.h" |
||||||
|
#include "buf.h" |
||||||
|
#include "log.h" |
||||||
|
#include "commands.h" |
||||||
|
#include "csocket.h" |
||||||
|
|
||||||
|
static int run = 1; |
||||||
|
|
||||||
|
void |
||||||
|
cmd_handler(const f2b_cmd_t *cmd, f2b_buf_t *res) { |
||||||
|
fprintf(stdout, "[handler] received cmd with type %d and %d args:\n", cmd->type, cmd->argc); |
||||||
|
for (int i = 0; i < cmd->argc; i++) { |
||||||
|
fprintf(stdout, "[handler] arg %d : %s\n", i + 1, cmd->args[i]); |
||||||
|
} |
||||||
|
UNUSED(res); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, const char **argv) { |
||||||
|
f2b_csock_t *csock = NULL; |
||||||
|
|
||||||
|
if (argc < 2) { |
||||||
|
puts("Usage: csocket-test <path>"); |
||||||
|
exit(EXIT_FAILURE); |
||||||
|
} |
||||||
|
|
||||||
|
f2b_log_set_level("debug"); |
||||||
|
f2b_log_to_stderr(); |
||||||
|
|
||||||
|
if ((csock = f2b_csocket_create(argv[1])) == NULL) { |
||||||
|
perror("f2b_csocket_create()"); |
||||||
|
exit(EXIT_FAILURE); |
||||||
|
} |
||||||
|
|
||||||
|
while (run) { |
||||||
|
f2b_csocket_poll(csock, cmd_handler); |
||||||
|
sleep(1); |
||||||
|
} |
||||||
|
f2b_csocket_destroy(csock); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -1,271 +0,0 @@ |
|||||||
/* Copyright 2016 Alex 'AdUser' Z (ad_user@runbox.com)
|
|
||||||
* |
|
||||||
* This program is free software; you can redistribute it and/or modify |
|
||||||
* it under the terms of the GNU General Public License version 2 as |
|
||||||
* published by the Free Software Foundation. |
|
||||||
*/ |
|
||||||
#include "source.h" |
|
||||||
|
|
||||||
#include <sys/types.h> |
|
||||||
#include <fcntl.h> |
|
||||||
#include <sys/socket.h> |
|
||||||
#include <netinet/in.h> |
|
||||||
#include <arpa/inet.h> |
|
||||||
#include <net/if.h> |
|
||||||
#include <netdb.h> |
|
||||||
|
|
||||||
#include "../commands.h" |
|
||||||
#include "../cmsg.h" |
|
||||||
#include "../csocket.h" |
|
||||||
|
|
||||||
#define DEFAULT_BIND_ADDR "0.0.0.0" |
|
||||||
#define DEFAULT_MCAST_ADDR "239.255.186.1" |
|
||||||
#define DEFAULT_MCAST_PORT "3370" |
|
||||||
|
|
||||||
#if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP) |
|
||||||
/* bsd-derivatives */ |
|
||||||
#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP |
|
||||||
#endif |
|
||||||
|
|
||||||
struct _config { |
|
||||||
char name[32]; |
|
||||||
char error[256]; |
|
||||||
void (*errcb)(const char *errstr); |
|
||||||
char baddr[INET6_ADDRSTRLEN]; /**< bind address */ |
|
||||||
char maddr[INET6_ADDRSTRLEN]; /**< multicast address */ |
|
||||||
char mport[6]; /**< multicast port */ |
|
||||||
char iface[IF_NAMESIZE]; /**< bind interface */ |
|
||||||
int sock; |
|
||||||
}; |
|
||||||
|
|
||||||
static void |
|
||||||
errcb_stub(const char *str) { |
|
||||||
assert(str != NULL); |
|
||||||
(void)(str); |
|
||||||
} |
|
||||||
|
|
||||||
cfg_t * |
|
||||||
create(const char *init) { |
|
||||||
cfg_t *cfg = NULL; |
|
||||||
assert(init != NULL); |
|
||||||
if ((cfg = calloc(1, sizeof(cfg_t))) == NULL) |
|
||||||
return NULL; |
|
||||||
strlcpy(cfg->name, init, sizeof(cfg->name)); |
|
||||||
strlcpy(cfg->baddr, DEFAULT_BIND_ADDR, sizeof(cfg->baddr)); |
|
||||||
strlcpy(cfg->maddr, DEFAULT_MCAST_ADDR, sizeof(cfg->maddr)); |
|
||||||
strlcpy(cfg->mport, DEFAULT_MCAST_PORT, sizeof(cfg->mport)); |
|
||||||
cfg->errcb = &errcb_stub; |
|
||||||
return cfg; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
config(cfg_t *cfg, const char *key, const char *value) { |
|
||||||
assert(cfg != NULL); |
|
||||||
assert(key != NULL); |
|
||||||
assert(value != NULL); |
|
||||||
|
|
||||||
if (strcmp(key, "group") == 0) { |
|
||||||
if (strncmp(value, "239.255.", 8) != 0) { |
|
||||||
strlcpy(cfg->error, "mcast group address should be inside 239.255.0.0/16 block", sizeof(cfg->error)); |
|
||||||
return false; |
|
||||||
} |
|
||||||
strlcpy(cfg->maddr, value, sizeof(cfg->maddr)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
if (strcmp(key, "address") == 0) { |
|
||||||
strlcpy(cfg->baddr, value, sizeof(cfg->baddr)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
if (strcmp(key, "port") == 0) { |
|
||||||
strlcpy(cfg->mport, value, sizeof(cfg->mport)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
if (strcmp(key, "iface") == 0) { |
|
||||||
strlcpy(cfg->iface, value, sizeof(cfg->iface)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
ready(cfg_t *cfg) { |
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
if (cfg->maddr[0] && (cfg->iface[0] || cfg->baddr[0])) |
|
||||||
return true; |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
char * |
|
||||||
error(cfg_t *cfg) { |
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
return cfg->error; |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
errcb(cfg_t *cfg, void (*cb)(const char *errstr)) { |
|
||||||
assert(cfg != NULL); |
|
||||||
assert(cb != NULL); |
|
||||||
|
|
||||||
cfg->errcb = cb; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
start(cfg_t *cfg) { |
|
||||||
struct addrinfo hints; |
|
||||||
struct addrinfo *maddr, *laddr; |
|
||||||
int opt, ret, sock = -1; |
|
||||||
|
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(struct addrinfo)); |
|
||||||
hints.ai_family = AF_UNSPEC; |
|
||||||
hints.ai_flags = AI_NUMERICHOST; |
|
||||||
hints.ai_socktype = SOCK_DGRAM; |
|
||||||
hints.ai_protocol = 0; |
|
||||||
|
|
||||||
if ((ret = getaddrinfo(cfg->maddr, cfg->mport, &hints, &maddr)) < 0) { |
|
||||||
snprintf(cfg->error, sizeof(cfg->error), "can't resolve mcast addr: %s", gai_strerror(ret)); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
hints.ai_family = maddr->ai_family; |
|
||||||
hints.ai_flags = AI_PASSIVE; |
|
||||||
hints.ai_socktype = SOCK_DGRAM; |
|
||||||
|
|
||||||
if ((ret = getaddrinfo(cfg->baddr[0] != '\0' ? cfg->baddr : NULL, cfg->mport, &hints, &laddr)) < 0) { |
|
||||||
snprintf(cfg->error, sizeof(cfg->error), "can't resolve local addr: %s", gai_strerror(ret)); |
|
||||||
freeaddrinfo(maddr); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
do { |
|
||||||
cfg->sock = -1; |
|
||||||
/* create socket */ |
|
||||||
if ((sock = socket(laddr->ai_family, laddr->ai_socktype, laddr->ai_protocol)) < 0) { |
|
||||||
snprintf(cfg->error, sizeof(cfg->error), "can't create socket: %s", strerror(errno)); |
|
||||||
continue; |
|
||||||
} |
|
||||||
/* set non-blocking mode */ |
|
||||||
if ((opt = fcntl(sock, F_GETFL, 0)) < 0) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
fcntl(sock, F_SETFL, opt | O_NONBLOCK); |
|
||||||
/* reuse address */ |
|
||||||
opt = 1; |
|
||||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); |
|
||||||
/* bind to given address */ |
|
||||||
if (bind(sock, laddr->ai_addr, laddr->ai_addrlen) < 0) { |
|
||||||
snprintf(cfg->error, sizeof(cfg->error), "can't bind socket to addr %s: %s", |
|
||||||
cfg->baddr, strerror(errno)); |
|
||||||
continue; |
|
||||||
} |
|
||||||
/* IP_MULTICAST_LOOP -- default: yes */ |
|
||||||
/* IP_MULTICAST_TTL -- default: 1 */ |
|
||||||
/* join mcast group */ |
|
||||||
if (maddr->ai_family == AF_INET) { |
|
||||||
struct ip_mreq mreq; |
|
||||||
struct in_addr *addr = NULL; |
|
||||||
memset(&mreq, 0x0, sizeof(mreq)); |
|
||||||
addr = &((struct sockaddr_in *)(maddr->ai_addr))->sin_addr; |
|
||||||
memcpy(&mreq.imr_multiaddr, addr, sizeof(mreq.imr_multiaddr)); |
|
||||||
addr = &((struct sockaddr_in *)(laddr->ai_addr))->sin_addr; |
|
||||||
memcpy(&mreq.imr_interface, addr, sizeof(mreq.imr_interface)); |
|
||||||
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { |
|
||||||
snprintf(cfg->error, sizeof(cfg->error), "can't join mcast group: %s", |
|
||||||
strerror(errno)); |
|
||||||
continue; |
|
||||||
} |
|
||||||
} else if (maddr->ai_family == AF_INET6) { |
|
||||||
struct ipv6_mreq mreq; |
|
||||||
struct in6_addr *addr = NULL; |
|
||||||
memset(&mreq, 0x0, sizeof(mreq)); |
|
||||||
addr = &((struct sockaddr_in6 *)(maddr->ai_addr))->sin6_addr; |
|
||||||
memcpy(&mreq.ipv6mr_multiaddr, addr, sizeof(mreq.ipv6mr_multiaddr)); |
|
||||||
if (cfg->iface[0] != '\0') { |
|
||||||
mreq.ipv6mr_interface = if_nametoindex(cfg->iface); |
|
||||||
} |
|
||||||
if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { |
|
||||||
snprintf(cfg->error, sizeof(cfg->error), "can't join mcast group: %s", |
|
||||||
strerror(errno)); |
|
||||||
continue; |
|
||||||
} |
|
||||||
} else { |
|
||||||
snprintf(cfg->error, sizeof(cfg->error), "unknown family for mcast addr: %d", laddr->ai_family); |
|
||||||
continue; |
|
||||||
} |
|
||||||
/* all ok */ |
|
||||||
cfg->sock = sock; |
|
||||||
sock = -1; |
|
||||||
} while (0); |
|
||||||
|
|
||||||
freeaddrinfo(maddr); |
|
||||||
freeaddrinfo(laddr); |
|
||||||
|
|
||||||
if (cfg->sock < 0) |
|
||||||
return false; |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
stop(cfg_t *cfg) { |
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
/* mcast group will be leaved automatically */ |
|
||||||
close(cfg->sock); |
|
||||||
cfg->sock = -1; |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
next(cfg_t *cfg, char *buf, size_t bufsize, bool reset) { |
|
||||||
const char *args[DATA_ARGS_MAX]; |
|
||||||
struct sockaddr_storage addr; |
|
||||||
socklen_t socklen; |
|
||||||
f2b_cmsg_t cmsg; |
|
||||||
int ret; |
|
||||||
|
|
||||||
(void)(reset); |
|
||||||
assert(cfg != NULL); |
|
||||||
assert(buf != NULL); |
|
||||||
assert(bufsize > 0); |
|
||||||
|
|
||||||
memset(&cmsg, 0x0, sizeof(cmsg)); |
|
||||||
memset(&addr, 0x0, sizeof(addr)); |
|
||||||
|
|
||||||
while (1) { |
|
||||||
ret = f2b_csocket_recv(cfg->sock, &cmsg, &addr, &socklen); |
|
||||||
if (ret == 0) |
|
||||||
break; /* no messages */ |
|
||||||
if (ret < 0) { |
|
||||||
cfg->errcb(f2b_csocket_error(ret)); |
|
||||||
continue; |
|
||||||
} |
|
||||||
/* ret > 0 */ |
|
||||||
if (cmsg.type != CMD_JAIL_IP_BAN) |
|
||||||
continue; |
|
||||||
ret = f2b_cmsg_extract_args(&cmsg, args); |
|
||||||
if (!f2b_cmd_check_argc(cmsg.type, ret)) { |
|
||||||
cfg->errcb("received cmsg with wrong args count"); |
|
||||||
continue; |
|
||||||
} |
|
||||||
if (strcmp(cfg->name, args[0]) != 0) |
|
||||||
continue; /* wrong group */ |
|
||||||
strlcpy(buf, args[1], bufsize); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
destroy(cfg_t *cfg) { |
|
||||||
assert(cfg != NULL); |
|
||||||
|
|
||||||
free(cfg); |
|
||||||
} |
|
@ -0,0 +1,68 @@ |
|||||||
|
#include "../src/common.h" |
||||||
|
#include "../src/buf.h" |
||||||
|
|
||||||
|
int main() { |
||||||
|
f2b_buf_t buf; |
||||||
|
char *line; |
||||||
|
bool ret; |
||||||
|
int len; |
||||||
|
|
||||||
|
memset(&buf, 0x0, sizeof(buf)); |
||||||
|
|
||||||
|
ret = f2b_buf_alloc(&buf, 512); |
||||||
|
assert(ret = true); |
||||||
|
assert(buf.used == 0); |
||||||
|
assert(buf.size == 512); |
||||||
|
|
||||||
|
ret = f2b_buf_append(&buf, "test ", 0); |
||||||
|
assert(ret == true); |
||||||
|
assert(buf.used == 5); |
||||||
|
|
||||||
|
line = f2b_buf_extract(&buf, "\n"); |
||||||
|
assert(line == NULL); |
||||||
|
|
||||||
|
ret = f2b_buf_append(&buf, "test2\n", 0); |
||||||
|
assert(ret == true); |
||||||
|
assert(buf.used == 11); |
||||||
|
|
||||||
|
ret = f2b_buf_append(&buf, "test3\n", 0); |
||||||
|
assert(ret == true); |
||||||
|
assert(buf.used == 17); |
||||||
|
|
||||||
|
line = f2b_buf_extract(&buf, "\n"); |
||||||
|
assert(line != NULL); |
||||||
|
assert(strcmp(line, "test test2") == 0); |
||||||
|
assert(buf.used == 6); |
||||||
|
free(line); |
||||||
|
|
||||||
|
line = f2b_buf_extract(&buf, "\n"); |
||||||
|
assert(line != NULL); |
||||||
|
assert(buf.used == 0); |
||||||
|
free(line); |
||||||
|
|
||||||
|
f2b_buf_append(&buf, "test4\n\n", 6); |
||||||
|
assert(buf.used == 6); |
||||||
|
assert(strcmp(buf.data, "test4\n") == 0); |
||||||
|
|
||||||
|
len = f2b_buf_splice(&buf, 0); |
||||||
|
assert(len == 0); |
||||||
|
assert(buf.used == 6); |
||||||
|
assert(strcmp(buf.data, "test4\n") == 0); |
||||||
|
|
||||||
|
len = f2b_buf_splice(&buf, 2); |
||||||
|
assert(len == 2); |
||||||
|
assert(buf.used == 4); |
||||||
|
assert(strcmp(buf.data, "st4\n") == 0); |
||||||
|
|
||||||
|
len = f2b_buf_splice(&buf, 6); |
||||||
|
assert(len == 4); |
||||||
|
assert(buf.used == 0); |
||||||
|
assert(buf.data[0] == '\0'); |
||||||
|
|
||||||
|
f2b_buf_free(&buf); |
||||||
|
assert(buf.used == 0); |
||||||
|
assert(buf.size == 0); |
||||||
|
assert(buf.data == NULL); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -1,34 +1,41 @@ |
|||||||
#include "../src/common.h" |
#include "../src/common.h" |
||||||
|
#include "../src/buf.h" |
||||||
#include "../src/commands.h" |
#include "../src/commands.h" |
||||||
|
|
||||||
int main() { |
int main() { |
||||||
char buf[1024]; |
f2b_cmd_t cmd; |
||||||
const char *line; |
|
||||||
|
assert(f2b_cmd_parse(&cmd, "status") == true); |
||||||
UNUSED(line); |
assert(cmd.type = CMD_STATUS); |
||||||
|
assert(cmd.argc == 1); |
||||||
buf[0] = '\0'; |
assert(strcmp(cmd.args[0], "status") == 0); |
||||||
f2b_cmd_append_arg(buf, sizeof(buf), "42"); |
assert(cmd.data.used == 6); /* "status" */ |
||||||
assert(strcmp(buf, "42\n") == 0); |
f2b_buf_free(&cmd.data); |
||||||
|
|
||||||
line = "status"; |
assert(f2b_cmd_parse(&cmd, "stat") == false); /* no such command */ |
||||||
assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_STATUS); |
assert(cmd.type == CMD_UNKNOWN); |
||||||
|
|
||||||
line = "statu"; /* no such command */ |
assert(f2b_cmd_parse(&cmd, "jail test") == false); /* incomplete command */ |
||||||
assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_NONE); |
assert(cmd.type == CMD_UNKNOWN); |
||||||
|
|
||||||
line = "jail test"; /* incomplete command */ |
assert(f2b_cmd_parse(&cmd, "jail test status") == true); |
||||||
assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_NONE); |
assert(cmd.type == CMD_JAIL_STATUS); |
||||||
|
assert(cmd.argc == 3); |
||||||
buf[0] = '\0'; |
assert(strcmp(cmd.args[0], "jail") == 0); |
||||||
line = "jail test status"; |
assert(strcmp(cmd.args[1], "test") == 0); |
||||||
assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_JAIL_STATUS); |
assert(strcmp(cmd.args[2], "status") == 0); |
||||||
assert(strcmp(buf, "test\n") == 0); |
assert(cmd.data.used == 16); /* "jail\0test\0status" */ |
||||||
|
f2b_buf_free(&cmd.data); |
||||||
buf[0] = '\0'; |
|
||||||
line = "jail test set bantime 7200"; |
assert(f2b_cmd_parse(&cmd, "jail test set bantime 7200") == true); |
||||||
assert(f2b_cmd_parse(buf, sizeof(buf), line) == CMD_JAIL_SET); |
assert(cmd.type == CMD_JAIL_SET); |
||||||
assert(strcmp(buf, "test\nbantime\n7200\n") == 0); |
assert(cmd.argc == 5); |
||||||
|
assert(strcmp(cmd.args[0], "jail") == 0); |
||||||
|
assert(strcmp(cmd.args[1], "test") == 0); |
||||||
|
assert(strcmp(cmd.args[2], "set") == 0); |
||||||
|
assert(strcmp(cmd.args[3], "bantime") == 0); |
||||||
|
assert(strcmp(cmd.args[4], "7200") == 0); |
||||||
|
f2b_buf_free(&cmd.data); |
||||||
|
|
||||||
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |
||||||
} |
} |
||||||
|
@ -1,32 +0,0 @@ |
|||||||
#include "../src/common.h" |
|
||||||
#include "../src/cmsg.h" |
|
||||||
|
|
||||||
int main() { |
|
||||||
const char *argv[DATA_ARGS_MAX]; |
|
||||||
f2b_cmsg_t msg; |
|
||||||
|
|
||||||
memset(&msg, 0x0, sizeof(msg)); |
|
||||||
memset(argv, 0x0, sizeof(argv)); |
|
||||||
|
|
||||||
snprintf(msg.data, sizeof(msg.data), "test1\ntest2\n"); |
|
||||||
msg.size = strlen(msg.data); |
|
||||||
|
|
||||||
f2b_cmsg_convert_args(&msg); |
|
||||||
assert(memcmp(msg.data, "test1\0test2\0", 12) == 0); |
|
||||||
|
|
||||||
f2b_cmsg_extract_args(&msg, argv); |
|
||||||
assert(argv[0] == msg.data + 0); |
|
||||||
assert(argv[1] == msg.data + 6); |
|
||||||
assert(memcmp(argv[0], "test1\0", 6) == 0); |
|
||||||
assert(memcmp(argv[1], "test2\0", 6) == 0); |
|
||||||
|
|
||||||
/* data not null-terminated */ |
|
||||||
msg.size = 10; |
|
||||||
memcpy(msg.data, "test1\0test2\n", 10); |
|
||||||
assert(f2b_cmsg_extract_args(&msg, argv) == -1); |
|
||||||
|
|
||||||
msg.size = 0; |
|
||||||
assert(f2b_cmsg_extract_args(&msg, argv) == 0); |
|
||||||
|
|
||||||
return EXIT_SUCCESS; |
|
||||||
} |
|
Loading…
Reference in new issue