Browse Source

API restriction policy and multicast announces (#16)

same as sgminer, fixed to build (tpruvot), untested yet
master
bitbandi 7 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;
#define SOCK_REC_BUFSZ 1024 #define SOCK_REC_BUFSZ 1024
#define QUEUE 10 #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 *localaddr = "127.0.0.1";
static const char *UNAVAILABLE = " - API will not be available"; 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 char *buffer = NULL;
static time_t startup = 0; static time_t startup = 0;
static int bye = 0; static int bye = 0;
extern char *opt_api_bind; extern char *opt_api_bind;
extern int opt_api_port;
extern char *opt_api_allow; extern char *opt_api_allow;
extern int opt_api_listen; /* port */ extern char *opt_api_groups;
extern int opt_api_remote; 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... // current stratum...
extern struct stratum_ctx stratum; extern struct stratum_ctx stratum;
@ -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) * Set pool by index (pools array in json config)
* switchpool|1| * switchpool|1|
@ -433,8 +430,6 @@ static char *remote_switchpool(char *params)
{ {
bool ret = false; bool ret = false;
*buffer = '\0'; *buffer = '\0';
if (!check_remote_access())
return buffer;
if (!params || strlen(params) == 0) { if (!params || strlen(params) == 0) {
// rotate pool test // rotate pool test
ret = pool_switch_next(-1); ret = pool_switch_next(-1);
@ -457,8 +452,6 @@ static char *remote_seturl(char *params)
{ {
bool ret; bool ret;
*buffer = '\0'; *buffer = '\0';
if (!check_remote_access())
return buffer;
if (!params || strlen(params) == 0) { if (!params || strlen(params) == 0) {
// rotate pool test // rotate pool test
ret = pool_switch_next(-1); ret = pool_switch_next(-1);
@ -475,8 +468,6 @@ static char *remote_seturl(char *params)
static char *remote_quit(char *params) static char *remote_quit(char *params)
{ {
*buffer = '\0'; *buffer = '\0';
if (!check_remote_access())
return buffer;
bye = 1; bye = 1;
sprintf(buffer, "%s", "bye|"); sprintf(buffer, "%s", "bye|");
return buffer; return buffer;
@ -488,22 +479,23 @@ static char *gethelp(char *params);
struct CMDS { struct CMDS {
const char *name; const char *name;
char *(*func)(char *); char *(*func)(char *);
bool iswritemode;
} cmds[] = { } cmds[] = {
{ "summary", getsummary }, { "summary", getsummary, false },
{ "threads", getthreads }, { "threads", getthreads, false },
{ "pool", getpoolnfo }, { "pool", getpoolnfo, false },
{ "histo", gethistory }, { "histo", gethistory, false },
{ "hwinfo", gethwinfos }, { "hwinfo", gethwinfos, false },
{ "meminfo", getmeminfo }, { "meminfo", getmeminfo, false },
{ "scanlog", getscanlog }, { "scanlog", getscanlog, false },
/* remote functions */ /* remote functions */
{ "seturl", remote_seturl }, /* prefer switchpool, deprecated */ { "seturl", remote_seturl, true }, /* prefer switchpool, deprecated */
{ "switchpool", remote_switchpool }, { "switchpool", remote_switchpool, true },
{ "quit", remote_quit }, { "quit", remote_quit, true },
/* keep it the last */ /* keep it the last */
{ "help", gethelp }, { "help", gethelp, false },
}; };
#define CMDMAX ARRAY_SIZE(cmds) #define CMDMAX ARRAY_SIZE(cmds)
@ -675,6 +667,147 @@ static int websocket_handshake(SOCKETTYPE c, char *result, char *clientkey)
return 0; 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 * N.B. IP4 addresses are by Definition 32bit big endian on all platforms
*/ */
@ -724,7 +857,7 @@ static void setup_ipaccess()
ipaccess[ips].group = group; ipaccess[ips].group = group;
if (strcmp(ptr, ALLIP4) == 0) if (strcmp(ptr, ALLIPS) == 0)
ipaccess[ips].ip = ipaccess[ips].mask = 0; ipaccess[ips].ip = ipaccess[ips].mask = 0;
else else
{ {
@ -793,10 +926,178 @@ static bool check_connect(struct sockaddr_in *cli, char **connectaddr, char *gro
return addrok; 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() static void api()
{ {
const char *addr = opt_api_bind; 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]; char buf[MYBUFSIZ];
int n, bound; int n, bound;
char *connectaddr; char *connectaddr;
@ -814,11 +1115,13 @@ static void api()
SOCKETTYPE c; SOCKETTYPE c;
SOCKETTYPE *apisock; SOCKETTYPE *apisock;
if (!opt_api_listen && opt_debug) { if (!opt_api_port && opt_debug) {
applog(LOG_DEBUG, "API disabled"); applog(LOG_DEBUG, "API disabled");
return; return;
} }
setup_groups();
if (opt_api_allow) { if (opt_api_allow) {
setup_ipaccess(); setup_ipaccess();
if (ips == 0) { if (ips == 0) {
@ -839,9 +1142,10 @@ static void api()
memset(&serv, 0, sizeof(serv)); memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET; 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) { if (serv.sin_addr.s_addr == (in_addr_t)INVINETADDR) {
applog(LOG_ERR, "API initialisation 2 failed (%s)%s", strerror(errno), UNAVAILABLE); applog(LOG_ERR, "API initialisation 2 failed (%s)%s", strerror(errno), UNAVAILABLE);
// free(apisock); FIXME!!
return; return;
} }
@ -869,7 +1173,7 @@ static void api()
binderror = strerror(errno); binderror = strerror(errno);
if ((time(NULL) - bindstart) > 61) if ((time(NULL) - bindstart) > 61)
break; break;
else if (opt_api_listen == 4068) { else if (opt_api_port == 4068) {
/* when port is default one, use first available */ /* when port is default one, use first available */
if (opt_debug) if (opt_debug)
applog(LOG_DEBUG, "API bind to port %d failed, trying port %u", applog(LOG_DEBUG, "API bind to port %d failed, trying port %u",
@ -886,10 +1190,10 @@ static void api()
} }
else { else {
bound = 1; bound = 1;
if (opt_api_listen != port) { if (opt_api_port != port) {
applog(LOG_WARNING, "API bind to port %d failed - using port %u", applog(LOG_WARNING, "API bind to port %d failed - using port %u",
opt_api_listen, (uint32_t) port); opt_api_port, (uint32_t)port);
opt_api_listen = port; opt_api_port = port;
} }
} }
} }
@ -907,6 +1211,18 @@ static void api()
return; 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); buffer = (char *) calloc(1, MYBUFSIZ + 1);
counter = 0; counter = 0;

56
ccminer.cpp

@ -211,12 +211,20 @@ double opt_resume_rate = -1.;
int opt_statsavg = 30; 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 // strdup on char* to allow a common free() if used
static char* opt_syslog_pfx = strdup(PROGRAM_NAME); 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_bind = strdup("127.0.0.1"); /* 0.0.0.0 for all ips */
char *opt_api_allow = NULL; /* unimplemented */ int opt_api_port = 4068; /* 0 to disable */
int opt_api_remote = 0; char *opt_api_allow = NULL;
int opt_api_listen = 4068; /* 0 to disable */ 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; bool opt_stratum_stats = false;
@ -361,6 +369,13 @@ struct option options[] = {
{ "algo", 1, NULL, 'a' }, { "algo", 1, NULL, 'a' },
{ "api-bind", 1, NULL, 'b' }, { "api-bind", 1, NULL, 'b' },
{ "api-remote", 0, NULL, 1030 }, { "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' }, { "background", 0, NULL, 'B' },
{ "benchmark", 0, NULL, 1005 }, { "benchmark", 0, NULL, 1005 },
{ "cert", 1, NULL, 1001 }, { "cert", 1, NULL, 1001 },
@ -2977,7 +2992,7 @@ void parse_arg(int key, char *arg)
opt_api_bind = strdup(arg); opt_api_bind = strdup(arg);
opt_api_bind[p - arg] = '\0'; opt_api_bind[p - arg] = '\0';
} }
opt_api_listen = atoi(p + 1); opt_api_port = atoi(p + 1);
} }
else if (arg && strstr(arg, ".")) { else if (arg && strstr(arg, ".")) {
/* ip only */ /* ip only */
@ -2986,12 +3001,39 @@ void parse_arg(int key, char *arg)
} }
else if (arg) { else if (arg) {
/* port or 0 to disable */ /* port or 0 to disable */
opt_api_listen = atoi(arg); opt_api_port = atoi(arg);
} }
break; break;
case 1030: /* --api-remote */ 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; 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': case 'B':
opt_background = true; opt_background = true;
break; break;
@ -4002,7 +4044,7 @@ int main(int argc, char *argv[])
} }
#endif #endif
if (opt_api_listen) { if (opt_api_port) {
/* api thread */ /* api thread */
api_thr_id = opt_n_threads + 3; api_thr_id = opt_n_threads + 3;
thr = &thr_info[api_thr_id]; thr = &thr_info[api_thr_id];

Loading…
Cancel
Save