Browse Source

API restriction policy and multicast announces (#16)

same as sgminer, fixed to build (tpruvot), untested yet
pull/2/head
bitbandi 8 years ago committed by Tanguy Pruvot
parent
commit
47472ed3f1
  1. 390
      api.cpp
  2. 56
      ccminer.cpp

390
api.cpp

@ -83,17 +83,23 @@ static struct IP4ACCESS *ipaccess = NULL; @@ -83,17 +83,23 @@ static struct IP4ACCESS *ipaccess = NULL;
#define SOCK_REC_BUFSZ 1024
#define QUEUE 10
#define ALLIP4 "0.0.0.0"
//#define ALLIP4 "0.0.0.0"
static const char *localaddr = "127.0.0.1";
static const char *UNAVAILABLE = " - API will not be available";
static const char *MUNAVAILABLE = " - API multicast listener will not be available";
static char *buffer = NULL;
static time_t startup = 0;
static int bye = 0;
extern char *opt_api_bind;
extern int opt_api_port;
extern char *opt_api_allow;
extern int opt_api_listen; /* port */
extern int opt_api_remote;
extern char *opt_api_groups;
extern bool opt_api_mcast;
extern char *opt_api_mcast_addr;
extern char *opt_api_mcast_code;
extern char *opt_api_mcast_des;
extern int opt_api_mcast_port;
// current stratum...
extern struct stratum_ctx stratum;
@ -416,15 +422,6 @@ static char *getmeminfo(char *params) @@ -416,15 +422,6 @@ static char *getmeminfo(char *params)
/*****************************************************************************/
/**
* Remote control allowed ?
* TODO: ip filters
*/
static bool check_remote_access(void)
{
return (opt_api_remote > 0);
}
/**
* Set pool by index (pools array in json config)
* switchpool|1|
@ -433,8 +430,6 @@ static char *remote_switchpool(char *params) @@ -433,8 +430,6 @@ static char *remote_switchpool(char *params)
{
bool ret = false;
*buffer = '\0';
if (!check_remote_access())
return buffer;
if (!params || strlen(params) == 0) {
// rotate pool test
ret = pool_switch_next(-1);
@ -457,8 +452,6 @@ static char *remote_seturl(char *params) @@ -457,8 +452,6 @@ static char *remote_seturl(char *params)
{
bool ret;
*buffer = '\0';
if (!check_remote_access())
return buffer;
if (!params || strlen(params) == 0) {
// rotate pool test
ret = pool_switch_next(-1);
@ -475,8 +468,6 @@ static char *remote_seturl(char *params) @@ -475,8 +468,6 @@ static char *remote_seturl(char *params)
static char *remote_quit(char *params)
{
*buffer = '\0';
if (!check_remote_access())
return buffer;
bye = 1;
sprintf(buffer, "%s", "bye|");
return buffer;
@ -488,22 +479,23 @@ static char *gethelp(char *params); @@ -488,22 +479,23 @@ static char *gethelp(char *params);
struct CMDS {
const char *name;
char *(*func)(char *);
bool iswritemode;
} cmds[] = {
{ "summary", getsummary },
{ "threads", getthreads },
{ "pool", getpoolnfo },
{ "histo", gethistory },
{ "hwinfo", gethwinfos },
{ "meminfo", getmeminfo },
{ "scanlog", getscanlog },
{ "summary", getsummary, false },
{ "threads", getthreads, false },
{ "pool", getpoolnfo, false },
{ "histo", gethistory, false },
{ "hwinfo", gethwinfos, false },
{ "meminfo", getmeminfo, false },
{ "scanlog", getscanlog, false },
/* remote functions */
{ "seturl", remote_seturl }, /* prefer switchpool, deprecated */
{ "switchpool", remote_switchpool },
{ "quit", remote_quit },
{ "seturl", remote_seturl, true }, /* prefer switchpool, deprecated */
{ "switchpool", remote_switchpool, true },
{ "quit", remote_quit, true },
/* keep it the last */
{ "help", gethelp },
{ "help", gethelp, false },
};
#define CMDMAX ARRAY_SIZE(cmds)
@ -675,6 +667,147 @@ static int websocket_handshake(SOCKETTYPE c, char *result, char *clientkey) @@ -675,6 +667,147 @@ static int websocket_handshake(SOCKETTYPE c, char *result, char *clientkey)
return 0;
}
/*
* Interpret --api-groups G:cmd1:cmd2:cmd3,P:cmd4,*,...
*/
static void setup_groups()
{
const char *api_groups = opt_api_groups ? opt_api_groups : "";
char *buf, *ptr, *next, *colon;
char group;
char commands[512];
char cmdbuf[100];
char *cmd;
bool addstar, did;
int i;
buf = (char *)malloc(strlen(api_groups) + 1);
if (unlikely(!buf))
proper_exit(1); //, "Failed to malloc ipgroups buf");
strcpy(buf, api_groups);
next = buf;
// for each group defined
while (next && *next) {
ptr = next;
next = strchr(ptr, ',');
if (next)
*(next++) = '\0';
// Validate the group
if (*(ptr+1) != ':') {
colon = strchr(ptr, ':');
if (colon)
*colon = '\0';
proper_exit(1); //, "API invalid group name '%s'", ptr);
}
group = GROUP(*ptr);
if (!VALIDGROUP(group))
proper_exit(1); //, "API invalid group name '%c'", *ptr);
if (group == PRIVGROUP)
proper_exit(1); //, "API group name can't be '%c'", PRIVGROUP);
if (group == NOPRIVGROUP)
proper_exit(1); //, "API group name can't be '%c'", NOPRIVGROUP);
if (apigroups[GROUPOFFSET(group)].commands != NULL)
proper_exit(1); //, "API duplicate group name '%c'", *ptr);
ptr += 2;
// Validate the command list (and handle '*')
cmd = &(commands[0]);
*(cmd++) = '|';
*cmd = '\0';
addstar = false;
while (ptr && *ptr) {
colon = strchr(ptr, ':');
if (colon)
*(colon++) = '\0';
if (strcmp(ptr, "*") == 0)
addstar = true;
else {
did = false;
for (i = 0; cmds[i].name != NULL; i++) {
if (strcasecmp(ptr, cmds[i].name) == 0) {
did = true;
break;
}
}
if (did) {
// skip duplicates
sprintf(cmdbuf, "|%s|", cmds[i].name);
if (strstr(commands, cmdbuf) == NULL) {
strcpy(cmd, cmds[i].name);
cmd += strlen(cmds[i].name);
*(cmd++) = '|';
*cmd = '\0';
}
} else {
proper_exit(1); //, "API unknown command '%s' in group '%c'", ptr, group);
}
}
ptr = colon;
}
// * = allow all non-iswritemode commands
if (addstar) {
for (i = 0; cmds[i].name != NULL; i++) {
if (cmds[i].iswritemode == false) {
// skip duplicates
sprintf(cmdbuf, "|%s|", cmds[i].name);
if (strstr(commands, cmdbuf) == NULL) {
strcpy(cmd, cmds[i].name);
cmd += strlen(cmds[i].name);
*(cmd++) = '|';
*cmd = '\0';
}
}
}
}
ptr = apigroups[GROUPOFFSET(group)].commands = (char *)malloc(strlen(commands) + 1);
if (unlikely(!ptr))
proper_exit(1); //, "Failed to malloc group commands buf");
strcpy(ptr, commands);
}
// Now define R (NOPRIVGROUP) as all non-iswritemode commands
cmd = &(commands[0]);
*(cmd++) = '|';
*cmd = '\0';
for (i = 0; cmds[i].name != NULL; i++) {
if (cmds[i].iswritemode == false) {
strcpy(cmd, cmds[i].name);
cmd += strlen(cmds[i].name);
*(cmd++) = '|';
*cmd = '\0';
}
}
ptr = apigroups[GROUPOFFSET(NOPRIVGROUP)].commands = (char *)malloc(strlen(commands) + 1);
if (unlikely(!ptr))
proper_exit(1); //, "Failed to malloc noprivgroup commands buf");
strcpy(ptr, commands);
// W (PRIVGROUP) is handled as a special case since it simply means all commands
free(buf);
return;
}
/*
* Interpret [W:]IP[/Prefix][,[R|W:]IP2[/Prefix2][,...]] --api-allow option
* special case of 0/0 allows /0 (means all IP addresses)
*/
#define ALLIPS "0/0"
/*
* N.B. IP4 addresses are by Definition 32bit big endian on all platforms
*/
@ -724,7 +857,7 @@ static void setup_ipaccess() @@ -724,7 +857,7 @@ static void setup_ipaccess()
ipaccess[ips].group = group;
if (strcmp(ptr, ALLIP4) == 0)
if (strcmp(ptr, ALLIPS) == 0)
ipaccess[ips].ip = ipaccess[ips].mask = 0;
else
{
@ -793,10 +926,178 @@ static bool check_connect(struct sockaddr_in *cli, char **connectaddr, char *gro @@ -793,10 +926,178 @@ static bool check_connect(struct sockaddr_in *cli, char **connectaddr, char *gro
return addrok;
}
static void mcast()
{
struct sockaddr_in listen;
struct ip_mreq grp;
struct sockaddr_in came_from;
time_t bindstart;
char *binderror;
SOCKETTYPE mcast_sock;
SOCKETTYPE reply_sock;
socklen_t came_from_siz;
char *connectaddr;
ssize_t rep;
int bound;
int count;
int reply_port;
bool addrok;
char group;
char expect[] = "ccminer-"; // first 8 bytes constant
char *expect_code;
size_t expect_code_len;
char buf[1024];
char replybuf[1024];
memset(&grp, 0, sizeof(grp));
grp.imr_multiaddr.s_addr = inet_addr(opt_api_mcast_addr);
if (grp.imr_multiaddr.s_addr == INADDR_NONE)
proper_exit(1); //, "Invalid Multicast Address");
grp.imr_interface.s_addr = INADDR_ANY;
mcast_sock = socket(AF_INET, SOCK_DGRAM, 0);
int optval = 1;
if (SOCKETFAIL(setsockopt(mcast_sock, SOL_SOCKET, SO_REUSEADDR, (const char *)(&optval), sizeof(optval)))) {
applog(LOG_ERR, "API mcast setsockopt SO_REUSEADDR failed (%s)%s", strerror(errno), MUNAVAILABLE);
goto die;
}
memset(&listen, 0, sizeof(listen));
listen.sin_family = AF_INET;
listen.sin_addr.s_addr = INADDR_ANY;
listen.sin_port = htons(opt_api_mcast_port);
// try for more than 1 minute ... in case the old one hasn't completely gone yet
bound = 0;
bindstart = time(NULL);
while (bound == 0) {
if (SOCKETFAIL(bind(mcast_sock, (struct sockaddr *)(&listen), sizeof(listen)))) {
binderror = strerror(errno);;
if ((time(NULL) - bindstart) > 61)
break;
else
sleep(30);
}
else
bound = 1;
}
if (bound == 0) {
applog(LOG_ERR, "API mcast bind to port %d failed (%s)%s", opt_api_port, binderror, MUNAVAILABLE);
goto die;
}
if (SOCKETFAIL(setsockopt(mcast_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)(&grp), sizeof(grp)))) {
applog(LOG_ERR, "API mcast join failed (%s)%s", strerror(errno), MUNAVAILABLE);
goto die;
}
expect_code_len = sizeof(expect) + strlen(opt_api_mcast_code);
expect_code = (char *)malloc(expect_code_len + 1);
if (!expect_code)
proper_exit(1); //, "Failed to malloc mcast expect_code");
snprintf(expect_code, expect_code_len + 1, "%s%s-", expect, opt_api_mcast_code);
count = 0;
while (80085) {
sleep(1);
count++;
came_from_siz = sizeof(came_from);
if (SOCKETFAIL(rep = recvfrom(mcast_sock, buf, sizeof(buf) - 1,
0, (struct sockaddr *)(&came_from), &came_from_siz))) {
applog(LOG_DEBUG, "API mcast failed count=%d (%s) (%d)",
count, strerror(errno), (int)mcast_sock);
continue;
}
addrok = check_connect(&came_from, &connectaddr, &group);
applog(LOG_DEBUG, "API mcast from %s - %s",
connectaddr, addrok ? "Accepted" : "Ignored");
if (!addrok)
continue;
buf[rep] = '\0';
if (rep > 0 && buf[rep - 1] == '\n')
buf[--rep] = '\0';
applog(LOG_DEBUG, "API mcast request rep=%d (%s) from %s:%d",
(int)rep, buf,
inet_ntoa(came_from.sin_addr),
ntohs(came_from.sin_port));
if ((size_t)rep > expect_code_len && memcmp(buf, expect_code, expect_code_len) == 0) {
reply_port = atoi(&buf[expect_code_len]);
if (reply_port < 1 || reply_port > 65535) {
applog(LOG_DEBUG, "API mcast request ignored - invalid port (%s)",
&buf[expect_code_len]);
}
else {
applog(LOG_DEBUG, "API mcast request OK port %s=%d",
&buf[expect_code_len], reply_port);
came_from.sin_port = htons(reply_port);
reply_sock = socket(AF_INET, SOCK_DGRAM, 0);
snprintf(replybuf, sizeof(replybuf),
"ccm-%s-%d-%s", opt_api_mcast_code, opt_api_port, opt_api_mcast_des);
rep = sendto(reply_sock, replybuf, strlen(replybuf) + 1,
0, (struct sockaddr *)(&came_from),
sizeof(came_from));
if (SOCKETFAIL(rep)) {
applog(LOG_DEBUG, "API mcast send reply failed (%s) (%d)",
strerror(errno), (int)reply_sock);
}
else {
applog(LOG_DEBUG, "API mcast send reply (%s) succeeded (%d) (%d)",
replybuf, (int)rep, (int)reply_sock);
}
CLOSESOCKET(reply_sock);
}
}
else
applog(LOG_DEBUG, "API mcast request was no good");
}
die:
CLOSESOCKET(mcast_sock);
}
static void *mcast_thread(void *userdata)
{
struct thr_info *mythr = (struct thr_info *)userdata;
pthread_detach(pthread_self());
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
mcast();
//PTH(mythr) = 0L;
return NULL;
}
void mcast_init()
{
struct thr_info *thr;
thr = (struct thr_info *)calloc(1, sizeof(*thr));
if (!thr)
proper_exit(1); //, "Failed to calloc mcast thr");
if (unlikely(pthread_create(&thr->pth, NULL, mcast_thread, thr)))
proper_exit(1); //, "API mcast thread create failed");
}
static void api()
{
const char *addr = opt_api_bind;
unsigned short port = (unsigned short) opt_api_listen; // 4068
unsigned short port = (unsigned short) opt_api_port; // 4068
char buf[MYBUFSIZ];
int n, bound;
char *connectaddr;
@ -814,11 +1115,13 @@ static void api() @@ -814,11 +1115,13 @@ static void api()
SOCKETTYPE c;
SOCKETTYPE *apisock;
if (!opt_api_listen && opt_debug) {
if (!opt_api_port && opt_debug) {
applog(LOG_DEBUG, "API disabled");
return;
}
setup_groups();
if (opt_api_allow) {
setup_ipaccess();
if (ips == 0) {
@ -839,9 +1142,10 @@ static void api() @@ -839,9 +1142,10 @@ static void api()
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = inet_addr(addr);
serv.sin_addr.s_addr = inet_addr(addr); // TODO: allow bind to ip/interface
if (serv.sin_addr.s_addr == (in_addr_t)INVINETADDR) {
applog(LOG_ERR, "API initialisation 2 failed (%s)%s", strerror(errno), UNAVAILABLE);
// free(apisock); FIXME!!
return;
}
@ -869,7 +1173,7 @@ static void api() @@ -869,7 +1173,7 @@ static void api()
binderror = strerror(errno);
if ((time(NULL) - bindstart) > 61)
break;
else if (opt_api_listen == 4068) {
else if (opt_api_port == 4068) {
/* when port is default one, use first available */
if (opt_debug)
applog(LOG_DEBUG, "API bind to port %d failed, trying port %u",
@ -886,10 +1190,10 @@ static void api() @@ -886,10 +1190,10 @@ static void api()
}
else {
bound = 1;
if (opt_api_listen != port) {
if (opt_api_port != port) {
applog(LOG_WARNING, "API bind to port %d failed - using port %u",
opt_api_listen, (uint32_t) port);
opt_api_listen = port;
opt_api_port, (uint32_t)port);
opt_api_port = port;
}
}
}
@ -907,6 +1211,18 @@ static void api() @@ -907,6 +1211,18 @@ static void api()
return;
}
if (opt_api_allow)
applog(LOG_WARNING, "API running in IP access mode on port %d (%d)", port, (int)*apisock);
else {
if (strcmp(opt_api_bind, "127.0.0.1"))
applog(LOG_WARNING, "API running in UNRESTRICTED read access mode on port %d (%d)", port, (int)*apisock);
else
applog(LOG_WARNING, "API running in local read access mode on port %d (%d)", port, (int)*apisock);
}
if (opt_api_mcast)
mcast_init();
buffer = (char *) calloc(1, MYBUFSIZ + 1);
counter = 0;

56
ccminer.cpp

@ -211,12 +211,20 @@ double opt_resume_rate = -1.; @@ -211,12 +211,20 @@ double opt_resume_rate = -1.;
int opt_statsavg = 30;
#define API_MCAST_CODE "FTW"
#define API_MCAST_ADDR "224.0.0.75"
// strdup on char* to allow a common free() if used
static char* opt_syslog_pfx = strdup(PROGRAM_NAME);
char *opt_api_bind = strdup("127.0.0.1"); /* 0.0.0.0 for all ips */
char *opt_api_allow = NULL; /* unimplemented */
int opt_api_remote = 0;
int opt_api_listen = 4068; /* 0 to disable */
int opt_api_port = 4068; /* 0 to disable */
char *opt_api_allow = NULL;
char *opt_api_groups = NULL;
bool opt_api_mcast = false;
char *opt_api_mcast_addr = strdup(API_MCAST_ADDR);
char *opt_api_mcast_code = strdup(API_MCAST_CODE);
char *opt_api_mcast_des = strdup("");
int opt_api_mcast_port = 4068;
bool opt_stratum_stats = false;
@ -361,6 +369,13 @@ struct option options[] = { @@ -361,6 +369,13 @@ struct option options[] = {
{ "algo", 1, NULL, 'a' },
{ "api-bind", 1, NULL, 'b' },
{ "api-remote", 0, NULL, 1030 },
{ "api-allow", 1, NULL, 1031 },
{ "api-groups", 1, NULL, 1032 },
{ "api-mcast", 0, NULL, 1033 },
{ "api-mcast-addr", 1, NULL, 1034 },
{ "api-mcast-code", 1, NULL, 1035 },
{ "api-mcast-port", 1, NULL, 1036 },
{ "api-mcast-des", 1, NULL, 1037 },
{ "background", 0, NULL, 'B' },
{ "benchmark", 0, NULL, 1005 },
{ "cert", 1, NULL, 1001 },
@ -2977,7 +2992,7 @@ void parse_arg(int key, char *arg) @@ -2977,7 +2992,7 @@ void parse_arg(int key, char *arg)
opt_api_bind = strdup(arg);
opt_api_bind[p - arg] = '\0';
}
opt_api_listen = atoi(p + 1);
opt_api_port = atoi(p + 1);
}
else if (arg && strstr(arg, ".")) {
/* ip only */
@ -2986,12 +3001,39 @@ void parse_arg(int key, char *arg) @@ -2986,12 +3001,39 @@ void parse_arg(int key, char *arg)
}
else if (arg) {
/* port or 0 to disable */
opt_api_listen = atoi(arg);
opt_api_port = atoi(arg);
}
break;
case 1030: /* --api-remote */
opt_api_remote = 1;
opt_api_allow = strdup("0/0");
break;
case 1031: /* --api-allow */
free(opt_api_allow);
opt_api_allow = strdup(arg);
break;
case 1032: /* --api-groups */
free(opt_api_groups);
opt_api_groups = strdup(arg);
break;
case 1033: /* --api-mcast */
opt_api_mcast = true;
break;
case 1034: /* --api-mcast-addr */
free(opt_api_mcast_addr);
opt_api_mcast_addr = strdup(arg);
case 1035: /* --api-mcast-code */
free(opt_api_mcast_code);
opt_api_mcast_code = strdup(arg);
break;
case 1036: /* --api-mcast-des */
free(opt_api_mcast_des);
opt_api_mcast_des = strdup(arg);
break;
case 1037: /* --api-mcast-port */
v = atoi(arg);
if (v < 1 || v > 65535) // sanity check
show_usage_and_exit(1);
opt_api_mcast_port = v;
case 'B':
opt_background = true;
break;
@ -4002,7 +4044,7 @@ int main(int argc, char *argv[]) @@ -4002,7 +4044,7 @@ int main(int argc, char *argv[])
}
#endif
if (opt_api_listen) {
if (opt_api_port) {
/* api thread */
api_thr_id = opt_n_threads + 3;
thr = &thr_info[api_thr_id];

Loading…
Cancel
Save