mirror of
https://github.com/ntop/n2n.git
synced 2024-09-19 16:41:11 +02:00
Merge pull request #983 from hamishcoleman/json_pubsub
Refactoring management code to increase sharing
This commit is contained in:
commit
3fa0fa1bbe
|
@ -190,6 +190,7 @@ add_library(n2n STATIC
|
|||
src/aes.c
|
||||
src/speck.c
|
||||
src/random_numbers.c
|
||||
src/management.c
|
||||
src/pearson.c
|
||||
src/header_encryption.c
|
||||
src/tuntap_freebsd.c
|
||||
|
|
|
@ -100,8 +100,11 @@ LINT_CCODE=\
|
|||
src/edge_utils_win32.c \
|
||||
src/example_edge_embed_quick_edge_init.c \
|
||||
src/header_encryption.c \
|
||||
src/management.c \
|
||||
src/management.h \
|
||||
src/sn_management.c \
|
||||
src/sn_selection.c \
|
||||
src/strbuf.h \
|
||||
src/transform_cc20.c \
|
||||
src/transform_null.c \
|
||||
src/tuntap_freebsd.c \
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# Management API
|
||||
|
||||
This document is focused on the machine readable API interfaces.
|
||||
This document is focused on the machine readable API interfaces. It is a
|
||||
generic document describing the protocol framing, but not the specific
|
||||
messages and replies.
|
||||
|
||||
Both the edge and the supernode provide a management interface UDP port.
|
||||
These interfaces have some documentation on their non machine readable
|
||||
|
@ -30,8 +32,7 @@ where this flexibility is easy to provide.
|
|||
Since the API is over UDP, the replies are structured so that each part of
|
||||
the reply is clearly tagged as belonging to one request and there is a
|
||||
clear begin and end marker for the reply. This is expected to support the
|
||||
future possibilities of pipelined and overlapping transactions as well as
|
||||
pub/sub asynchronous event channels.
|
||||
future possibilities of pipelined and overlapping transactions.
|
||||
|
||||
The replies will also handle some small amount of re-ordering of the
|
||||
packets, but that is not an specific goal of the protocol.
|
||||
|
@ -105,6 +106,11 @@ on a given socket.
|
|||
One possible client implementation is a number between 0 and 999, incremented
|
||||
for each request and wrapping around to zero when it is greater than 999.
|
||||
|
||||
The client is also expected to use the tag to determine the format of the
|
||||
data in the reply - since the client requested that data, it also knows what
|
||||
should be in the matching reply. This is especially important with
|
||||
asynchronous pub/sub events.
|
||||
|
||||
#### Message Flags
|
||||
|
||||
This subfield is a set of bit flags that are hex-encoded and describe any
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef _N2N_DEFINE_H_
|
||||
#define _N2N_DEFINE_H_
|
||||
|
||||
/* N2N packet header indicators. */
|
||||
#define MSG_TYPE_REGISTER 1
|
||||
#define MSG_TYPE_DEREGISTER 2
|
||||
|
@ -219,3 +222,5 @@ enum skip_add {SN_ADD = 0, SN_ADD_SKIP = 1, SN_ADD_ADDED = 2};
|
|||
#ifndef min
|
||||
#define min(a, b) (((a) >(b)) ? (b) : (a))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -19,6 +19,12 @@
|
|||
#ifndef _N2N_TYPEDEFS_H_
|
||||
#define _N2N_TYPEDEFS_H_
|
||||
|
||||
#include <stdint.h> // for uint8_t and friends
|
||||
#ifndef WIN32
|
||||
#include <arpa/inet.h> // for in_addr_t
|
||||
#endif
|
||||
#include <uthash.h>
|
||||
#include <n2n_define.h>
|
||||
|
||||
typedef uint8_t n2n_community_t[N2N_COMMUNITY_SIZE];
|
||||
typedef uint8_t n2n_private_public_key_t[N2N_PRIVATE_PUBLIC_KEY_SIZE];
|
||||
|
@ -38,11 +44,6 @@ typedef unsigned int uint32_t;
|
|||
typedef unsigned short uint16_t;
|
||||
typedef unsigned char uint8_t;
|
||||
|
||||
/* sys/types.h typedefs (not present in Visual Studio) */
|
||||
typedef unsigned int u_int32_t;
|
||||
typedef unsigned short u_int16_t;
|
||||
typedef unsigned char u_int8_t;
|
||||
|
||||
#ifndef __MINGW32__
|
||||
typedef int ssize_t;
|
||||
#endif
|
||||
|
@ -139,45 +140,45 @@ typedef struct ether_hdr ether_hdr_t;
|
|||
|
||||
struct n2n_iphdr {
|
||||
#if defined(__LITTLE_ENDIAN__)
|
||||
u_int8_t ihl:4, version:4;
|
||||
uint8_t ihl:4, version:4;
|
||||
#elif defined(__BIG_ENDIAN__)
|
||||
u_int8_t version:4, ihl:4;
|
||||
uint8_t version:4, ihl:4;
|
||||
#else
|
||||
# error "Byte order must be defined"
|
||||
#endif
|
||||
u_int8_t tos;
|
||||
u_int16_t tot_len;
|
||||
u_int16_t id;
|
||||
u_int16_t frag_off;
|
||||
u_int8_t ttl;
|
||||
u_int8_t protocol;
|
||||
u_int16_t check;
|
||||
u_int32_t saddr;
|
||||
u_int32_t daddr;
|
||||
uint8_t tos;
|
||||
uint16_t tot_len;
|
||||
uint16_t id;
|
||||
uint16_t frag_off;
|
||||
uint8_t ttl;
|
||||
uint8_t protocol;
|
||||
uint16_t check;
|
||||
uint32_t saddr;
|
||||
uint32_t daddr;
|
||||
} PACK_STRUCT;
|
||||
|
||||
struct n2n_tcphdr {
|
||||
u_int16_t source;
|
||||
u_int16_t dest;
|
||||
u_int32_t seq;
|
||||
u_int32_t ack_seq;
|
||||
uint16_t source;
|
||||
uint16_t dest;
|
||||
uint32_t seq;
|
||||
uint32_t ack_seq;
|
||||
#if defined(__LITTLE_ENDIAN__)
|
||||
u_int16_t res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1;
|
||||
uint16_t res1:4, doff:4, fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1;
|
||||
#elif defined(__BIG_ENDIAN__)
|
||||
u_int16_t doff:4, res1:4, cwr:1, ece:1, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1;
|
||||
uint16_t doff:4, res1:4, cwr:1, ece:1, urg:1, ack:1, psh:1, rst:1, syn:1, fin:1;
|
||||
#else
|
||||
# error "Byte order must be defined"
|
||||
#endif
|
||||
u_int16_t window;
|
||||
u_int16_t check;
|
||||
u_int16_t urg_ptr;
|
||||
uint16_t window;
|
||||
uint16_t check;
|
||||
uint16_t urg_ptr;
|
||||
} PACK_STRUCT;
|
||||
|
||||
struct n2n_udphdr {
|
||||
u_int16_t source;
|
||||
u_int16_t dest;
|
||||
u_int16_t len;
|
||||
u_int16_t check;
|
||||
uint16_t source;
|
||||
uint16_t dest;
|
||||
uint16_t len;
|
||||
uint16_t check;
|
||||
} PACK_STRUCT;
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
|
|
|
@ -19,106 +19,8 @@
|
|||
#include "n2n.h"
|
||||
#include "edge_utils_win32.h"
|
||||
|
||||
typedef struct strbuf {
|
||||
size_t size;
|
||||
char str[];
|
||||
} strbuf_t;
|
||||
|
||||
#define STRBUF_INIT(buf,p) do { \
|
||||
buf = (void *)p; \
|
||||
buf->size = sizeof(*p) - sizeof(size_t); \
|
||||
} while(0)
|
||||
|
||||
enum n2n_mgmt_type {
|
||||
N2N_MGMT_UNKNOWN = 0,
|
||||
N2N_MGMT_READ = 1,
|
||||
N2N_MGMT_WRITE = 2,
|
||||
N2N_MGMT_SUB = 3,
|
||||
};
|
||||
|
||||
/*
|
||||
* Everything needed to reply to a request
|
||||
*/
|
||||
typedef struct mgmt_req {
|
||||
n2n_edge_t *eee;
|
||||
enum n2n_mgmt_type type;
|
||||
char tag[10];
|
||||
struct sockaddr_in sender_sock;
|
||||
} mgmt_req_t;
|
||||
|
||||
/*
|
||||
* Read/Write handlers are defined in this structure
|
||||
*/
|
||||
#define FLAG_WROK 1
|
||||
typedef struct mgmt_handler {
|
||||
int flags;
|
||||
char *cmd;
|
||||
char *help;
|
||||
void (*func)(mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv);
|
||||
} mgmt_handler_t;
|
||||
|
||||
/*
|
||||
* Event topic names are defined in this structure
|
||||
*/
|
||||
typedef struct mgmt_events {
|
||||
enum n2n_event_topic topic;
|
||||
char *cmd;
|
||||
char *help;
|
||||
} mgmt_events_t;
|
||||
|
||||
// Lookup the index of matching argv0 in a cmd list
|
||||
// store index in "Result", or -1 for not found
|
||||
#define lookup_handler(Result, list, argv0) do { \
|
||||
int nr_max = sizeof(list) / sizeof(list[0]); \
|
||||
for( Result=0; Result < nr_max; Result++ ) { \
|
||||
if(0 == strcmp(list[Result].cmd, argv0)) { \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
if( Result >= nr_max ) { \
|
||||
Result = -1; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
ssize_t send_reply (mgmt_req_t *req, strbuf_t *buf, size_t msg_len) {
|
||||
// TODO: better error handling (counters?)
|
||||
return sendto(req->eee->udp_mgmt_sock, buf->str, msg_len, 0,
|
||||
(struct sockaddr *) &req->sender_sock, sizeof(struct sockaddr_in));
|
||||
}
|
||||
|
||||
size_t gen_json_1str (strbuf_t *buf, char *tag, char *_type, char *key, char *val) {
|
||||
return snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"%s\","
|
||||
"\"%s\":\"%s\"}\n",
|
||||
tag,
|
||||
_type,
|
||||
key,
|
||||
val);
|
||||
}
|
||||
|
||||
size_t gen_json_1uint (strbuf_t *buf, char *tag, char *_type, char *key, unsigned int val) {
|
||||
return snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"%s\","
|
||||
"\"%s\":%u}\n",
|
||||
tag,
|
||||
_type,
|
||||
key,
|
||||
val);
|
||||
}
|
||||
|
||||
static void send_json_1str (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, char *val) {
|
||||
size_t msg_len = gen_json_1str(buf, req->tag, _type, key, val);
|
||||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
|
||||
static void send_json_1uint (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, unsigned int val) {
|
||||
size_t msg_len = gen_json_1uint(buf, req->tag, _type, key, val);
|
||||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
#include "strbuf.h"
|
||||
#include "management.h"
|
||||
|
||||
size_t event_debug (strbuf_t *buf, char *tag, int data0, void *data1) {
|
||||
traceEvent(TRACE_DEBUG, "Unexpected call to event_debug");
|
||||
|
@ -155,31 +57,9 @@ size_t event_peer (strbuf_t *buf, char *tag, int data0, void *data1) {
|
|||
sock_to_cstr(sockbuf, &(peer->sock)));
|
||||
}
|
||||
|
||||
static void mgmt_error (mgmt_req_t *req, strbuf_t *buf, char *msg) {
|
||||
send_json_1str(req, buf, "error", "error", msg);
|
||||
}
|
||||
|
||||
static void mgmt_stop (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||
|
||||
if(req->type==N2N_MGMT_WRITE) {
|
||||
*req->eee->keep_running = 0;
|
||||
}
|
||||
|
||||
send_json_1uint(req, buf, "row", "keep_running", *req->eee->keep_running);
|
||||
}
|
||||
|
||||
static void mgmt_verbose (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||
|
||||
if(req->type==N2N_MGMT_WRITE) {
|
||||
if(argv) {
|
||||
setTraceLevel(strtoul(argv, NULL, 0));
|
||||
}
|
||||
}
|
||||
|
||||
send_json_1uint(req, buf, "row", "traceLevel", getTraceLevel());
|
||||
}
|
||||
|
||||
static void mgmt_communities (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||
static void mgmt_communities (mgmt_req_t *req, strbuf_t *buf) {
|
||||
|
||||
if(req->eee->conf.header_encryption != HEADER_ENCRYPTION_NONE) {
|
||||
mgmt_error(req, buf, "noaccess");
|
||||
|
@ -189,7 +69,7 @@ static void mgmt_communities (mgmt_req_t *req, strbuf_t *buf, char *argv0, char
|
|||
send_json_1str(req, buf, "row", "community", (char *)req->eee->conf.community_name);
|
||||
}
|
||||
|
||||
static void mgmt_supernodes (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||
static void mgmt_supernodes (mgmt_req_t *req, strbuf_t *buf) {
|
||||
size_t msg_len;
|
||||
struct peer_info *peer, *tmpPeer;
|
||||
macstr_t mac_buf;
|
||||
|
@ -266,7 +146,7 @@ static void mgmt_edges_row (mgmt_req_t *req, strbuf_t *buf, struct peer_info *pe
|
|||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
|
||||
static void mgmt_edges (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||
static void mgmt_edges (mgmt_req_t *req, strbuf_t *buf) {
|
||||
struct peer_info *peer, *tmpPeer;
|
||||
|
||||
// dump nodes with forwarding through supernodes
|
||||
|
@ -280,7 +160,7 @@ static void mgmt_edges (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv)
|
|||
}
|
||||
}
|
||||
|
||||
static void mgmt_timestamps (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||
static void mgmt_timestamps (mgmt_req_t *req, strbuf_t *buf) {
|
||||
size_t msg_len;
|
||||
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
|
@ -298,7 +178,7 @@ static void mgmt_timestamps (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *
|
|||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
|
||||
static void mgmt_packetstats (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||
static void mgmt_packetstats (mgmt_req_t *req, strbuf_t *buf) {
|
||||
size_t msg_len;
|
||||
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
|
@ -354,20 +234,15 @@ static void mgmt_packetstats (mgmt_req_t *req, strbuf_t *buf, char *argv0, char
|
|||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
|
||||
static void mgmt_post_test (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||
static void mgmt_post_test (mgmt_req_t *req, strbuf_t *buf) {
|
||||
|
||||
send_json_1str(req, buf, "row", "sending", "test");
|
||||
mgmt_event_post(N2N_EVENT_TEST, -1, argv);
|
||||
}
|
||||
|
||||
static void mgmt_unimplemented (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||
|
||||
mgmt_error(req, buf, "unimplemented");
|
||||
mgmt_event_post(N2N_EVENT_TEST, -1, req->argv);
|
||||
}
|
||||
|
||||
// Forward define so we can include this in the mgmt_handlers[] table
|
||||
static void mgmt_help (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv);
|
||||
static void mgmt_help_events (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv);
|
||||
static void mgmt_help (mgmt_req_t *req, strbuf_t *buf);
|
||||
static void mgmt_help_events (mgmt_req_t *req, strbuf_t *buf);
|
||||
|
||||
static const mgmt_handler_t mgmt_handlers[] = {
|
||||
{ .cmd = "reload_communities", .flags = FLAG_WROK, .help = "Reserved for supernode", .func = mgmt_unimplemented},
|
||||
|
@ -392,7 +267,8 @@ static mgmt_req_t mgmt_event_subscribers[] = {
|
|||
};
|
||||
|
||||
/* Map topic number to function */
|
||||
static const size_t (*mgmt_events[])(strbuf_t *buf, char *tag, int data0, void *data1) = {
|
||||
// TODO: want this to be const
|
||||
static mgmt_event_handler_t *mgmt_events[] = {
|
||||
[N2N_EVENT_DEBUG] = event_debug,
|
||||
[N2N_EVENT_TEST] = event_test,
|
||||
[N2N_EVENT_PEER] = event_peer,
|
||||
|
@ -408,81 +284,25 @@ static const mgmt_events_t mgmt_event_names[] = {
|
|||
void mgmt_event_post (enum n2n_event_topic topic, int data0, void *data1) {
|
||||
mgmt_req_t *debug = &mgmt_event_subscribers[N2N_EVENT_DEBUG];
|
||||
mgmt_req_t *sub = &mgmt_event_subscribers[topic];
|
||||
mgmt_event_handler_t *fn = mgmt_events[topic];
|
||||
|
||||
traceEvent(TRACE_DEBUG, "post topic=%i data0=%i", topic, data0);
|
||||
|
||||
if( sub->type != N2N_MGMT_SUB && debug->type != N2N_MGMT_SUB) {
|
||||
// If neither of this topic or the debug topic have a subscriber
|
||||
// then we dont need to do any work
|
||||
return;
|
||||
mgmt_event_post2(topic, data0, data1, debug, sub, fn);
|
||||
}
|
||||
|
||||
char buf_space[100];
|
||||
strbuf_t *buf;
|
||||
STRBUF_INIT(buf, buf_space);
|
||||
|
||||
char *tag;
|
||||
if(sub->type == N2N_MGMT_SUB) {
|
||||
tag = sub->tag;
|
||||
} else {
|
||||
tag = debug->tag;
|
||||
}
|
||||
|
||||
size_t msg_len = mgmt_events[topic](buf, tag, data0, data1);
|
||||
|
||||
if(sub->type == N2N_MGMT_SUB) {
|
||||
send_reply(sub, buf, msg_len);
|
||||
}
|
||||
if(debug->type == N2N_MGMT_SUB) {
|
||||
send_reply(debug, buf, msg_len);
|
||||
}
|
||||
}
|
||||
|
||||
static void mgmt_help_events (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||
size_t msg_len;
|
||||
|
||||
static void mgmt_help_events (mgmt_req_t *req, strbuf_t *buf) {
|
||||
int i;
|
||||
int nr_handlers = sizeof(mgmt_event_names) / sizeof(mgmt_events_t);
|
||||
for( i=0; i < nr_handlers; i++ ) {
|
||||
int topic = mgmt_event_names[i].topic;
|
||||
mgmt_req_t *sub = &mgmt_event_subscribers[topic];
|
||||
char host[40];
|
||||
char serv[6];
|
||||
|
||||
if((sub->type != N2N_MGMT_SUB) ||
|
||||
getnameinfo((struct sockaddr *)&sub->sender_sock, sizeof(sub->sender_sock),
|
||||
host, sizeof(host),
|
||||
serv, sizeof(serv),
|
||||
NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
|
||||
host[0] = '?';
|
||||
host[1] = 0;
|
||||
serv[0] = '?';
|
||||
serv[1] = 0;
|
||||
}
|
||||
|
||||
// TODO: handle a topic with no subscribers more cleanly
|
||||
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"topic\":\"%s\","
|
||||
"\"tag\":\"%s\","
|
||||
"\"sockaddr\":\"%s:%s\","
|
||||
"\"help\":\"%s\"}\n",
|
||||
req->tag,
|
||||
mgmt_event_names[i].cmd,
|
||||
sub->tag,
|
||||
host, serv,
|
||||
mgmt_event_names[i].help);
|
||||
|
||||
send_reply(req, buf, msg_len);
|
||||
mgmt_help_events_row(req, buf, sub, mgmt_event_names[i].cmd, mgmt_event_names[i].help);
|
||||
}
|
||||
}
|
||||
|
||||
static void mgmt_help (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv) {
|
||||
size_t msg_len;
|
||||
|
||||
// TODO: want to keep the mgmt_handlers defintion const static, otherwise
|
||||
// this whole function could be shared
|
||||
static void mgmt_help (mgmt_req_t *req, strbuf_t *buf) {
|
||||
/*
|
||||
* Even though this command is readonly, we deliberately do not check
|
||||
* the type - allowing help replies to both read and write requests
|
||||
|
@ -491,60 +311,14 @@ static void mgmt_help (mgmt_req_t *req, strbuf_t *buf, char *argv0, char *argv)
|
|||
int i;
|
||||
int nr_handlers = sizeof(mgmt_handlers) / sizeof(mgmt_handler_t);
|
||||
for( i=0; i < nr_handlers; i++ ) {
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"cmd\":\"%s\","
|
||||
"\"help\":\"%s\"}\n",
|
||||
req->tag,
|
||||
mgmt_handlers[i].cmd,
|
||||
mgmt_handlers[i].help);
|
||||
|
||||
send_reply(req, buf, msg_len);
|
||||
mgmt_help_row(req, buf, mgmt_handlers[i].cmd, mgmt_handlers[i].help);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the user is authorised for this command.
|
||||
* - this should be more configurable!
|
||||
* - for the moment we use some simple heuristics:
|
||||
* Reads are not dangerous, so they are simply allowed
|
||||
* Writes are possibly dangerous, so they need a fake password
|
||||
*/
|
||||
static int mgmt_auth (mgmt_req_t *req, char *auth, char *argv0, char *argv) {
|
||||
|
||||
if(auth) {
|
||||
/* If we have an auth key, it must match */
|
||||
if(req->eee->conf.mgmt_password_hash == pearson_hash_64((uint8_t*)auth, strlen(auth))) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* if we dont have an auth key, we can still read */
|
||||
if(req->type == N2N_MGMT_READ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
|
||||
|
||||
strbuf_t *buf;
|
||||
char cmdlinebuf[80];
|
||||
char *typechar;
|
||||
char *options;
|
||||
char *argv0;
|
||||
char *argv;
|
||||
char *flagstr;
|
||||
int flags;
|
||||
char *auth;
|
||||
|
||||
/* Initialise the tag field until we extract it from the cmdline */
|
||||
req->tag[0] = '-';
|
||||
req->tag[1] = '1';
|
||||
req->tag[2] = '\0';
|
||||
|
||||
/* save a copy of the commandline before we reuse the udp_buf */
|
||||
strncpy(cmdlinebuf, udp_buf, sizeof(cmdlinebuf)-1);
|
||||
|
@ -555,71 +329,11 @@ static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
|
|||
/* we reuse the buffer already on the stack for all our strings */
|
||||
STRBUF_INIT(buf, udp_buf);
|
||||
|
||||
typechar = strtok(cmdlinebuf, " \r\n");
|
||||
if(!typechar) {
|
||||
/* should not happen */
|
||||
mgmt_error(req, buf, "notype");
|
||||
return;
|
||||
}
|
||||
if(*typechar == 'r') {
|
||||
req->type=N2N_MGMT_READ;
|
||||
} else if(*typechar == 'w') {
|
||||
req->type=N2N_MGMT_WRITE;
|
||||
} else if(*typechar == 's') {
|
||||
req->type=N2N_MGMT_SUB;
|
||||
} else {
|
||||
mgmt_error(req, buf, "badtype");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Extract the tag to use in all reply packets */
|
||||
options = strtok(NULL, " \r\n");
|
||||
if(!options) {
|
||||
mgmt_error(req, buf, "nooptions");
|
||||
return;
|
||||
}
|
||||
|
||||
argv0 = strtok(NULL, " \r\n");
|
||||
if(!argv0) {
|
||||
mgmt_error(req, buf, "nocmd");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The entire rest of the line is the argv. We apply no processing
|
||||
* or arg separation so that the cmd can use it however it needs.
|
||||
*/
|
||||
argv = strtok(NULL, "\r\n");
|
||||
|
||||
/*
|
||||
* There might be an auth token mixed in with the tag
|
||||
*/
|
||||
char *tagp = strtok(options, ":");
|
||||
strncpy(req->tag, tagp, sizeof(req->tag)-1);
|
||||
req->tag[sizeof(req->tag)-1] = '\0';
|
||||
|
||||
flagstr = strtok(NULL, ":");
|
||||
if(flagstr) {
|
||||
flags = strtoul(flagstr, NULL, 16);
|
||||
} else {
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
/* Only 1 flag bit defined at the moment - "auth option present" */
|
||||
if(flags & 1) {
|
||||
auth = strtok(NULL, ":");
|
||||
} else {
|
||||
auth = NULL;
|
||||
}
|
||||
|
||||
if(!mgmt_auth(req, auth, argv0, argv)) {
|
||||
mgmt_error(req, buf, "badauth");
|
||||
return;
|
||||
}
|
||||
mgmt_req_init2(req, buf, (char *)&cmdlinebuf);
|
||||
|
||||
if(req->type == N2N_MGMT_SUB) {
|
||||
int handler;
|
||||
lookup_handler(handler, mgmt_event_names, argv0);
|
||||
lookup_handler(handler, mgmt_event_names, req->argv0);
|
||||
if(handler == -1) {
|
||||
mgmt_error(req, buf, "unknowntopic");
|
||||
return;
|
||||
|
@ -628,18 +342,18 @@ static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
|
|||
int topic = mgmt_event_names[handler].topic;
|
||||
if(mgmt_event_subscribers[topic].type == N2N_MGMT_SUB) {
|
||||
send_json_1str(&mgmt_event_subscribers[topic], buf,
|
||||
"unsubscribed", "topic", argv0);
|
||||
send_json_1str(req, buf, "replacing", "topic", argv0);
|
||||
"unsubscribed", "topic", req->argv0);
|
||||
send_json_1str(req, buf, "replacing", "topic", req->argv0);
|
||||
}
|
||||
|
||||
memcpy(&mgmt_event_subscribers[topic], req, sizeof(*req));
|
||||
|
||||
send_json_1str(req, buf, "subscribe", "topic", argv0);
|
||||
send_json_1str(req, buf, "subscribe", "topic", req->argv0);
|
||||
return;
|
||||
}
|
||||
|
||||
int handler;
|
||||
lookup_handler(handler, mgmt_handlers, argv0);
|
||||
lookup_handler(handler, mgmt_handlers, req->argv0);
|
||||
if(handler == -1) {
|
||||
mgmt_error(req, buf, "unknowncmd");
|
||||
return;
|
||||
|
@ -656,11 +370,11 @@ static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
|
|||
* that make our JSON invalid.
|
||||
* - do we care?
|
||||
*/
|
||||
send_json_1str(req, buf, "begin", "cmd", argv0);
|
||||
send_json_1str(req, buf, "begin", "cmd", req->argv0);
|
||||
|
||||
mgmt_handlers[handler].func(req, buf, argv0, argv);
|
||||
mgmt_handlers[handler].func(req, buf);
|
||||
|
||||
send_json_1str(req, buf, "end", "cmd", argv0);
|
||||
send_json_1str(req, buf, "end", "cmd", req->argv0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -688,7 +402,11 @@ void readFromMgmtSocket (n2n_edge_t *eee) {
|
|||
uint32_t num = 0;
|
||||
selection_criterion_str_t sel_buf;
|
||||
|
||||
req.sss = NULL;
|
||||
req.eee = eee;
|
||||
req.mgmt_sock = eee->udp_mgmt_sock;
|
||||
req.keep_running = eee->keep_running;
|
||||
req.mgmt_password_hash = eee->conf.mgmt_password_hash;
|
||||
|
||||
now = time(NULL);
|
||||
i = sizeof(req.sender_sock);
|
||||
|
|
280
src/management.c
Normal file
280
src/management.c
Normal file
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* Common routines shared between the management interfaces
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#ifndef WIN32
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
// TODO: move logging defs in their own header and include that
|
||||
void setTraceLevel (int level);
|
||||
int getTraceLevel ();
|
||||
|
||||
#include <pearson.h>
|
||||
#include "management.h"
|
||||
|
||||
#include "n2n.h" // for traceEvent and friends
|
||||
|
||||
ssize_t send_reply (mgmt_req_t *req, strbuf_t *buf, size_t msg_len) {
|
||||
// TODO: better error handling (counters?)
|
||||
return sendto(req->mgmt_sock, buf->str, msg_len, 0,
|
||||
(struct sockaddr *) &req->sender_sock, sizeof(struct sockaddr_in));
|
||||
}
|
||||
|
||||
size_t gen_json_1str (strbuf_t *buf, char *tag, char *_type, char *key, char *val) {
|
||||
return snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"%s\","
|
||||
"\"%s\":\"%s\"}\n",
|
||||
tag,
|
||||
_type,
|
||||
key,
|
||||
val);
|
||||
}
|
||||
|
||||
size_t gen_json_1uint (strbuf_t *buf, char *tag, char *_type, char *key, unsigned int val) {
|
||||
return snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"%s\","
|
||||
"\"%s\":%u}\n",
|
||||
tag,
|
||||
_type,
|
||||
key,
|
||||
val);
|
||||
}
|
||||
|
||||
void send_json_1str (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, char *val) {
|
||||
size_t msg_len = gen_json_1str(buf, req->tag, _type, key, val);
|
||||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
|
||||
void send_json_1uint (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, unsigned int val) {
|
||||
size_t msg_len = gen_json_1uint(buf, req->tag, _type, key, val);
|
||||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
|
||||
void mgmt_error (mgmt_req_t *req, strbuf_t *buf, char *msg) {
|
||||
send_json_1str(req, buf, "error", "error", msg);
|
||||
}
|
||||
|
||||
void mgmt_stop (mgmt_req_t *req, strbuf_t *buf) {
|
||||
|
||||
if(req->type==N2N_MGMT_WRITE) {
|
||||
*req->keep_running = 0;
|
||||
}
|
||||
|
||||
send_json_1uint(req, buf, "row", "keep_running", *req->keep_running);
|
||||
}
|
||||
|
||||
void mgmt_verbose (mgmt_req_t *req, strbuf_t *buf) {
|
||||
|
||||
if(req->type==N2N_MGMT_WRITE) {
|
||||
if(req->argv) {
|
||||
setTraceLevel(strtoul(req->argv, NULL, 0));
|
||||
}
|
||||
}
|
||||
|
||||
send_json_1uint(req, buf, "row", "traceLevel", getTraceLevel());
|
||||
}
|
||||
|
||||
void mgmt_unimplemented (mgmt_req_t *req, strbuf_t *buf) {
|
||||
|
||||
mgmt_error(req, buf, "unimplemented");
|
||||
}
|
||||
|
||||
void mgmt_event_post2 (enum n2n_event_topic topic, int data0, void *data1, mgmt_req_t *debug, mgmt_req_t *sub, mgmt_event_handler_t fn) {
|
||||
traceEvent(TRACE_DEBUG, "post topic=%i data0=%i", topic, data0);
|
||||
|
||||
if( sub->type != N2N_MGMT_SUB && debug->type != N2N_MGMT_SUB) {
|
||||
// If neither of this topic or the debug topic have a subscriber
|
||||
// then we dont need to do any work
|
||||
return;
|
||||
}
|
||||
|
||||
char buf_space[100];
|
||||
strbuf_t *buf;
|
||||
STRBUF_INIT(buf, buf_space);
|
||||
|
||||
char *tag;
|
||||
if(sub->type == N2N_MGMT_SUB) {
|
||||
tag = sub->tag;
|
||||
} else {
|
||||
tag = debug->tag;
|
||||
}
|
||||
|
||||
size_t msg_len = fn(buf, tag, data0, data1);
|
||||
|
||||
if(sub->type == N2N_MGMT_SUB) {
|
||||
send_reply(sub, buf, msg_len);
|
||||
}
|
||||
if(debug->type == N2N_MGMT_SUB) {
|
||||
send_reply(debug, buf, msg_len);
|
||||
}
|
||||
// TODO:
|
||||
// - ideally, we would detect that the far end has gone away and
|
||||
// set the ->type back to N2N_MGMT_UNKNOWN, but we are not using
|
||||
// a connected socket, so that is difficult
|
||||
// - failing that, we should require the client to send an unsubscribe
|
||||
// and provide a manual unsubscribe
|
||||
}
|
||||
|
||||
void mgmt_help_row (mgmt_req_t *req, strbuf_t *buf, char *cmd, char *help) {
|
||||
size_t msg_len;
|
||||
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"cmd\":\"%s\","
|
||||
"\"help\":\"%s\"}\n",
|
||||
req->tag,
|
||||
cmd,
|
||||
help);
|
||||
|
||||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
|
||||
void mgmt_help_events_row (mgmt_req_t *req, strbuf_t *buf, mgmt_req_t *sub, char *cmd, char *help) {
|
||||
size_t msg_len;
|
||||
char host[40];
|
||||
char serv[6];
|
||||
|
||||
if((sub->type != N2N_MGMT_SUB) ||
|
||||
getnameinfo((struct sockaddr *)&sub->sender_sock, sizeof(sub->sender_sock),
|
||||
host, sizeof(host),
|
||||
serv, sizeof(serv),
|
||||
NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
|
||||
host[0] = '?';
|
||||
host[1] = 0;
|
||||
serv[0] = '?';
|
||||
serv[1] = 0;
|
||||
}
|
||||
|
||||
// TODO: handle a topic with no subscribers more cleanly
|
||||
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"topic\":\"%s\","
|
||||
"\"tag\":\"%s\","
|
||||
"\"sockaddr\":\"%s:%s\","
|
||||
"\"help\":\"%s\"}\n",
|
||||
req->tag,
|
||||
cmd,
|
||||
sub->tag,
|
||||
host, serv,
|
||||
help);
|
||||
|
||||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
|
||||
// TODO: work out a method to keep the mgmt_handlers defintion const static,
|
||||
// and then import the shared mgmt_help () definition to this file
|
||||
|
||||
/*
|
||||
* Check if the user is authorised for this command.
|
||||
* - this should be more configurable!
|
||||
* - for the moment we use some simple heuristics:
|
||||
* Reads are not dangerous, so they are simply allowed
|
||||
* Writes are possibly dangerous, so they need a fake password
|
||||
*/
|
||||
int mgmt_auth (mgmt_req_t *req, char *auth) {
|
||||
|
||||
if(auth) {
|
||||
/* If we have an auth key, it must match */
|
||||
if(req->mgmt_password_hash == pearson_hash_64((uint8_t*)auth, strlen(auth))) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* if we dont have an auth key, we can still read */
|
||||
if(req->type == N2N_MGMT_READ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the common and shred parts of the mgmt_req_t initialisation
|
||||
*/
|
||||
void mgmt_req_init2 (mgmt_req_t *req, strbuf_t *buf, char *cmdline) {
|
||||
char *typechar;
|
||||
char *options;
|
||||
char *flagstr;
|
||||
int flags;
|
||||
char *auth;
|
||||
|
||||
/* Initialise the tag field until we extract it from the cmdline */
|
||||
req->tag[0] = '-';
|
||||
req->tag[1] = '1';
|
||||
req->tag[2] = '\0';
|
||||
|
||||
typechar = strtok(cmdline, " \r\n");
|
||||
if(!typechar) {
|
||||
/* should not happen */
|
||||
mgmt_error(req, buf, "notype");
|
||||
return;
|
||||
}
|
||||
if(*typechar == 'r') {
|
||||
req->type=N2N_MGMT_READ;
|
||||
} else if(*typechar == 'w') {
|
||||
req->type=N2N_MGMT_WRITE;
|
||||
} else if(*typechar == 's') {
|
||||
req->type=N2N_MGMT_SUB;
|
||||
} else {
|
||||
mgmt_error(req, buf, "badtype");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Extract the tag to use in all reply packets */
|
||||
options = strtok(NULL, " \r\n");
|
||||
if(!options) {
|
||||
mgmt_error(req, buf, "nooptions");
|
||||
return;
|
||||
}
|
||||
|
||||
req->argv0 = strtok(NULL, " \r\n");
|
||||
if(!req->argv0) {
|
||||
mgmt_error(req, buf, "nocmd");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The entire rest of the line is the argv. We apply no processing
|
||||
* or arg separation so that the cmd can use it however it needs.
|
||||
*/
|
||||
req->argv = strtok(NULL, "\r\n");
|
||||
|
||||
/*
|
||||
* There might be an auth token mixed in with the tag
|
||||
*/
|
||||
char *tagp = strtok(options, ":");
|
||||
strncpy(req->tag, tagp, sizeof(req->tag)-1);
|
||||
req->tag[sizeof(req->tag)-1] = '\0';
|
||||
|
||||
flagstr = strtok(NULL, ":");
|
||||
if(flagstr) {
|
||||
flags = strtoul(flagstr, NULL, 16);
|
||||
} else {
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
/* Only 1 flag bit defined at the moment - "auth option present" */
|
||||
if(flags & 1) {
|
||||
auth = strtok(NULL, ":");
|
||||
} else {
|
||||
auth = NULL;
|
||||
}
|
||||
|
||||
if(!mgmt_auth(req, auth)) {
|
||||
mgmt_error(req, buf, "badauth");
|
||||
return;
|
||||
}
|
||||
}
|
100
src/management.h
Normal file
100
src/management.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Internal interface definitions for the management interfaces
|
||||
*
|
||||
* This header is not part of the public library API and is thus not in
|
||||
* the public include folder
|
||||
*/
|
||||
|
||||
#ifndef MANAGEMENT_H
|
||||
#define MANAGEMENT_H 1
|
||||
|
||||
#include <n2n_typedefs.h> // For the n2n_edge_t and n2n_sn_t defs
|
||||
|
||||
#include "strbuf.h"
|
||||
|
||||
enum n2n_mgmt_type {
|
||||
N2N_MGMT_UNKNOWN = 0,
|
||||
N2N_MGMT_READ = 1,
|
||||
N2N_MGMT_WRITE = 2,
|
||||
N2N_MGMT_SUB = 3,
|
||||
};
|
||||
|
||||
/*
|
||||
* Everything needed to reply to a request
|
||||
*
|
||||
* TODO:
|
||||
* - one day, we might be able to merge the sss and eee members
|
||||
* - once eee and sss are merged, some fields should migrate back into it:
|
||||
* - mgmt_sock
|
||||
* - keep_running
|
||||
* - mgmt_password_hash
|
||||
*/
|
||||
typedef struct mgmt_req {
|
||||
n2n_sn_t *sss;
|
||||
n2n_edge_t *eee;
|
||||
int mgmt_sock; // socket replies come from
|
||||
int *keep_running;
|
||||
uint64_t mgmt_password_hash;
|
||||
enum n2n_mgmt_type type;
|
||||
char *argv0;
|
||||
char *argv;
|
||||
char tag[10];
|
||||
struct sockaddr_in sender_sock;
|
||||
} mgmt_req_t;
|
||||
|
||||
/*
|
||||
* Read/Write handlers are defined in this structure
|
||||
* TODO: DRY
|
||||
*/
|
||||
#define FLAG_WROK 1
|
||||
typedef struct mgmt_handler {
|
||||
int flags;
|
||||
char *cmd;
|
||||
char *help;
|
||||
void (*func)(mgmt_req_t *req, strbuf_t *buf);
|
||||
} mgmt_handler_t;
|
||||
|
||||
/*
|
||||
* Event topic names are defined in this structure
|
||||
*/
|
||||
typedef struct mgmt_events {
|
||||
enum n2n_event_topic topic;
|
||||
char *cmd;
|
||||
char *help;
|
||||
} mgmt_events_t;
|
||||
|
||||
typedef size_t (mgmt_event_handler_t)(strbuf_t *buf, char *tag, int data0, void *data1);
|
||||
|
||||
// Lookup the index of matching argv0 in a cmd list
|
||||
// store index in "Result", or -1 for not found
|
||||
#define lookup_handler(Result, list, argv0) do { \
|
||||
int nr_max = sizeof(list) / sizeof(list[0]); \
|
||||
for( Result=0; Result < nr_max; Result++ ) { \
|
||||
if(0 == strcmp(list[Result].cmd, argv0)) { \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
if( Result >= nr_max ) { \
|
||||
Result = -1; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
ssize_t send_reply (mgmt_req_t *req, strbuf_t *buf, size_t msg_len);
|
||||
size_t gen_json_1str (strbuf_t *buf, char *tag, char *_type, char *key, char *val);
|
||||
size_t gen_json_1uint (strbuf_t *buf, char *tag, char *_type, char *key, unsigned int val);
|
||||
void send_json_1str (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, char *val);
|
||||
void send_json_1uint (mgmt_req_t *req, strbuf_t *buf, char *_type, char *key, unsigned int val);
|
||||
|
||||
void mgmt_error (mgmt_req_t *req, strbuf_t *buf, char *msg);
|
||||
|
||||
void mgmt_stop (mgmt_req_t *req, strbuf_t *buf);
|
||||
void mgmt_verbose (mgmt_req_t *req, strbuf_t *buf);
|
||||
void mgmt_unimplemented (mgmt_req_t *req, strbuf_t *buf);
|
||||
|
||||
void mgmt_event_post2 (enum n2n_event_topic topic, int data0, void *data1, mgmt_req_t *debug, mgmt_req_t *sub, mgmt_event_handler_t fn);
|
||||
void mgmt_help_row (mgmt_req_t *req, strbuf_t *buf, char *cmd, char *help);
|
||||
void mgmt_help_events_row (mgmt_req_t *req, strbuf_t *buf, mgmt_req_t *sub, char *cmd, char *help);
|
||||
int mgmt_auth (mgmt_req_t *req, char *auth);
|
||||
void mgmt_req_init2 (mgmt_req_t *req, strbuf_t *buf, char *cmdline);
|
||||
|
||||
#endif
|
|
@ -24,186 +24,106 @@
|
|||
#include "n2n.h"
|
||||
#include "edge_utils_win32.h"
|
||||
|
||||
#include "strbuf.h"
|
||||
#include "management.h"
|
||||
|
||||
int load_allowed_sn_community (n2n_sn_t *sss); /* defined in sn_utils.c */
|
||||
|
||||
enum n2n_mgmt_type {
|
||||
N2N_MGMT_READ = 0,
|
||||
N2N_MGMT_WRITE = 1,
|
||||
};
|
||||
static void mgmt_reload_communities (mgmt_req_t *req, strbuf_t *buf) {
|
||||
|
||||
#define FLAG_WROK 1
|
||||
typedef struct mgmt_handler {
|
||||
int flags;
|
||||
char *cmd;
|
||||
char *help;
|
||||
void (*func)(n2n_sn_t *sss, char *udp_buf, struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv);
|
||||
} mgmt_handler_t;
|
||||
|
||||
static void mgmt_error (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, char *tag, char *msg) {
|
||||
size_t msg_len;
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"error\","
|
||||
"\"error\":\"%s\"}\n",
|
||||
tag,
|
||||
msg);
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
}
|
||||
|
||||
static void mgmt_stop (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
|
||||
size_t msg_len;
|
||||
|
||||
if(type==N2N_MGMT_WRITE) {
|
||||
*sss->keep_running = 0;
|
||||
}
|
||||
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"keep_running\":%u}\n",
|
||||
tag,
|
||||
*sss->keep_running);
|
||||
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
}
|
||||
|
||||
static void mgmt_verbose (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
|
||||
size_t msg_len;
|
||||
|
||||
if(type==N2N_MGMT_WRITE) {
|
||||
if(argv) {
|
||||
setTraceLevel(strtoul(argv, NULL, 0));
|
||||
}
|
||||
}
|
||||
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"traceLevel\":%u}\n",
|
||||
tag,
|
||||
getTraceLevel());
|
||||
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
}
|
||||
|
||||
static void mgmt_reload_communities (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
|
||||
size_t msg_len;
|
||||
|
||||
if(type!=N2N_MGMT_WRITE) {
|
||||
mgmt_error(sss, udp_buf, sender_sock, tag, "writeonly");
|
||||
if(req->type!=N2N_MGMT_WRITE) {
|
||||
mgmt_error(req, buf, "writeonly");
|
||||
return;
|
||||
}
|
||||
|
||||
if(!sss->community_file) {
|
||||
mgmt_error(sss, udp_buf, sender_sock, tag, "nofile");
|
||||
if(!req->sss->community_file) {
|
||||
mgmt_error(req, buf, "nofile");
|
||||
return;
|
||||
}
|
||||
|
||||
int ok = load_allowed_sn_community(sss);
|
||||
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"ok\":%i}\n",
|
||||
tag,
|
||||
ok);
|
||||
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
int ok = load_allowed_sn_community(req->sss);
|
||||
send_json_1uint(req, buf, "row", "ok", ok);
|
||||
}
|
||||
|
||||
static void mgmt_timestamps (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
|
||||
static void mgmt_timestamps (mgmt_req_t *req, strbuf_t *buf) {
|
||||
size_t msg_len;
|
||||
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"start_time\":%lu,"
|
||||
"\"last_fwd\":%ld,"
|
||||
"\"last_reg_super\":%ld}\n",
|
||||
tag,
|
||||
sss->start_time,
|
||||
sss->stats.last_fwd,
|
||||
sss->stats.last_reg_super);
|
||||
req->tag,
|
||||
req->sss->start_time,
|
||||
req->sss->stats.last_fwd,
|
||||
req->sss->stats.last_reg_super);
|
||||
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
|
||||
static void mgmt_packetstats (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
|
||||
static void mgmt_packetstats (mgmt_req_t *req, strbuf_t *buf) {
|
||||
size_t msg_len;
|
||||
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"type\":\"forward\","
|
||||
"\"tx_pkt\":%lu}\n",
|
||||
tag,
|
||||
sss->stats.fwd);
|
||||
req->tag,
|
||||
req->sss->stats.fwd);
|
||||
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
send_reply(req, buf, msg_len);
|
||||
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"type\":\"broadcast\","
|
||||
"\"tx_pkt\":%lu}\n",
|
||||
tag,
|
||||
sss->stats.broadcast);
|
||||
req->tag,
|
||||
req->sss->stats.broadcast);
|
||||
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
send_reply(req, buf, msg_len);
|
||||
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"type\":\"reg_super\","
|
||||
"\"rx_pkt\":%lu,"
|
||||
"\"nak\":%lu}\n",
|
||||
tag,
|
||||
sss->stats.reg_super,
|
||||
sss->stats.reg_super_nak);
|
||||
req->tag,
|
||||
req->sss->stats.reg_super,
|
||||
req->sss->stats.reg_super_nak);
|
||||
|
||||
/* Note: reg_super_nak is not currently incremented anywhere */
|
||||
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
send_reply(req, buf, msg_len);
|
||||
|
||||
/* Generic errors when trying to sendto() */
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"type\":\"errors\","
|
||||
"\"tx_pkt\":%lu}\n",
|
||||
tag,
|
||||
sss->stats.errors);
|
||||
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
req->tag,
|
||||
req->sss->stats.errors);
|
||||
|
||||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
|
||||
static void mgmt_communities (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
|
||||
static void mgmt_communities (mgmt_req_t *req, strbuf_t *buf) {
|
||||
size_t msg_len;
|
||||
struct sn_community *community, *tmp;
|
||||
dec_ip_bit_str_t ip_bit_str = {'\0'};
|
||||
|
||||
HASH_ITER(hh, sss->communities, community, tmp) {
|
||||
HASH_ITER(hh, req->sss->communities, community, tmp) {
|
||||
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
|
@ -211,19 +131,17 @@ static void mgmt_communities (n2n_sn_t *sss, char *udp_buf, const struct sockadd
|
|||
"\"purgeable\":%i,"
|
||||
"\"is_federation\":%i,"
|
||||
"\"ip4addr\":\"%s\"}\n",
|
||||
tag,
|
||||
req->tag,
|
||||
(community->is_federation) ? "-/-" : community->community,
|
||||
community->purgeable,
|
||||
community->is_federation,
|
||||
(community->auto_ip_net.net_addr == 0) ? "" : ip_subnet_to_str(ip_bit_str, &community->auto_ip_net));
|
||||
|
||||
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
}
|
||||
|
||||
static void mgmt_edges (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
|
||||
static void mgmt_edges (mgmt_req_t *req, strbuf_t *buf) {
|
||||
size_t msg_len;
|
||||
struct sn_community *community, *tmp;
|
||||
struct peer_info *peer, *tmpPeer;
|
||||
|
@ -231,10 +149,10 @@ static void mgmt_edges (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in s
|
|||
n2n_sock_str_t sockbuf;
|
||||
dec_ip_bit_str_t ip_bit_str = {'\0'};
|
||||
|
||||
HASH_ITER(hh, sss->communities, community, tmp) {
|
||||
HASH_ITER(hh, req->sss->communities, community, tmp) {
|
||||
HASH_ITER(hh, community->edges, peer, tmpPeer) {
|
||||
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
msg_len = snprintf(buf->str, buf->size,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
|
@ -246,29 +164,25 @@ static void mgmt_edges (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in s
|
|||
"\"proto\":\"%s\","
|
||||
"\"desc\":\"%s\","
|
||||
"\"last_seen\":%li}\n",
|
||||
tag,
|
||||
req->tag,
|
||||
(community->is_federation) ? "-/-" : community->community,
|
||||
(peer->dev_addr.net_addr == 0) ? "" : ip_subnet_to_str(ip_bit_str, &peer->dev_addr),
|
||||
peer->purgeable,
|
||||
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac_buf, peer->mac_addr),
|
||||
sock_to_cstr(sockbuf, &(peer->sock)),
|
||||
((peer->socket_fd >= 0) && (peer->socket_fd != sss->sock)) ? "TCP" : "UDP",
|
||||
((peer->socket_fd >= 0) && (peer->socket_fd != req->sss->sock)) ? "TCP" : "UDP",
|
||||
peer->dev_desc,
|
||||
peer->last_seen);
|
||||
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
send_reply(req, buf, msg_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mgmt_unimplemented (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
|
||||
mgmt_error(sss, udp_buf, sender_sock, tag, "unimplemented");
|
||||
}
|
||||
// Forward define so we can include this in the mgmt_handlers[] table
|
||||
static void mgmt_help (mgmt_req_t *req, strbuf_t *buf);
|
||||
|
||||
static void mgmt_help (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv);
|
||||
|
||||
mgmt_handler_t mgmt_handlers[] = {
|
||||
static const mgmt_handler_t mgmt_handlers[] = {
|
||||
{ .cmd = "supernodes", .help = "Reserved for edge", .func = mgmt_unimplemented},
|
||||
|
||||
{ .cmd = "stop", .flags = FLAG_WROK, .help = "Gracefully exit edge", .func = mgmt_stop},
|
||||
|
@ -279,72 +193,28 @@ mgmt_handler_t mgmt_handlers[] = {
|
|||
{ .cmd = "timestamps", .help = "Event timestamps", .func = mgmt_timestamps},
|
||||
{ .cmd = "packetstats", .help = "Traffic statistics", .func = mgmt_packetstats},
|
||||
{ .cmd = "help", .flags = FLAG_WROK, .help = "Show JSON commands", .func = mgmt_help},
|
||||
{ .cmd = NULL },
|
||||
};
|
||||
|
||||
static void mgmt_help (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *tag, char *argv0, char *argv) {
|
||||
size_t msg_len;
|
||||
mgmt_handler_t *handler;
|
||||
|
||||
// TODO: want to keep the mgmt_handlers defintion const static, otherwise
|
||||
// this whole function could be shared
|
||||
static void mgmt_help (mgmt_req_t *req, strbuf_t *buf) {
|
||||
/*
|
||||
* Even though this command is readonly, we deliberately do not check
|
||||
* the type - allowing help replies to both read and write requests
|
||||
*/
|
||||
|
||||
for( handler=mgmt_handlers; handler->cmd; handler++ ) {
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
"{"
|
||||
"\"_tag\":\"%s\","
|
||||
"\"_type\":\"row\","
|
||||
"\"cmd\":\"%s\","
|
||||
"\"help\":\"%s\"}\n",
|
||||
tag,
|
||||
handler->cmd,
|
||||
handler->help);
|
||||
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
int i;
|
||||
int nr_handlers = sizeof(mgmt_handlers) / sizeof(mgmt_handler_t);
|
||||
for( i=0; i < nr_handlers; i++ ) {
|
||||
mgmt_help_row(req, buf, mgmt_handlers[i].cmd, mgmt_handlers[i].help);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the user is authorised for this command.
|
||||
* - this should be more configurable!
|
||||
* - for the moment we use some simple heuristics:
|
||||
* Reads are not dangerous, so they are simply allowed
|
||||
* Writes are possibly dangerous, so they need a fake password
|
||||
*/
|
||||
static int mgmt_auth (n2n_sn_t *sss, const struct sockaddr_in sender_sock, enum n2n_mgmt_type type, char *auth, char *argv0, char *argv) {
|
||||
|
||||
if(auth) {
|
||||
/* If we have an auth key, it must match */
|
||||
if(sss->mgmt_password_hash == pearson_hash_64((uint8_t*)auth, strlen(auth))) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/* if we dont have an auth key, we can still read */
|
||||
if(type == N2N_MGMT_READ) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void handleMgmtJson (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_in sender_sock) {
|
||||
// TODO: DRY
|
||||
static void handleMgmtJson (mgmt_req_t *req, char *udp_buf, const int recvlen) {
|
||||
|
||||
strbuf_t *buf;
|
||||
char cmdlinebuf[80];
|
||||
enum n2n_mgmt_type type;
|
||||
char *typechar;
|
||||
char *options;
|
||||
char *argv0;
|
||||
char *argv;
|
||||
char *tag;
|
||||
char *flagstr;
|
||||
int flags;
|
||||
char *auth;
|
||||
mgmt_handler_t *handler;
|
||||
size_t msg_len;
|
||||
|
||||
/* save a copy of the commandline before we reuse the udp_buf */
|
||||
strncpy(cmdlinebuf, udp_buf, sizeof(cmdlinebuf)-1);
|
||||
|
@ -352,76 +222,21 @@ static void handleMgmtJson (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_
|
|||
|
||||
traceEvent(TRACE_DEBUG, "mgmt json %s", cmdlinebuf);
|
||||
|
||||
typechar = strtok(cmdlinebuf, " \r\n");
|
||||
if(!typechar) {
|
||||
/* should not happen */
|
||||
mgmt_error(sss, udp_buf, sender_sock, "-1", "notype");
|
||||
return;
|
||||
}
|
||||
if(*typechar == 'r') {
|
||||
type=N2N_MGMT_READ;
|
||||
} else if(*typechar == 'w') {
|
||||
type=N2N_MGMT_WRITE;
|
||||
} else {
|
||||
/* dunno how we got here */
|
||||
mgmt_error(sss, udp_buf, sender_sock, "-1", "badtype");
|
||||
/* we reuse the buffer already on the stack for all our strings */
|
||||
// xx
|
||||
STRBUF_INIT(buf, udp_buf);
|
||||
|
||||
mgmt_req_init2(req, buf, (char *)&cmdlinebuf);
|
||||
|
||||
int handler;
|
||||
lookup_handler(handler, mgmt_handlers, req->argv0);
|
||||
if(handler == -1) {
|
||||
mgmt_error(req, buf, "unknowncmd");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Extract the tag to use in all reply packets */
|
||||
options = strtok(NULL, " \r\n");
|
||||
if(!options) {
|
||||
mgmt_error(sss, udp_buf, sender_sock, "-1", "nooptions");
|
||||
return;
|
||||
}
|
||||
|
||||
argv0 = strtok(NULL, " \r\n");
|
||||
if(!argv0) {
|
||||
mgmt_error(sss, udp_buf, sender_sock, "-1", "nocmd");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The entire rest of the line is the argv. We apply no processing
|
||||
* or arg separation so that the cmd can use it however it needs.
|
||||
*/
|
||||
argv = strtok(NULL, "\r\n");
|
||||
|
||||
/*
|
||||
* There might be an auth token mixed in with the tag
|
||||
*/
|
||||
tag = strtok(options, ":");
|
||||
flagstr = strtok(NULL, ":");
|
||||
if(flagstr) {
|
||||
flags = strtoul(flagstr, NULL, 16);
|
||||
} else {
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
/* Only 1 flag bit defined at the moment - "auth option present" */
|
||||
if(flags & 1) {
|
||||
auth = strtok(NULL, ":");
|
||||
} else {
|
||||
auth = NULL;
|
||||
}
|
||||
|
||||
if(!mgmt_auth(sss, sender_sock, type, auth, argv0, argv)) {
|
||||
mgmt_error(sss, udp_buf, sender_sock, tag, "badauth");
|
||||
return;
|
||||
}
|
||||
|
||||
for( handler=mgmt_handlers; handler->cmd; handler++ ) {
|
||||
if(0 == strcmp(handler->cmd, argv0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!handler->cmd) {
|
||||
mgmt_error(sss, udp_buf, sender_sock, tag, "unknowncmd");
|
||||
return;
|
||||
}
|
||||
|
||||
if((type==N2N_MGMT_WRITE) && !(handler->flags & FLAG_WROK)) {
|
||||
mgmt_error(sss, udp_buf, sender_sock, tag, "readonly");
|
||||
if((req->type==N2N_MGMT_WRITE) && !(mgmt_handlers[handler].flags & FLAG_WROK)) {
|
||||
mgmt_error(req, buf, "readonly");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -431,17 +246,11 @@ static void handleMgmtJson (n2n_sn_t *sss, char *udp_buf, const struct sockaddr_
|
|||
* that make our JSON invalid.
|
||||
* - do we care?
|
||||
*/
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
"{\"_tag\":\"%s\",\"_type\":\"begin\",\"cmd\":\"%s\"}\n", tag, argv0);
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
send_json_1str(req, buf, "begin", "cmd", req->argv0);
|
||||
|
||||
handler->func(sss, udp_buf, sender_sock, type, tag, argv0, argv);
|
||||
mgmt_handlers[handler].func(req, buf);
|
||||
|
||||
msg_len = snprintf(udp_buf, N2N_PKT_BUF_SIZE,
|
||||
"{\"_tag\":\"%s\",\"_type\":\"end\"}\n", tag);
|
||||
sendto(sss->mgmt_sock, udp_buf, msg_len, 0,
|
||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in));
|
||||
send_json_1str(req, buf, "end", "cmd", req->argv0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -470,6 +279,7 @@ int process_mgmt (n2n_sn_t *sss,
|
|||
|
||||
char resbuf[N2N_SN_PKTBUF_SIZE];
|
||||
size_t ressize = 0;
|
||||
mgmt_req_t req;
|
||||
uint32_t num_edges = 0;
|
||||
uint32_t num_comm = 0;
|
||||
uint32_t num = 0;
|
||||
|
@ -482,6 +292,13 @@ int process_mgmt (n2n_sn_t *sss,
|
|||
|
||||
traceEvent(TRACE_DEBUG, "process_mgmt");
|
||||
|
||||
req.eee = NULL;
|
||||
req.sss = sss;
|
||||
req.mgmt_sock = sss->mgmt_sock;
|
||||
req.keep_running = sss->keep_running;
|
||||
req.mgmt_password_hash = sss->mgmt_password_hash;
|
||||
memcpy(&req.sender_sock, sender_sock, sizeof(req.sender_sock));
|
||||
|
||||
/* avoid parsing any uninitialized junk from the stack */
|
||||
mgmt_buf[mgmt_size] = 0;
|
||||
|
||||
|
@ -517,9 +334,9 @@ int process_mgmt (n2n_sn_t *sss,
|
|||
return 0; /* no status output afterwards */
|
||||
}
|
||||
|
||||
if((mgmt_buf[0] == 'r' || mgmt_buf[0] == 'w') && (mgmt_buf[1] == ' ')) {
|
||||
if((mgmt_buf[0] >= 'a' || mgmt_buf[0] <= 'z') && (mgmt_buf[1] == ' ')) {
|
||||
/* this is a JSON request */
|
||||
handleMgmtJson(sss, mgmt_buf, *sender_sock);
|
||||
handleMgmtJson(&req, mgmt_buf, mgmt_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
24
src/strbuf.h
Normal file
24
src/strbuf.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Internal interface definitions for the strbuf abstrction
|
||||
*
|
||||
* This header is not part of the public library API and is thus not in
|
||||
* the public include folder
|
||||
*/
|
||||
|
||||
#ifndef STRBUF_H
|
||||
#define STRBUF_H 1
|
||||
|
||||
typedef struct strbuf {
|
||||
size_t size;
|
||||
char str[];
|
||||
} strbuf_t;
|
||||
|
||||
// Initialise the strbuf pointer buf to point at the storage area p
|
||||
// p must be a known sized object
|
||||
#define STRBUF_INIT(buf,p) do { \
|
||||
buf = (void *)p; \
|
||||
buf->size = sizeof(*p) - sizeof(size_t); \
|
||||
} while(0)
|
||||
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user