diff --git a/src/accept.c b/src/accept.c index 28d4367..2bda0bb 100644 --- a/src/accept.c +++ b/src/accept.c @@ -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; } } diff --git a/src/accept.h b/src/accept.h index 489c313..b8060b8 100644 --- a/src/accept.h +++ b/src/accept.h @@ -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); diff --git a/src/dupecheck.c b/src/dupecheck.c index 877d7c9..7dc3123 100644 --- a/src/dupecheck.c +++ b/src/dupecheck.c @@ -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; diff --git a/src/http.c b/src/http.c index f720e30..446c2d7 100644 --- a/src/http.c +++ b/src/http.c @@ -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) { diff --git a/src/http.h b/src/http.h index 33e172d..ca92f39 100644 --- a/src/http.h +++ b/src/http.h @@ -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 diff --git a/src/login.c b/src/login.c index 49c5158..01b3d91 100644 --- a/src/login.c +++ b/src/login.c @@ -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 */ diff --git a/src/login.h b/src/login.h index 1454c9e..e74eb58 100644 --- a/src/login.h +++ b/src/login.h @@ -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 diff --git a/src/worker.c b/src/worker.c index b3c1ebf..4005387 100644 --- a/src/worker.c +++ b/src/worker.c @@ -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) { diff --git a/src/worker.h b/src/worker.h index 29d0e37..631af85 100644 --- a/src/worker.h +++ b/src/worker.h @@ -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 */