implemented UDP submit support

git-svn-id: http://repo.ham.fi/svn/aprsc/trunk@727 3ce903b1-3385-4e86-93cd-f9a4a239f7ac
This commit is contained in:
Heikki Hannikainen 2012-08-28 07:05:32 +00:00
parent e75669d684
commit 7bf0500a50
9 changed files with 276 additions and 149 deletions

View File

@ -36,6 +36,7 @@
#include "dupecheck.h"
#include "filter.h"
#include "login.h"
#include "http.h"
#include "incoming.h" /* incoming_handler prototype */
#include "uplink.h"
@ -65,6 +66,10 @@ static struct listen_t *listen_list;
int accept_shutting_down;
int accept_reconfiguring;
/* pseudoworker + pseudoclient for incoming UDP packets */
struct worker_t *udp_worker = NULL;
struct client_t *udp_pseudoclient = NULL;
/* structure allocator/free */
@ -128,7 +133,7 @@ static int accept_sighandler(int signum)
* Pass a new client to a worker thread
*/
int accept_pass_client_to_worker(struct worker_t *wc, struct client_t *c)
static int accept_pass_client_to_worker(struct worker_t *wc, struct client_t *c)
{
int pe;
@ -337,7 +342,7 @@ static void close_listeners(void)
* Generate UDP peer "clients"
*/
void peerip_clients_config(void)
static void peerip_clients_config(void)
{
struct client_t *c;
struct peerip_config_t *pe;
@ -416,46 +421,114 @@ void peerip_clients_config(void)
}
}
/*
* Process an incoming UDP packet submission
*/
static void accept_process_udpsubmit(struct listen_t *l, char *buf, int len, char *remote_host)
{
int packet_len;
char *login_string = NULL;
char *packet = NULL;
char *username = NULL;
char validated;
int e;
//hlog(LOG_DEBUG, "got udp submit: %.*s", len, buf);
packet_len = loginpost_split(buf, len, &login_string, &packet);
if (packet_len == -1) {
hlog(LOG_DEBUG, "UDP submit [%s]: No newline (LF) found in data", remote_host);
return;
}
if (!login_string) {
hlog(LOG_DEBUG, "UDP submit [%s]: No login string in data", remote_host);
return;
}
if (!packet) {
hlog(LOG_DEBUG, "UDP submit [%s]: No packet data found in data", remote_host);
return;
}
hlog(LOG_DEBUG, "UDP submit [%s]: login string: %s", remote_host, login_string);
hlog(LOG_DEBUG, "UDP submit [%s]: packet: %s", remote_host, packet);
/* process the login string */
validated = http_udp_upload_login(remote_host, login_string, &username);
if (validated < 0) {
hlog(LOG_DEBUG, "UDP submit [%s]: Invalid login string", remote_host);
return;
}
if (validated != 1) {
hlog(LOG_DEBUG, "UDP submit [%s]: Invalid passcode for user %s", remote_host, username);
return;
}
/* packet size limits */
if (packet_len < PACKETLEN_MIN) {
hlog(LOG_DEBUG, "UDP submit [%s]: Packet too short: %d bytes", remote_host, packet_len);
return;
}
if (packet_len > PACKETLEN_MAX-2) {
hlog(LOG_DEBUG, "UDP submit [%s]: Packet too long: %d bytes", remote_host, packet_len);
return;
}
e = pseudoclient_push_packet(udp_worker, udp_pseudoclient, username, packet, packet_len);
if (e < 0)
hlog(LOG_DEBUG, "UDP submit [%s]: Incoming packet parse failure code %d: %s", remote_host, e, packet);
else
hlog(LOG_DEBUG, "UDP submit [%s]: Incoming packet parsed, code %d: %s", remote_host, e, packet);
}
/*
* Receive UDP packets from an UDP listener
*/
static void accept_udp_recv(struct listen_t *l)
{
union sockaddr_u addr;
socklen_t addrlen;
char buf[2000];
int i;
char *addrs;
/* Receive as much as there is -- that is, LOOP... */
addrlen = sizeof(addr);
while ((i = recvfrom( l->udp->fd, buf, sizeof(buf)-1, MSG_DONTWAIT|MSG_TRUNC, (struct sockaddr *)&addr, &addrlen )) >= 0) {
if (!(l->clientflags & CLFLAGS_UDPSUBMIT)) {
hlog(LOG_DEBUG, "accept thread discarded an UDP packet on a listening socket");
continue;
}
addrs = strsockaddr(&addr.sa, addrlen);
accept_process_udpsubmit(l, buf, i, addrs);
hfree(addrs);
}
}
/*
* Accept a single connection
*/
static struct client_t *do_accept(struct listen_t *l)
static void do_accept(struct listen_t *l)
{
int fd, i;
struct client_t *c;
union sockaddr_u sa; /* large enough for also IPv6 address */
socklen_t addr_len = sizeof(sa);
char buf[2000];
static int next_receiving_worker;
struct worker_t *w;
struct worker_t *wc;
static time_t last_EMFILE_report;
char *s;
while (l->udp) {
/* This should not really happen any more, since accept_thread
* does not poll() UDP sockets, that's left to worker 0.
*/
/* Received data will be discarded, so receiving it */
/* TRUNCATED is just fine. Sender isn't interesting either. */
/* Receive as much as there is -- that is, LOOP... */
i = recv( l->udp->fd, buf, sizeof(buf), MSG_DONTWAIT|MSG_TRUNC );
if (i < 0)
return 0; /* no more data */
if (!(l->clientflags & CLFLAGS_UDPSUBMIT)) {
hlog(LOG_DEBUG, "accept thread discarded an UDP packet on a listening socket");
continue;
}
hlog(LOG_DEBUG, "accept thread got an UDP packet on an udpsubmit socket");
}
if ((fd = accept(l->fd, (struct sockaddr*)&sa, &addr_len)) < 0) {
int e = errno;
switch (e) {
@ -486,11 +559,11 @@ static struct client_t *do_accept(struct listen_t *l)
last_EMFILE_report = tick;
hlog(LOG_ERR, "accept() failed: %s (continuing)", strerror(e));
}
return NULL;
return;
/* Errors reporting system internal/external glitches */
default:
hlog(LOG_ERR, "accept() failed: %s (continuing)", strerror(e));
return NULL;
return;
}
}
@ -507,7 +580,7 @@ static struct client_t *do_accept(struct listen_t *l)
close(fd);
hfree(s);
inbound_connects_account(-1, l->portaccount); /* account rejected connection */
return NULL;
return;
}
/* match against acl... could probably have an error message to the client */
@ -517,7 +590,7 @@ static struct client_t *do_accept(struct listen_t *l)
close(fd);
hfree(s);
inbound_connects_account(-1, l->portaccount); /* account rejected connection */
return NULL;
return;
}
}
@ -645,13 +718,13 @@ static struct client_t *do_accept(struct listen_t *l)
if (accept_pass_client_to_worker(wc, c))
goto err;
return c;
return;
err:
inbound_connects_account(0, c->portaccount); /* something failed, remove this from accounts.. */
client_free(c);
return 0;
return;
}
/*
@ -683,6 +756,18 @@ void accept_thread(void *asdf)
/* start the accept thread, which will start server threads */
hlog(LOG_INFO, "Accept_thread starting...");
/* we allocate a worker structure to be used within the accept thread
* for parsing incoming UDP packets and passing them on to the dupecheck
* thread.
*/
udp_worker = worker_alloc();
udp_worker->id = 81;
/* we also need a client structure to be used with incoming
* HTTP position uploads
*/
udp_pseudoclient = pseudoclient_setup(81);
accept_reconfiguring = 1;
while (!accept_shutting_down) {
if (accept_reconfiguring) {
@ -761,8 +846,12 @@ void accept_thread(void *asdf)
hlog(LOG_CRIT, "accept_thread: polling list and listener list do mot match!");
exit(1);
}
if (acceptpfd[n].revents)
do_accept(l); /* accept a single connection */
if (acceptpfd[n].revents) {
if (l->udp)
accept_udp_recv(l); /* receive UDP packets */
else
do_accept(l); /* accept a single connection */
}
l = l->next;
}
}

View File

@ -16,6 +16,8 @@
extern int accept_reconfiguring;
extern int accept_shutting_down;
extern struct worker_t *udp_worker;
extern void accept_thread(void *asdf);
extern int accept_listener_status(cJSON *listeners, cJSON *totals);

View File

@ -29,6 +29,7 @@
#include "filter.h"
#include "historydb.h"
#include "http.h"
#include "accept.h"
int dupecheck_shutting_down;
int dupecheck_running;
@ -524,6 +525,13 @@ static void dupecheck_thread(void)
&pb_out_count, &pb_out_dupe_count);
}
if ((udp_worker) && udp_worker->pbuf_incoming) {
n += dupecheck_drain_worker(udp_worker,
&pb_out_prevp, &pb_out_last,
&pb_out_dupe_prevp, &pb_out_dupe_last,
&pb_out_count, &pb_out_dupe_count);
}
// terminate those out-chains in every case..
*pb_out_prevp = NULL;
*pb_out_dupe_prevp = NULL;

View File

@ -42,6 +42,7 @@
#include "status.h"
#include "passcode.h"
#include "incoming.h"
#include "login.h"
#include "counterdata.h"
int http_shutting_down;
@ -96,7 +97,7 @@ static struct http_static_t http_content_types[] = {
* Find a content-type for a file name
*/
char *http_content_type(char *fn)
static char *http_content_type(const char *fn)
{
struct http_static_t *cmdp;
static char default_ctype[] = "text/html";
@ -120,7 +121,7 @@ char *http_content_type(char *fn)
* HTTP date formatting
*/
int http_date(char *buf, int len, time_t t)
static int http_date(char *buf, int len, time_t t)
{
struct tm tb;
char *wkday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", NULL };
@ -134,7 +135,7 @@ int http_date(char *buf, int len, time_t t)
tb.tm_hour, tb.tm_min, tb.tm_sec);
}
void http_header_base(struct evkeyvalq *headers, int last_modified)
static void http_header_base(struct evkeyvalq *headers, int last_modified)
{
char dbuf[80];
@ -149,64 +150,6 @@ void http_header_base(struct evkeyvalq *headers, int last_modified)
}
}
/*
* Parse the login string in a POST
* Argh, why are these not in standard POST parameters?
*/
int http_upload_login(char *addr_rem, char *s, char **username)
{
int argc;
char *argv[256];
int i;
/* parse to arguments */
if ((argc = parse_args_noshell(argv, s)) == 0)
return -1;
if (argc < 2) {
hlog(LOG_WARNING, "%s: HTTP POST: Invalid login string, too few arguments: '%s'", addr_rem, s);
return -1;
}
if (strcasecmp(argv[0], "user") != 0) {
hlog(LOG_WARNING, "%s: HTTP POST: Invalid login string, no 'user': '%s'", addr_rem, s);
return -1;
}
*username = argv[1];
if (strlen(*username) > 9) /* limit length */
*username[9] = 0;
int given_passcode = -1;
int validated = 0;
for (i = 2; i < argc; i++) {
if (strcasecmp(argv[i], "pass") == 0) {
if (++i >= argc) {
hlog(LOG_WARNING, "%s (%s): HTTP POST: No passcode after pass command", addr_rem, username);
break;
}
given_passcode = atoi(argv[i]);
if (given_passcode >= 0)
if (given_passcode == aprs_passcode(*username))
validated = 1;
} else if (strcasecmp(argv[i], "vers") == 0) {
if (i+2 >= argc) {
hlog(LOG_DEBUG, "%s (%s): HTTP POST: No application name and version after vers command", addr_rem, username);
break;
}
// skip app name and version
i += 2;
}
}
return validated;
}
/*
* Split a login + packet string. Terminates login string with 0,
* returns length of packet.
@ -254,6 +197,48 @@ int loginpost_split(char *post, int len, char **login_string, char **packet)
return packet_len;
}
/*
* Process an incoming HTTP or UDP packet by parsing it and pushing
* it to the dupecheck thread through the pseudoworker
*/
int pseudoclient_push_packet(struct worker_t *worker, struct client_t *pseudoclient, const char *username, char *packet, int packet_len)
{
int e;
/* fill the user's information in the pseudoclient's structure
* for the q construct handler's viewing pleasure
*/
#ifdef FIXED_IOBUFS
strncpy(pseudoclient->username, username, sizeof(pseudoclient->username));
pseudoclient->username[sizeof(pseudoclient->username)-1] = 0;
#else
pseudoclient->username = username;
#endif
pseudoclient->username_len = strlen(pseudoclient->username);
/* ok, try to digest the packet */
e = incoming_parse(worker, pseudoclient, packet, packet_len);
#ifdef FIXED_IOBUFS
pseudoclient->username[0] = 0;
#else
pseudoclient->username = NULL;
#endif
pseudoclient->username_len = 0;
if (e < 0)
return e;
/* if the packet parser managed to digest the packet and put it to
* the thread-local incoming queue, flush it for dupecheck to
* grab
*/
if (worker->pbuf_incoming_local)
incoming_flush(worker);
return e;
}
/*
* Accept a POST containing a position
@ -261,7 +246,7 @@ int loginpost_split(char *post, int len, char **login_string, char **packet)
#define MAX_HTTP_POST_DATA 2048
void http_upload_position(struct evhttp_request *r, char *remote_host)
static void http_upload_position(struct evhttp_request *r, const char *remote_host)
{
struct evbuffer *bufin, *bufout;
struct evkeyvalq *req_headers;
@ -271,6 +256,7 @@ void http_upload_position(struct evhttp_request *r, char *remote_host)
ev_ssize_t l;
char *login_string = NULL;
char *packet = NULL;
char *username = NULL;
char validated;
int e;
int packet_len;
@ -331,8 +317,7 @@ void http_upload_position(struct evhttp_request *r, char *remote_host)
hlog(LOG_DEBUG, "packet: %s", packet);
/* process the login string */
char *username;
validated = http_upload_login(remote_host, login_string, &username);
validated = http_udp_upload_login(remote_host, login_string, &username);
if (validated < 0) {
evhttp_send_error(r, HTTP_BADREQUEST, "Invalid login string");
return;
@ -354,39 +339,14 @@ void http_upload_position(struct evhttp_request *r, char *remote_host)
return;
}
/* fill the user's information in the pseudoclient's structure
* for the q construct handler's viewing pleasure
*/
#ifdef FIXED_IOBUFS
strncpy(http_pseudoclient->username, username, sizeof(http_pseudoclient->username));
http_pseudoclient->username[sizeof(http_pseudoclient->username)-1] = 0;
#else
http_pseudoclient->username = username;
#endif
http_pseudoclient->username_len = strlen(http_pseudoclient->username);
/* ok, try to digest the packet */
e = incoming_parse(http_worker, http_pseudoclient, packet, packet_len);
e = pseudoclient_push_packet(http_worker, http_pseudoclient, username, packet, packet_len);
#ifdef FIXED_IOBUFS
http_pseudoclient->username[0] = 0;
#else
http_pseudoclient->username = NULL;
#endif
if (e < 0) {
hlog(LOG_DEBUG, "http incoming packet parse failure code %d: %s", e, packet);
evhttp_send_error(r, HTTP_BADREQUEST, "Packet parsing failure");
return;
}
/* if the packet parser managed to digest the packet and put it to
* the thread-local incoming queue, flush it for dupecheck to
* grab
*/
if (http_worker->pbuf_incoming_local)
incoming_flush(http_worker);
bufout = evbuffer_new();
evbuffer_add(bufout, "ok\n", 3);
@ -402,7 +362,7 @@ void http_upload_position(struct evhttp_request *r, char *remote_host)
* Generate a status JSON response
*/
void http_status(struct evhttp_request *r)
static void http_status(struct evhttp_request *r)
{
char *json;
struct evbuffer *buffer = evbuffer_new();
@ -424,7 +384,7 @@ void http_status(struct evhttp_request *r)
* Return counterdata in JSON
*/
void http_counterdata(struct evhttp_request *r, const char *uri)
static void http_counterdata(struct evhttp_request *r, const char *uri)
{
char *json;
const char *query;
@ -548,7 +508,7 @@ static void http_route_static(struct evhttp_request *r, const char *uri)
* HTTP request router
*/
void http_router(struct evhttp_request *r, void *which_server)
static void http_router(struct evhttp_request *r, void *which_server)
{
char *remote_host;
ev_uint16_t remote_port;
@ -603,7 +563,7 @@ struct event_base *libbase = NULL;
* HTTP timer event, mainly to catch the shutdown signal
*/
void http_timer(evutil_socket_t fd, short events, void *arg)
static void http_timer(evutil_socket_t fd, short events, void *arg)
{
struct timeval http_timer_tv;
http_timer_tv.tv_sec = 0;
@ -620,7 +580,7 @@ void http_timer(evutil_socket_t fd, short events, void *arg)
event_add(ev_timer, &http_timer_tv);
}
void http_srvr_defaults(struct evhttp *srvr)
static void http_srvr_defaults(struct evhttp *srvr)
{
// limit what the clients can do a bit
evhttp_set_allowed_methods(srvr, EVHTTP_REQ_GET);
@ -631,24 +591,6 @@ void http_srvr_defaults(struct evhttp *srvr)
// TODO: How to limit the amount of concurrent HTTP connections?
}
struct client_t *http_pseudoclient_setup(void)
{
struct client_t *c;
c = http_pseudoclient = client_alloc();
c->fd = -1;
c->portnum = 80;
c->state = CSTATE_CONNECTED;
c->flags = CLFLAGS_INPORT|CLFLAGS_CLIENTONLY;
c->validated = 1; // we will validate on every packet
//c->portaccount = l->portaccount;
c->keepalive = tick;
c->connect_time = tick;
c->last_read = tick;
return c;
}
/*
* HTTP server thread
*/
@ -689,7 +631,7 @@ void http_thread(void *asdf)
/* we also need a client structure to be used with incoming
* HTTP position uploads
*/
http_pseudoclient_setup();
http_pseudoclient = pseudoclient_setup(80);
http_reconfiguring = 1;
while (!http_shutting_down) {

View File

@ -11,11 +11,16 @@
#ifndef HTTP_H
#define HTTP_H
#include "worker.h"
extern struct worker_t *http_worker;
extern int http_reconfiguring;
extern int http_shutting_down;
extern int loginpost_split(char *post, int len, char **login_string, char **packet);
extern int pseudoclient_push_packet(struct worker_t *worker, struct client_t *pseudoclient, const char *username, char *packet, int packet_len);
extern void http_thread(void *asdf);
#endif

View File

@ -22,6 +22,64 @@
#include "clientlist.h"
#include "parse_qc.h"
/*
* Parse the login string in a HTTP POST or UDP submit packet
* Argh, why are these not in standard POST parameters in HTTP?
*/
int http_udp_upload_login(const char *addr_rem, char *s, char **username)
{
int argc;
char *argv[256];
int i;
/* parse to arguments */
if ((argc = parse_args_noshell(argv, s)) == 0)
return -1;
if (argc < 2) {
hlog(LOG_WARNING, "%s: HTTP POST: Invalid login string, too few arguments: '%s'", addr_rem, s);
return -1;
}
if (strcasecmp(argv[0], "user") != 0) {
hlog(LOG_WARNING, "%s: HTTP POST: Invalid login string, no 'user': '%s'", addr_rem, s);
return -1;
}
*username = argv[1];
if (strlen(*username) > 9) /* limit length */
*username[9] = 0;
int given_passcode = -1;
int validated = 0;
for (i = 2; i < argc; i++) {
if (strcasecmp(argv[i], "pass") == 0) {
if (++i >= argc) {
hlog(LOG_WARNING, "%s (%s): HTTP POST: No passcode after pass command", addr_rem, username);
break;
}
given_passcode = atoi(argv[i]);
if (given_passcode >= 0)
if (given_passcode == aprs_passcode(*username))
validated = 1;
} else if (strcasecmp(argv[i], "vers") == 0) {
if (i+2 >= argc) {
hlog(LOG_DEBUG, "%s (%s): HTTP POST: No application name and version after vers command", addr_rem, username);
break;
}
// skip app name and version
i += 2;
}
}
return validated;
}
/*
* login.c: works in the context of the worker thread
*/

View File

@ -13,6 +13,7 @@
#include "worker.h"
extern int http_udp_upload_login(const char *addr_rem, char *s, char **username);
extern int login_handler(struct worker_t *self, struct client_t *c, int l4proto, char *s, int len);
#endif

View File

@ -350,6 +350,27 @@ void client_free(struct client_t *c)
#endif
}
/*
* Set up a pseudoclient for UDP and HTTP submitted packets
*/
struct client_t *pseudoclient_setup(int portnum)
{
struct client_t *c;
c = client_alloc();
c->fd = -1;
c->portnum = portnum;
c->state = CSTATE_CONNECTED;
c->flags = CLFLAGS_INPORT|CLFLAGS_CLIENTONLY;
c->validated = 1; // we will validate on every packet
//c->portaccount = l->portaccount;
c->keepalive = tick;
c->connect_time = tick;
c->last_read = tick;
return c;
}
char *strsockaddr(const struct sockaddr *sa, const int addr_len)
{

View File

@ -338,6 +338,7 @@ struct client_t {
extern struct client_t *client_alloc(void);
extern void client_free(struct client_t *c);
extern struct client_t *pseudoclient_setup(int portnum);
/* worker thread structure */