Merge branch 'andrew' into 'master'

This commit is contained in:
Andrew Bettison 2012-07-25 18:04:16 +09:30
commit 04b95d2590
19 changed files with 1204 additions and 353 deletions

View File

@ -434,6 +434,11 @@ int app_echo(int argc, const char *const *argv, struct command_line_option *o)
return 0;
}
int cli_lookup_did(const char *text)
{
return text[0] == '\0' || strcmp(text, "*") == 0 || str_is_did(text);
}
int app_dna_lookup(int argc, const char *const *argv, struct command_line_option *o)
{
int i;
@ -447,7 +452,7 @@ int app_dna_lookup(int argc, const char *const *argv, struct command_line_option
char uris[MAXREPLIES][MAXURILEN];
const char *did;
if (cli_arg(argc, argv, o, "did", &did, NULL, "*") == -1)
if (cli_arg(argc, argv, o, "did", &did, cli_lookup_did, "*") == -1)
return -1;
/* Bind to MDP socket and await confirmation */
@ -503,28 +508,32 @@ int app_dna_lookup(int argc, const char *const *argv, struct command_line_option
else if ((rx.packetTypeAndFlags&MDP_TYPE_MASK)==MDP_TX) {
/* Extract DID, Name, URI from response. */
if (strlen((char *)rx.in.payload)<512) {
char sidhex[512];
char did[512];
char name[512];
char sidhex[SID_STRLEN + 1];
char did[DID_MAXSIZE + 1];
char name[64];
char uri[512];
if (!parseDnaReply(rx.in.payload,rx.in.payload_length,
sidhex,did,name,uri))
{
/* Have we seen this response before? */
int i;
for(i=0;i<uri_count;i++)
if (!strcmp(uri,uris[i])) break;
if (i==uri_count) {
/* Not previously seen, so report it */
cli_puts(uri); cli_delim(":");
cli_puts(did); cli_delim(":");
cli_puts(name); cli_delim("\n");
/* Remember that we have seen it */
if (uri_count<MAXREPLIES&&strlen(uri)<MAXURILEN) {
strcpy(uris[uri_count++],uri);
}
if ( !parseDnaReply((char *)rx.in.payload, rx.in.payload_length, sidhex, did, name, uri, NULL)
|| !str_is_subscriber_id(sidhex)
|| !str_is_did(did)
|| !str_is_uri(uri)
) {
WHYF("Received malformed DNA reply: %s", alloca_toprint(160, (const char *)rx.in.payload, rx.in.payload_length));
} else {
/* Have we seen this response before? */
int i;
for(i=0;i<uri_count;i++)
if (!strcmp(uri,uris[i])) break;
if (i==uri_count) {
/* Not previously seen, so report it */
cli_puts(uri); cli_delim(":");
cli_puts(did); cli_delim(":");
cli_puts(name); cli_delim("\n");
/* Remember that we have seen it */
if (uri_count<MAXREPLIES&&strlen(uri)<MAXURILEN) {
strcpy(uris[uri_count++],uri);
}
}
}
}
}
else WHYF("packettype=0x%x",rx.packetTypeAndFlags);
@ -1423,15 +1432,19 @@ int app_keyring_add(int argc, const char *const *argv, struct command_line_optio
return 0;
}
int cli_optional_did(const char *text)
{
return text[0] == '\0' || str_is_did(text);
}
int app_keyring_set_did(int argc, const char *const *argv, struct command_line_option *o)
{
const char *sid, *did, *pin, *name;
cli_arg(argc, argv, o, "sid", &sid, NULL, "");
cli_arg(argc, argv, o, "did", &did, NULL, "");
cli_arg(argc, argv, o, "sid", &sid, str_is_subscriber_id, "");
cli_arg(argc, argv, o, "did", &did, cli_optional_did, "");
cli_arg(argc, argv, o, "name", &name, NULL, "");
cli_arg(argc, argv, o, "pin", &pin, NULL, "");
if (strlen(did)>31) return WHY("DID too long (31 digits max)");
if (strlen(name)>63) return WHY("Name too long (31 char max)");
if (!(keyring = keyring_open_with_pins(pin)))
@ -1647,14 +1660,18 @@ int app_node_info(int argc, const char *const *argv, struct command_line_option
continue;
}
{
char sidhex[512];
char did[512];
char name[512];
{
char sidhex[SID_STRLEN + 1];
char did[DID_MAXSIZE + 1];
char name[64];
char uri[512];
if (!parseDnaReply(m2.in.payload,m2.in.payload_length,
sidhex,did,name,uri))
{
if ( !parseDnaReply((char *)m2.in.payload, m2.in.payload_length, sidhex, did, name, uri, NULL)
|| !str_is_subscriber_id(sidhex)
|| !str_is_did(did)
|| !str_is_uri(uri)
) {
WHYF("Received malformed DNA reply: %s", alloca_toprint(160, (const char *)m2.in.payload, m2.in.payload_length));
} else {
/* Got a good DNA reply, copy it into place */
bcopy(did,mdp.nodeinfo.did,32);
bcopy(name,mdp.nodeinfo.name,64);

View File

@ -1,6 +1,6 @@
/*
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@ -99,7 +99,8 @@ int fromhexstr(unsigned char *dstBinary, const char *srcHex, size_t nbinary)
int str_is_subscriber_id(const char *sid)
{
return strcasecmp(sid, "broadcast") == 0 || _is_xstring(sid, SID_STRLEN);
size_t len = 0;
return strn_is_subscriber_id(sid, &len) && sid[len] == '\0';
}
int strn_is_subscriber_id(const char *sid, size_t *lenp)
@ -157,6 +158,29 @@ int rhizome_str_is_file_hash(const char *hash)
return _is_xstring(hash, RHIZOME_FILEHASH_STRLEN);
}
int str_is_did(const char *did)
{
size_t len = 0;
return strn_is_did(did, &len) && did[len] == '\0';
}
int is_didchar(char c)
{
return isdigit(c) || c == '*' || c == '#' || c == '+';
}
int strn_is_did(const char *did, size_t *lenp)
{
int i;
for (i = 0; i < DID_MAXSIZE && is_didchar(did[i]); ++i)
;
if (i < DID_MINSIZE)
return 0;
if (lenp)
*lenp = i;
return 1;
}
int extractDid(unsigned char *packet,int *ofs,char *did)
{
int d=0;
@ -337,3 +361,45 @@ int safeZeroField(unsigned char *packet,int start,int count)
return 0;
}
int is_uri_char_scheme(char c)
{
return isalpha(c) || isdigit(c) || c == '+' || c == '-' || c == '.';
}
int is_uri_char_unreserved(char c)
{
return isalpha(c) || isdigit(c) || c == '-' || c == '.' || c == '_' || c == '~';
}
int is_uri_char_reserved(char c)
{
switch (c) {
case ':': case '/': case '?': case '#': case '[': case ']': case '@':
case '!': case '$': case '&': case '\'': case '(': case ')':
case '*': case '+': case ',': case ';': case '=':
return 1;
}
return 0;
}
/* Return true if the string resembles a URI.
Based on RFC-3986 generic syntax, assuming nothing about the hierarchical part.
@author Andrew Bettison <andrew@servalproject.com>
*/
int str_is_uri(const char *uri)
{
const char *p = uri;
// Scheme is ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
if (!isalpha(*p++))
return 0;
while (is_uri_char_scheme(*p))
++p;
// Scheme is followed by colon ":".
if (*p++ != ':')
return 0;
// Hierarchical part must contain only valid characters.
const char *q = p;
while (is_uri_char_unreserved(*p) || is_uri_char_reserved(*p))
++p;
return p != q && *p == '\0';
}

View File

@ -17,7 +17,12 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include "serval.h"
#include "strbuf.h"
#include "strbuf_helpers.h"
/*
The challenge with making an interface for calling an external program to
@ -38,194 +43,563 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
main loop.
*/
int
parseDnaReply(const char *buf, size_t len, char *token, char *did, char *name, char *uri, const char **bufp)
{
/* Replies look like: TOKEN|URI|DID|NAME| where TOKEN is usually a hex SID */
const char *b = buf;
const char *e = buf + len;
char *p, *q;
for (p = token, q = token + SID_STRLEN; b != e && *b != '|' && p != q; ++p, ++b)
*p = *b;
*p = '\0';
if (b == e || *b++ != '|')
return 0;
for (p = uri, q = uri + 511; b != e && *b != '|' && p != q; ++p, ++b)
*p = *b;
*p = '\0';
if (b == e || *b++ != '|')
return 0;
for (p = did, q = did + DID_MAXSIZE; b != e && *b != '|' && p != q; ++p, ++b)
*p = *b;
*p = '\0';
if (b == e || *b++ != '|')
return 0;
for (p = name, q = name + 63; b != e && *b != '|' && p != q; ++p, ++b)
*p = *b;
*p = '\0';
if (b == e || *b++ != '|')
return 0;
if (bufp)
*bufp = b;
return 1;
}
static pid_t dna_helper_pid = -1;
static int dna_helper_stdin = -1;
static int dna_helper_stdout = -1;
static int dna_helper_stderr = -1;
static int dna_helper_started = 0;
int
parseDnaReply(unsigned char *bytes, int count,
char *sidhex, char *did, char *name, char *uri) {
bzero(sidhex, SID_SIZE*2+1);
bzero(did, SID_SIZE);
bzero(name,64);
bzero(uri,512);
int i,l,maxlen;
static struct sched_ent sched_requests = STRUCT_SCHED_ENT_UNUSED;
static struct sched_ent sched_replies = STRUCT_SCHED_ENT_UNUSED;
static struct sched_ent sched_harvester = STRUCT_SCHED_ENT_UNUSED;
static struct sched_ent sched_errors = STRUCT_SCHED_ENT_UNUSED;
static struct sched_ent sched_restart = STRUCT_SCHED_ENT_UNUSED;
static struct sched_ent sched_timeout = STRUCT_SCHED_ENT_UNUSED;
l=0;
/* Replies look like: TOKEN|URI|DID|CALLERID| */
// This buffer must hold "SID|DID|\n\0"
static char request_buffer[SID_STRLEN + DID_MAXSIZE + 4];
static char *request_bufptr = NULL;
static char *request_bufend = NULL;
static overlay_mdp_data_frame request_mdp_data;
static char request_did[DID_MAXSIZE + 1];
maxlen=SID_SIZE*2+1;
for(i=0;l<maxlen&&i<count&&bytes[i]!='|';i++)
sidhex[l++]=bytes[i];
sidhex[l]=0;
static int awaiting_reply = 0;
static int discarding_until_nl = 0;
static char reply_buffer[2048];
static char *reply_bufend = NULL;
if (l>=count) return WHY("DNA helper response does not contain URI field");
if (l>=maxlen) return WHY("DNA helper response SID field too long");
l=0; i++; maxlen=511;
for(;l<maxlen&&i<count&&bytes[i]!='|';i++)
uri[l++]=bytes[i];
uri[l]=0;
static void monitor_requests(struct sched_ent *alarm);
static void monitor_replies(struct sched_ent *alarm);
static void monitor_errors(struct sched_ent *alarm);
static void harvester(struct sched_ent *alarm);
static void restart_delayer(struct sched_ent *alarm);
static void reply_timeout(struct sched_ent *alarm);
if (l>=count) return WHY("DNA helper response does not contain DID field");
if (l>=maxlen) return WHY("DNA helper response URI field too long");
l=0; i++; maxlen=SID_SIZE;
for(;l<maxlen&&i<count&&bytes[i]!='|';i++)
did[l++]=bytes[i];
did[l]=0;
if (l>=count) return WHY("DNA helper response does not contain CALLERID field");
if (l>=maxlen) return WHY("DNA helper response DID field too long");
l=0; i++; maxlen=SID_SIZE;
for(;l<maxlen&&i<count&&bytes[i]!='|';i++)
name[l++]=bytes[i];
name[l]=0;
if (l>=count) return WHY("DNA helper response does not contain terminator");
if (l>=maxlen) return WHY("DNA helper response CALLERID field too long");
/* DEBUGF("did='%s', name='%s', uri='%s'",did,name,uri); */
return 0;
}
void dna_helper_monitor(int fd)
static void
dna_helper_close_pipes()
{
return;
if (dna_helper_stdin != -1) {
if (debug & DEBUG_DNAHELPER)
DEBUGF("DNAHELPER closing stdin pipe fd=%d", dna_helper_stdin);
close(dna_helper_stdin);
dna_helper_stdin = -1;
}
if (sched_requests.poll.fd != -1) {
unwatch(&sched_requests);
sched_requests.poll.fd = -1;
}
if (dna_helper_stdout != -1) {
if (debug & DEBUG_DNAHELPER)
DEBUGF("DNAHELPER closing stdout pipe fd=%d", dna_helper_stdout);
close(dna_helper_stdout);
dna_helper_stdout = -1;
}
if (sched_replies.poll.fd != -1) {
unwatch(&sched_replies);
sched_replies.poll.fd = -1;
}
if (dna_helper_stderr != -1) {
if (debug & DEBUG_DNAHELPER)
DEBUGF("DNAHELPER closing stderr pipe fd=%d", dna_helper_stderr);
close(dna_helper_stderr);
dna_helper_stderr = -1;
}
if (sched_errors.poll.fd != -1) {
unwatch(&sched_errors);
sched_errors.poll.fd = -1;
}
}
struct sched_ent dna_helper_sched;
static int
dna_helper_start(const char *command, const char *arg) {
int stdin_fds[2], stdout_fds[2];
pid_t pid;
DEBUG("Starting DNA helper");
if (pipe(stdin_fds))
dna_helper_start(const char *command, const char *arg)
{
dna_helper_close_pipes();
int stdin_fds[2], stdout_fds[2], stderr_fds[2];
if (pipe(stdin_fds) == -1)
return WHY_perror("pipe");
if (pipe(stdout_fds)) {
if (pipe(stdout_fds) == -1) {
WHY_perror("pipe");
close(stdin_fds[0]);
close(stdin_fds[1]);
return WHY_perror("pipe");
close(stdin_fds[1]);
return -1;
}
if ((pid = fork()) == 0) {
if (pipe(stderr_fds) == -1) {
WHY_perror("pipe");
close(stdin_fds[0]);
close(stdin_fds[1]);
close(stdout_fds[0]);
close(stdout_fds[1]);
return -1;
}
switch (dna_helper_pid = fork()) {
case 0:
/* Child, should exec() to become helper after installing file descriptors. */
if (dup2(stdin_fds[1], 0)) /* replace stdin */
exit(-1);
if (dup2(stdout_fds[0], 1)) /* replace stdout */
exit(-1);
if (dup2(stdout_fds[0], 2)) /* replace stderr */
exit(-1);
execl(command, command, arg, NULL);
DEBUG("execl() failed");
abort(); /* Can't get here */
} else {
if (pid == -1) {
/* fork failed */
WHY_perror("fork");
close(stdin_fds[0]);
close(stdin_fds[1]);
close(stdout_fds[0]);
close(stdout_fds[1]);
return -1;
} else {
/* Parent, should put file descriptors into place for use */
dna_helper_stdin = stdin_fds[0];
dna_helper_stdout = stdout_fds[1];
/* Need to watch dna_helper_stdout */
// XXX need to initialise structure before calling watch
// XXX watch(&dna_helper_sched);
// XXX need to add unwatch() when we detect that the process has died.
return 0;
set_logging(stderr);
signal(SIGTERM, SIG_DFL);
close(stdin_fds[1]);
close(stdout_fds[0]);
close(stderr_fds[0]);
if (dup2(stderr_fds[1], 2) == -1 || dup2(stdout_fds[1], 1) == -1 || dup2(stdin_fds[0], 0) == -1) {
LOG_perror(LOG_LEVEL_FATAL, "dup2");
fflush(stderr);
_exit(-1);
}
execl(command, command, arg, NULL);
LOGF_perror(LOG_LEVEL_FATAL, "execl(%s, %s, %s, NULL)", command, command, arg ? arg : "NULL");
fflush(stderr);
do { _exit(-1); } while (1);
break;
case -1:
/* fork failed */
WHY_perror("fork");
close(stdin_fds[0]);
close(stdin_fds[1]);
close(stdout_fds[0]);
close(stdout_fds[1]);
close(stderr_fds[0]);
close(stderr_fds[1]);
return -1;
default:
/* Parent, should put file descriptors into place for use */
close(stdin_fds[0]);
close(stdout_fds[1]);
close(stderr_fds[1]);
dna_helper_started = 0;
dna_helper_stdin = stdin_fds[1];
dna_helper_stdout = stdout_fds[0];
dna_helper_stderr = stderr_fds[0];
INFOF("STARTED DNA HELPER pid=%u stdin=%d stdout=%d stderr=%d executable=%s arg=%s",
dna_helper_pid,
dna_helper_stdin,
dna_helper_stdout,
dna_helper_stderr,
command,
arg ? arg : "NULL"
);
sched_requests.function = monitor_requests;
sched_requests.context = NULL;
sched_requests.poll.fd = -1;
sched_requests.poll.events = POLLOUT;
sched_requests.stats = NULL;
sched_timeout.function = reply_timeout;
sched_timeout.context = NULL;
sched_timeout.stats = NULL;
sched_replies.function = monitor_replies;
sched_replies.context = NULL;
sched_replies.poll.fd = dna_helper_stdout;
sched_replies.poll.events = POLLIN;
sched_replies.stats = NULL;
sched_errors.function = monitor_errors;
sched_errors.context = NULL;
sched_errors.poll.fd = dna_helper_stderr;
sched_errors.poll.events = POLLIN;
sched_errors.stats = NULL;
sched_harvester.function = harvester;
sched_harvester.stats = NULL;
sched_harvester.alarm = overlay_gettime_ms() + 1000;
sched_harvester.deadline = sched_harvester.alarm + 1000;
reply_bufend = reply_buffer;
discarding_until_nl = 0;
awaiting_reply = 0;
watch(&sched_replies);
watch(&sched_errors);
schedule(&sched_harvester);
return 0;
}
return -1;
}
int
dna_helper_enqueue(char *did, unsigned char *requestorSid) {
const char *dna_helper, *dna_helper_arg;
char buffer[1024];
if (dna_helper_stdin == -2)
return -1;
static int
dna_helper_kill()
{
if (awaiting_reply) {
unschedule(&sched_timeout);
awaiting_reply = 0;
}
if (dna_helper_pid > 0) {
if (debug & DEBUG_DNAHELPER)
DEBUGF("DNAHELPER sending SIGTERM to pid=%d", dna_helper_pid);
if (kill(dna_helper_pid, SIGTERM) == -1)
WHYF_perror("kill(%d, SIGTERM)", dna_helper_pid);
// The process is wait()ed for in dna_helper_monitor() so that we do not block here.
return 1;
}
return 0;
}
if (dna_helper_stdin == -1) {
dna_helper = confValueGet("dna.helper", NULL);
if (!dna_helper || !dna_helper[0]) {
/* Check if we have a helper configured. If not, then set
dna_helper_stdin to magic value of -2 so that we don't waste time
in future looking up the dna helper configuration value. */
DEBUG("We have no DNA helper configured");
dna_helper_stdin = -2;
return -1;
static int
dna_helper_harvest(int blocking)
{
if (dna_helper_pid > 0) {
if (blocking && (debug & DEBUG_DNAHELPER))
DEBUGF("DNAHELPER waiting for pid=%d to die", dna_helper_pid);
int status;
pid_t pid = waitpid(dna_helper_pid, &status, blocking ? 0 : WNOHANG);
if (pid == dna_helper_pid) {
strbuf b = strbuf_alloca(80);
INFOF("DNAHELPER process pid=%u %s", pid, strbuf_str(strbuf_append_exit_status(b, status)));
unschedule(&sched_harvester);
dna_helper_pid = -1;
if (awaiting_reply) {
unschedule(&sched_timeout);
awaiting_reply = 0;
}
return 1;
} else if (pid == -1) {
return WHYF_perror("waitpid(%d, %s)", dna_helper_pid, blocking ? "0" : "WNOHANG");
} else if (pid) {
return WHYF("waitpid(%d, %s) returned %d", dna_helper_pid, blocking ? "0" : "WNOHANG", pid);
}
/* Look for optional argument */
dna_helper_arg = confValueGet("dna.helperarg", NULL);
}
return 0;
}
/* Okay, so we have a helper configured.
Run it */
if (dna_helper_start(dna_helper, dna_helper_arg) < 0) {
int dna_helper_shutdown()
{
if (debug & DEBUG_DNAHELPER)
DEBUG("DNAHELPER shutting down");
dna_helper_close_pipes();
switch (dna_helper_kill()) {
case -1:
return -1;
case 0:
return 0;
default:
return dna_helper_harvest(1);
}
}
static void monitor_requests(struct sched_ent *alarm)
{
if (debug & DEBUG_DNAHELPER) {
DEBUGF("sched_requests.poll.revents=%s",
strbuf_str(strbuf_append_poll_events(strbuf_alloca(40), sched_requests.poll.revents))
);
}
if (sched_requests.poll.revents & (POLLHUP | POLLERR)) {
if (debug & DEBUG_DNAHELPER)
DEBUGF("DNAHELPER closing stdin fd=%d", dna_helper_stdin);
close(dna_helper_stdin);
dna_helper_stdin = -1;
unwatch(&sched_requests);
sched_requests.poll.fd = -1;
dna_helper_kill();
}
else if (sched_requests.poll.revents & POLLOUT) {
if (request_bufptr) {
if (request_bufptr < request_bufend) {
size_t remaining = request_bufend - request_bufptr;
sigPipeFlag = 0;
ssize_t written = write_nonblock(dna_helper_stdin, request_bufptr, remaining);
if (sigPipeFlag) {
/* Broken pipe is probably due to a dead helper, but make sure the helper is dead, just to be
sure. It will be harvested at the next harvester() timeout, and restarted on the first
request that arrives after a suitable pause has elapsed. Losing the current request is not
a big problem, because DNA preemptively retries.
*/
INFO("DNAHELPER got SIGPIPE on write -- stopping process");
dna_helper_kill();
} else if (written > 0) {
if (debug & DEBUG_DNAHELPER)
DEBUGF("DNAHELPER wrote request %s", alloca_toprint(-1, request_bufptr, written));
request_bufptr += written;
}
}
if (request_bufptr >= request_bufend) {
// Request sent successfully. Start watching for reply.
request_bufptr = request_bufend = NULL;
awaiting_reply = 1;
sched_timeout.alarm = overlay_gettime_ms() + 1500;
sched_timeout.deadline = sched_timeout.alarm + 3000;
schedule(&sched_timeout);
}
}
// If no request to send, stop monitoring the helper's stdin pipe.
if (!request_bufptr) {
unwatch(&sched_requests);
sched_requests.poll.fd = -1;
}
}
}
static char *strnstr(char *haystack, size_t haystack_len, const char *needle)
{
size_t needle_len = strlen(needle);
for (; haystack_len >= needle_len; ++haystack, --haystack_len) {
if (strncmp(haystack, needle, needle_len) == 0)
return haystack;
}
return NULL;
}
void handle_reply_line(const char *bufp, size_t len)
{
if (!dna_helper_started) {
if (len == 8 && strncmp(bufp, "STARTED\n", 8) == 0) {
if (debug & DEBUG_DNAHELPER)
DEBUGF("DNAHELPER got STARTED ACK");
dna_helper_started = 1;
// Start sending request if there is one pending.
if (request_bufptr) {
sched_requests.poll.fd = dna_helper_stdin;
watch(&sched_requests);
}
} else {
WHYF("DNAHELPER malformed start ACK %s", alloca_toprint(-1, bufp, len));
dna_helper_kill();
}
} else if (awaiting_reply) {
if (len == 5 && strncmp(bufp, "DONE\n", 5) == 0) {
if (debug & DEBUG_DNAHELPER)
DEBUG("DNAHELPER reply DONE");
unschedule(&sched_timeout);
awaiting_reply = 0;
} else {
char sidhex[SID_STRLEN + 1];
char did[DID_MAXSIZE + 1];
char name[64];
char uri[512];
const char *replyend = NULL;
if (!parseDnaReply(bufp, len, sidhex, did, name, uri, &replyend))
WHYF("DNAHELPER reply %s invalid -- ignored", alloca_toprint(-1, bufp, len));
else if (uri[0] == '\0')
WHYF("DNAHELPER reply %s contains empty URI -- ignored", alloca_toprint(-1, bufp, len));
else if (!str_is_uri(uri))
WHYF("DNAHELPER reply %s contains invalid URI -- ignored", alloca_toprint(-1, bufp, len));
else if (sidhex[0] == '\0')
WHYF("DNAHELPER reply %s contains empty token -- ignored", alloca_toprint(-1, bufp, len));
else if (!str_is_subscriber_id(sidhex))
WHYF("DNAHELPER reply %s contains invalid token -- ignored", alloca_toprint(-1, bufp, len));
else if (did[0] == '\0')
WHYF("DNAHELPER reply %s contains empty DID -- ignored", alloca_toprint(-1, bufp, len));
else if (!str_is_did(did))
WHYF("DNAHELPER reply %s contains invalid DID -- ignored", alloca_toprint(-1, bufp, len));
else if (strcmp(did, request_did) != 0)
WHYF("DNAHELPER reply %s contains mismatched DID -- ignored", alloca_toprint(-1, bufp, len));
else if (*replyend != '\n')
WHYF("DNAHELPER reply %s contains spurious trailing chars -- ignored", alloca_toprint(-1, bufp, len));
else {
if (debug & DEBUG_DNAHELPER)
DEBUGF("DNAHELPER reply %s", alloca_toprint(-1, bufp, len));
unsigned char sid[SID_SIZE];
fromhex(sid, sidhex, SID_SIZE);
overlay_mdp_dnalookup_reply(&request_mdp_data, sid, uri, did, name);
}
}
} else {
WARNF("DNAHELPER spurious output %s -- ignored", alloca_toprint(-1, bufp, len));
}
}
static void monitor_replies(struct sched_ent *alarm)
{
if (debug & DEBUG_DNAHELPER) {
DEBUGF("sched_replies.poll.revents=%s",
strbuf_str(strbuf_append_poll_events(strbuf_alloca(40), sched_replies.poll.revents))
);
}
if (sched_replies.poll.revents & POLLIN) {
size_t remaining = reply_buffer + sizeof reply_buffer - reply_bufend;
ssize_t nread = read_nonblock(sched_replies.poll.fd, reply_bufend, remaining);
if (nread > 0) {
char *bufp = reply_buffer;
char *readp = reply_bufend;
reply_bufend += nread;
char *nl;
while (nread > 0 && (nl = strnstr(readp, nread, "\n"))) {
size_t len = nl - bufp + 1;
if (discarding_until_nl) {
if (debug & DEBUG_DNAHELPER)
DEBUGF("Discarding %s", alloca_toprint(-1, bufp, len));
discarding_until_nl = 0;
} else {
handle_reply_line(bufp, len);
}
readp = bufp = nl + 1;
nread = reply_bufend - readp;
}
if (bufp != reply_buffer) {
size_t len = reply_bufend - bufp;
memmove(reply_buffer, bufp, len);
reply_bufend = reply_buffer + len;
} else if (reply_bufend >= reply_buffer + sizeof reply_buffer) {
WHY("DNAHELPER reply buffer overrun");
if (debug & DEBUG_DNAHELPER)
DEBUGF("Discarding %s", alloca_toprint(-1, reply_buffer, sizeof reply_buffer));
reply_bufend = reply_buffer;
discarding_until_nl = 1;
}
}
}
if (sched_replies.poll.revents & (POLLHUP | POLLERR)) {
if (debug & DEBUG_DNAHELPER)
DEBUGF("DNAHELPER closing stdout fd=%d", dna_helper_stdout);
close(dna_helper_stdout);
dna_helper_stdout = -1;
unwatch(&sched_replies);
sched_replies.poll.fd = -1;
dna_helper_kill();
}
}
static void monitor_errors(struct sched_ent *alarm)
{
if (debug & DEBUG_DNAHELPER) {
DEBUGF("sched_errors.poll.revents=%s",
strbuf_str(strbuf_append_poll_events(strbuf_alloca(40), sched_errors.poll.revents))
);
}
if (sched_errors.poll.revents & POLLIN) {
char buffer[1024];
ssize_t nread = read_nonblock(sched_errors.poll.fd, buffer, sizeof buffer);
if (nread > 0)
WHYF("DNAHELPER stderr %s", alloca_toprint(-1, buffer, nread));
}
if (sched_errors.poll.revents & (POLLHUP | POLLERR)) {
if (debug & DEBUG_DNAHELPER)
DEBUGF("DNAHELPER closing stderr fd=%d", dna_helper_stderr);
close(dna_helper_stderr);
dna_helper_stderr = -1;
unwatch(&sched_errors);
sched_errors.poll.fd = -1;
}
}
static void harvester(struct sched_ent *alarm)
{
// While the helper process appears to still be running, keep calling this function.
// Otherwise, wait a while before re-starting the helper.
if (dna_helper_harvest(0) <= 0) {
sched_harvester.alarm = overlay_gettime_ms() + 1000;
sched_harvester.deadline = sched_harvester.alarm + 1000;
schedule(&sched_harvester);
} else {
const int delay_ms = 500;
if (debug & DEBUG_DNAHELPER)
DEBUGF("DNAHELPER process died, pausing %d ms before restart", delay_ms);
dna_helper_pid = 0; // Will be set to -1 after delay
sched_restart.function = restart_delayer;
sched_restart.alarm = overlay_gettime_ms() + delay_ms;
sched_restart.deadline = sched_restart.alarm + 500;
schedule(&sched_restart);
}
}
static void restart_delayer(struct sched_ent *alarm)
{
if (dna_helper_pid == 0) {
if (debug & DEBUG_DNAHELPER)
DEBUG("DNAHELPER re-enable restart");
dna_helper_pid = -1;
}
}
static void reply_timeout(struct sched_ent *alarm)
{
if (awaiting_reply) {
WHY("DNAHELPER reply timeout");
dna_helper_kill();
}
}
int
dna_helper_enqueue(overlay_mdp_frame *mdp, const char *did, const unsigned char *requestorSid)
{
if (debug & DEBUG_DNAHELPER)
DEBUGF("DNAHELPER request did=%s sid=%s", did, alloca_tohex_sid(requestorSid));
if (dna_helper_pid == 0)
return 0;
// Only try to restart a DNA helper process if the previous one is well and truly gone.
if (dna_helper_pid == -1 && dna_helper_stdin == -1 && dna_helper_stdout == -1 && dna_helper_stderr == -1) {
const char *dna_helper_executable = confValueGet("dna.helper.executable", NULL);
const char *dna_helper_arg1 = confValueGet("dna.helper.argv.1", NULL);
if (!dna_helper_executable || !dna_helper_executable[0]) {
/* Check if we have a helper configured. If not, then set
dna_helper_pid to magic value of 0 so that we don't waste time
in future looking up the dna helper configuration value. */
INFO("DNAHELPER none configured");
dna_helper_pid = 0;
return 0;
}
if (dna_helper_start(dna_helper_executable, dna_helper_arg1) == -1) {
/* Something broke, bail out */
DEBUG("Failed to start dna helper");
WHY("DNAHELPER start failed");
return -1;
}
}
/* Write request to dna helper.
Request takes form: SID-of-Requestor|DID|\n
By passing the requestor's SID to the helper, we don't need to maintain
any state, as all we have to do is wait for responses from the helper,
which will include the requestor's SID.
*/
bzero(buffer, sizeof(buffer));
if (snprintf(buffer, sizeof(buffer) - 1, "%s|%s|\n", alloca_tohex_sid(requestorSid), did) >
sizeof(buffer) - 1)
return WHY("Command to helper is too long");
sigPipeFlag = 0;
write_str(dna_helper_stdin, buffer);
if (sigPipeFlag) {
/* Assume broken pipe due to dead helper.
Next request will cause it to be restarted.
(Losing the current request is not a big problem, because
DNA preemptively retries, anyway.
XXX In fact, we should probably have a limit to the number of restarts
in quick succession so that we don't waste lots of time with a buggy or
suicidal helper.
*/
close(dna_helper_stdin);
close(dna_helper_stdout);
dna_helper_stdin = -1;
dna_helper_stdout = -1;
DEBUG("Sigpipe encountered");
return -1;
if (dna_helper_stdin == -1)
return 0;
if (request_bufptr && request_bufptr != request_buffer) {
WARNF("DNAHELPER currently sending request %s -- dropping new request", request_buffer);
return 0;
}
return 0;
}
int dna_return_resolution(overlay_mdp_frame *mdp, unsigned char *fromSid,
const char *did,const char *name, const char *uri) {
/* copy SID out into source address of frame */
bcopy(fromSid,&mdp->out.src.sid[0],SID_SIZE);
/* and build reply as did\nname\nURI<NUL> */
snprintf((char *)&mdp->out.payload[0],512,"%s\n%s\n%s",
did,name,uri);
mdp->out.payload_length=strlen((char *)mdp->out.payload)+1;
/* Dispatch response */
mdp->packetTypeAndFlags&=MDP_FLAG_MASK;
mdp->packetTypeAndFlags|=MDP_TX;
overlay_mdp_dispatch(mdp,0 /* system generated */,
NULL,0);
return 0;
if (awaiting_reply) {
WARNF("DNAHELPER currently awaiting reply -- dropping new request", request_buffer);
return 0;
}
char buffer[sizeof request_buffer];
strbuf b = strbuf_local(request_bufptr == request_buffer ? buffer : request_buffer, sizeof buffer);
strbuf_tohex(b, requestorSid, SID_SIZE);
strbuf_putc(b, '|');
strbuf_puts(b, did);
strbuf_putc(b, '|');
strbuf_putc(b, '\n');
if (strbuf_overrun(b)) {
WHYF("DNAHELPER request buffer overrun: %s -- request not sent", strbuf_str(b));
request_bufptr = request_bufend = NULL;
} else {
if (strbuf_str(b) != request_buffer) {
if (strcmp(strbuf_str(b), request_buffer) != 0)
WARNF("DNAHELPER overwriting unsent request %s", request_buffer);
strcpy(request_buffer, strbuf_str(b));
}
request_bufptr = request_buffer;
request_bufend = request_buffer + strbuf_len(b);
request_mdp_data = mdp->out;
strncpy(request_did, did, sizeof request_did);
request_did[sizeof request_did - 1] = '\0';
}
if (dna_helper_started) {
sched_requests.poll.fd = dna_helper_stdin;
watch(&sched_requests);
}
return 1;
}

View File

@ -137,6 +137,7 @@ int watch(struct sched_ent *alarm){
if (fdcount>=MAX_WATCHED_FDS)
return WHY("Too many file handles to watch");
fd_callbacks[fdcount]=alarm;
alarm->poll.revents = 0;
alarm->_poll_index=fdcount;
fdcount++;
}

84
log.c
View File

@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "serval.h"
#include "strbuf.h"
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
@ -39,9 +40,20 @@ static char _log_buf[8192];
static struct strbuf logbuf = STRUCT_STRBUF_EMPTY;
#ifdef ANDROID
#include <android/log.h>
#include <android/log.h>
#endif
void set_logging(FILE *f)
{
logfile = f;
if (f == stdout)
INFO("Logging to stdout");
else if (f == stderr)
INFO("Logging to stderr");
else if (f != NULL)
INFOF("Logging to stream with fd=%d", fileno(f));
}
FILE *open_logging()
{
if (!logfile) {
@ -231,7 +243,7 @@ unsigned int debugFlagMask(const char *flagname) {
else if (!strcasecmp(flagname,"verbio")) return DEBUG_VERBOSE_IO;
else if (!strcasecmp(flagname,"peers")) return DEBUG_PEERS;
else if (!strcasecmp(flagname,"dnaresponses")) return DEBUG_DNARESPONSES;
else if (!strcasecmp(flagname,"dnarequests")) return DEBUG_DNAREQUESTS;
else if (!strcasecmp(flagname,"dnahelper")) return DEBUG_DNAHELPER;
else if (!strcasecmp(flagname,"simulation")) return DEBUG_SIMULATION;
else if (!strcasecmp(flagname,"packetformats")) return DEBUG_PACKETFORMATS;
else if (!strcasecmp(flagname,"packetconstruction")) return DEBUG_PACKETCONSTRUCTION;
@ -251,40 +263,54 @@ unsigned int debugFlagMask(const char *flagname) {
else if (!strcasecmp(flagname,"manifests")) return DEBUG_MANIFESTS;
else if (!strcasecmp(flagname,"mdprequests")) return DEBUG_MDPREQUESTS;
else if (!strcasecmp(flagname,"timing")) return DEBUG_TIMING;
return 0;
}
static strbuf _toprint(strbuf sb, const char *srcBuf, size_t srcBytes)
{
strbuf_putc(sb, '"');
for (; srcBytes && !strbuf_overrun(sb); ++srcBuf, --srcBytes) {
if (*srcBuf == '\0')
strbuf_puts(sb, "\\0");
else if (*srcBuf == '\n')
strbuf_puts(sb, "\\n");
else if (*srcBuf == '\r')
strbuf_puts(sb, "\\r");
else if (*srcBuf == '\t')
strbuf_puts(sb, "\\t");
else if (*srcBuf == '\\')
strbuf_puts(sb, "\\\\");
else if (*srcBuf >= ' ' && *srcBuf <= '~')
strbuf_putc(sb, *srcBuf);
else
strbuf_sprintf(sb, "\\x%02x", *srcBuf);
}
strbuf_putc(sb, '"');
if (strbuf_overrun(sb)) {
strbuf_trunc(sb, -4);
strbuf_puts(sb, "\"...");
}
return sb;
}
/* Format a buffer of data as a printable representation, eg: "Abc\x0b\n\0", for display
in log messages.
in log messages. If dstStrLen == -1 then assumes the dstStr buffer is large enough to
hold the representation of the entire srcBuf.
@author Andrew Bettison <andrew@servalproject.com>
*/
char *toprint(char *dstStr, size_t dstChars, const unsigned char *srcBuf, size_t srcBytes)
char *toprint(char *dstStr, ssize_t dstStrLen, const char *srcBuf, size_t srcBytes)
{
strbuf b = strbuf_local(dstStr, dstChars);
strbuf_putc(b, '"');
for (; srcBytes && !strbuf_overrun(b); ++srcBuf, --srcBytes) {
if (*srcBuf == '\0')
strbuf_puts(b, "\\0");
else if (*srcBuf == '\n')
strbuf_puts(b, "\\n");
else if (*srcBuf == '\r')
strbuf_puts(b, "\\r");
else if (*srcBuf == '\t')
strbuf_puts(b, "\\t");
else if (*srcBuf == '\\')
strbuf_puts(b, "\\\\");
else if (*srcBuf >= ' ' && *srcBuf <= '~')
strbuf_putc(b, *srcBuf);
else
strbuf_sprintf(b, "\\x%02x", *srcBuf);
}
strbuf_putc(b, '"');
if (strbuf_overrun(b)) {
strbuf_trunc(b, -4);
strbuf_puts(b, "\"...");
}
return dstStr;
return strbuf_str(_toprint(strbuf_local(dstStr, (dstStrLen == -1 ? 2 + srcBytes * 4 : dstStrLen) + 1), srcBuf, srcBytes));
}
/* Compute the length of the printable string produced by toprint(). If dstStrLen == -1 then
returns the exact number of characters in the printable representation, otherwise returns
dstStrLen.
@author Andrew Bettison <andrew@servalproject.com>
*/
size_t toprint_strlen(ssize_t dstStrLen, const char *srcBuf, size_t srcBytes)
{
return dstStrLen == -1 ? strbuf_count(_toprint(strbuf_local(NULL, 0), srcBuf, srcBytes)) : dstStrLen;
}
/* Read the symbolic link into the supplied buffer and add a terminating nul. Return -1 if the

12
net.c
View File

@ -47,7 +47,7 @@ int _set_block(int fd, const char *file, unsigned int line, const char *function
return 0;
}
int _read_nonblock(int fd, void *buf, size_t len, const char *file, unsigned int line, const char *function)
ssize_t _read_nonblock(int fd, void *buf, size_t len, const char *file, unsigned int line, const char *function)
{
ssize_t nread = read(fd, buf, len);
if (nread == -1) {
@ -66,7 +66,7 @@ int _read_nonblock(int fd, void *buf, size_t len, const char *file, unsigned int
return nread;
}
int _write_all(int fd, const void *buf, size_t len, const char *file, unsigned int line, const char *function)
ssize_t _write_all(int fd, const void *buf, size_t len, const char *file, unsigned int line, const char *function)
{
ssize_t written = write(fd, buf, len);
if (written == -1) {
@ -82,7 +82,7 @@ int _write_all(int fd, const void *buf, size_t len, const char *file, unsigned i
return written;
}
int _write_nonblock(int fd, const void *buf, size_t len, const char *file, unsigned int line, const char *function)
ssize_t _write_nonblock(int fd, const void *buf, size_t len, const char *file, unsigned int line, const char *function)
{
ssize_t written = write(fd, buf, len);
if (written == -1) {
@ -101,7 +101,7 @@ int _write_nonblock(int fd, const void *buf, size_t len, const char *file, unsig
return written;
}
int _write_all_nonblock(int fd, const void *buf, size_t len, const char *file, unsigned int line, const char *function)
ssize_t _write_all_nonblock(int fd, const void *buf, size_t len, const char *file, unsigned int line, const char *function)
{
ssize_t written = _write_nonblock(fd, buf, len, file, line, function);
if (written != -1 && written != len) {
@ -112,12 +112,12 @@ int _write_all_nonblock(int fd, const void *buf, size_t len, const char *file, u
return written;
}
int _write_str(int fd, const char *str, const char *file, unsigned int line, const char *function)
ssize_t _write_str(int fd, const char *str, const char *file, unsigned int line, const char *function)
{
return _write_all(fd, str, strlen(str), file, line, function);
}
int _write_str_nonblock(int fd, const char *str, const char *file, unsigned int line, const char *function)
ssize_t _write_str_nonblock(int fd, const char *str, const char *file, unsigned int line, const char *function)
{
return _write_all_nonblock(fd, str, strlen(str), file, line, function);
}

View File

@ -16,8 +16,9 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "serval.h"
#include <sys/stat.h>
#include "serval.h"
#include "strbuf.h"
struct sched_ent mdp_abstract;
struct sched_ent mdp_named;
@ -502,53 +503,26 @@ int overlay_saw_mdp_frame(overlay_mdp_frame *mdp,long long now)
if (mdp->out.payload_length<1) {
RETURN(WHY("Empty DID in DNA resolution request")); }
bcopy(&mdp->out.payload[0],&did[0],pll);
/* make sure it is null terminated */
did[pll]=0;
/* remember source sid for putting back later */
overlay_mdp_frame mdpreply;
int results=0;
while(keyring_find_did(keyring,&cn,&in,&kp,did))
{
bzero(&mdpreply,sizeof(mdpreply));
/* mark as outgoing MDP message */
mdpreply.packetTypeAndFlags=MDP_TX;
/* Set source and destination addresses */
bcopy(&mdp->out.dst.sid,mdpreply.out.src.sid,SID_SIZE);
bcopy(&mdp->out.src.sid,mdpreply.out.dst.sid,SID_SIZE);
mdpreply.out.src.port=mdp->out.dst.port;
mdpreply.out.dst.port=mdp->out.src.port;
/* package DID and Name into reply (we include the DID because
it could be a wild-card DID search, but the SID is implied
in the source address of our reply). */
if (keyring->contexts[cn]->identities[in]->keypairs[kp]
->private_key_len>64)
if (keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key_len > DID_MAXSIZE)
/* skip excessively long DID records */
continue;
/* and null-terminated DID */
unsigned char *unpackedDid=
keyring->contexts[cn]->identities[in]->keypairs[kp]
->private_key;
unsigned char *packedSid=
keyring->contexts[cn]->identities[in]->keypairs[0]
->public_key;
char *name=
(char *)keyring->contexts[cn]->identities[in]->keypairs[kp]
->public_key;
/* copy SID out into source address of frame */
bcopy(packedSid,&mdpreply.out.src.sid[0],SID_SIZE);
/* and build reply as did\nname\nURI<NUL> */
snprintf((char *)&mdpreply.out.payload[0],512,"%s|sid://%s/%s|%s|%s|",
alloca_tohex_sid(packedSid),
alloca_tohex_sid(packedSid),unpackedDid,
unpackedDid,name);
mdpreply.out.payload_length=strlen((char *)mdpreply.out.payload)+1;
/* deliver reply */
overlay_mdp_dispatch(&mdpreply,0 /* system generated */,NULL,0);
const unsigned char *packedSid = keyring->contexts[cn]->identities[in]->keypairs[0]->public_key;
const char *unpackedDid = (const char *) keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key;
const char *name = (const char *)keyring->contexts[cn]->identities[in]->keypairs[kp]->public_key;
// URI is sid://SIDHEX/DID
strbuf b = strbuf_alloca(SID_STRLEN + DID_MAXSIZE + 10);
strbuf_puts(b, "sid://");
strbuf_tohex(b, packedSid, SID_SIZE);
strbuf_putc(b, '/');
strbuf_puts(b, unpackedDid);
overlay_mdp_dnalookup_reply(&mdp->out, packedSid, strbuf_str(b), unpackedDid, name);
kp++;
results++;
}
@ -563,8 +537,7 @@ int overlay_saw_mdp_frame(overlay_mdp_frame *mdp,long long now)
when results become available, so this function will return
immediately, so as not to cause blockages and delays in servald.
*/
DEBUGF("Asking DNA helper to resolve '%s'",did);
dna_helper_enqueue(did,mdp->out.src.sid);
dna_helper_enqueue(mdp, did, mdp->out.src.sid);
}
RETURN(0);
DEBUG("Got here");
@ -624,6 +597,26 @@ int overlay_saw_mdp_frame(overlay_mdp_frame *mdp,long long now)
RETURN(0);
}
int overlay_mdp_dnalookup_reply(const overlay_mdp_data_frame *mdpd, const unsigned char *sid, const char *uri, const char *did, const char *name)
{
overlay_mdp_frame mdpreply;
bzero(&mdpreply, sizeof mdpreply);
mdpreply.packetTypeAndFlags = MDP_TX; // outgoing MDP message
bcopy(mdpd->src.sid, mdpreply.out.dst.sid, SID_SIZE);
mdpreply.out.dst.port = mdpd->src.port;
mdpreply.out.src.port = mdpd->dst.port;
bcopy(sid, mdpreply.out.src.sid, SID_SIZE);
/* build reply as TOKEN|URI|DID|NAME|<NUL> */
strbuf b = strbuf_local((char *)mdpreply.out.payload, sizeof mdpreply.out.payload);
strbuf_tohex(b, sid, SID_SIZE);
strbuf_sprintf(b, "|%s|%s|%s|", uri, did, name);
if (strbuf_overrun(b))
return WHY("MDP payload overrun");
mdpreply.out.payload_length = strbuf_len(b) + 1;
/* deliver reply */
return overlay_mdp_dispatch(&mdpreply, 0 /* system generated */, NULL, 0);
}
int overlay_mdp_sanitytest_sourceaddr(sockaddr_mdp *src,int userGeneratedFrameP,
struct sockaddr_un *recvaddr,
int recvaddrlen)

View File

@ -296,7 +296,7 @@ int extractRequest(unsigned char *packet,int *packet_ofs,int packet_len,
*bytes|=packet[(*packet_ofs)++];
*flags=packet[(*packet_ofs)++];
if (debug&DEBUG_DNAREQUESTS) printf("Write flags = 0x%02x\n",*flags);
if (debug&DEBUG_PACKETFORMATS) printf("Write flags = 0x%02x\n",*flags);
if (*packet_ofs<0||(*packet_ofs)+(*bytes)>=packet_len)
{

View File

@ -141,7 +141,7 @@ int rhizome_read_manifest_file(rhizome_manifest *m, const char *filename, int bu
m->errors++;
WARNF(bufferP ? "Malformed manifest line in buffer %p: %s"
: "Malformed manifest line in file %s: %s",
filename, alloca_toprint(80, (unsigned char *)line, linelen));
filename, alloca_toprint(80, line, linelen));
} else {
*p++ = '\0';
char *var = line;

View File

@ -869,7 +869,7 @@ void rhizome_fetch_poll(struct sched_ent *alarm)
q->request_len += bytes;
if (http_header_complete(q->request, q->request_len, bytes + 4)) {
if (debug & DEBUG_RHIZOME_RX)
DEBUGF("Got HTTP reply: %s", alloca_toprint(160, (unsigned char *)q->request, q->request_len));
DEBUGF("Got HTTP reply: %s", alloca_toprint(160, q->request, q->request_len));
/* We have all the reply headers, so parse them, taking care of any following bytes of
content. */
char *p = NULL;

View File

@ -564,7 +564,7 @@ static int rhizome_server_parse_http_request(rhizome_http_request *r)
if (path) {
char *id = NULL;
if (debug & DEBUG_RHIZOME_TX)
DEBUGF("GET %s", alloca_toprint(1024, (unsigned char *)path, pathlen));
DEBUGF("GET %s", alloca_toprint(1024, path, pathlen));
if (strcmp(path, "/favicon.ico") == 0) {
r->request_type = RHIZOME_HTTP_REQUEST_FAVICON;
rhizome_server_http_response_header(r, 200, "image/vnd.microsoft.icon", favicon_len);
@ -605,7 +605,7 @@ static int rhizome_server_parse_http_request(rhizome_http_request *r)
}
} else {
if (debug & DEBUG_RHIZOME_TX)
DEBUGF("Received malformed HTTP request: %s", alloca_toprint(120, (unsigned char *)r->request, r->request_length));
DEBUGF("Received malformed HTTP request: %s", alloca_toprint(120, (const char *)r->request, r->request_length));
rhizome_server_simple_http_response(r, 400, "<html><h1>Malformed request</h1></html>\r\n");
}
@ -669,7 +669,7 @@ static int rhizome_server_set_response(rhizome_http_request *r, const struct htt
r->buffer_offset = 0;
r->request_type |= RHIZOME_HTTP_REQUEST_FROMBUFFER;
if (debug & DEBUG_RHIZOME_TX)
DEBUGF("Sending HTTP response: %s", alloca_toprint(120, r->buffer, r->buffer_length));
DEBUGF("Sending HTTP response: %s", alloca_toprint(120, (const char *)r->buffer, r->buffer_length));
return 0;
}

View File

@ -299,7 +299,8 @@ void keyring_identity_extract(const keyring_identity *id, const unsigned char **
get - 8 bit variable value
*/
#define SID_SIZE 32
#define SID_SIZE 32
#define DID_MINSIZE 5
#define DID_MAXSIZE 32
#define SIDDIDFIELD_LEN (SID_SIZE+1)
#define PINFIELD_LEN 32
@ -501,6 +502,7 @@ struct sched_ent{
int _poll_index;
};
#define STRUCT_SCHED_ENT_UNUSED ((struct sched_ent){NULL, NULL, NULL, NULL, {-1, 0, 0}, 0LL, 0LL, NULL, -1})
extern int overlayMode;
#define OVERLAY_INTERFACE_UNKNOWN 0
@ -703,6 +705,9 @@ char *str_toupper_inplace(char *s);
int str_is_subscriber_id(const char *sid);
int strn_is_subscriber_id(const char *sid, size_t *lenp);
int str_is_did(const char *did);
int strn_is_did(const char *did, size_t *lenp);
int str_is_uri(const char *uri);
int stowSid(unsigned char *packet, int ofs, const char *sid);
int stowDid(unsigned char *packet,int *ofs,char *did);
@ -798,6 +803,7 @@ int overlay_frame_resolve_addresses(overlay_frame *f);
#define LOG_LEVEL_FATAL (4)
extern unsigned int debug;
void set_logging(FILE *f);
FILE *open_logging();
void close_logging();
void logMessage(int level, const char *file, unsigned int line, const char *function, const char *fmt, ...);
@ -805,42 +811,48 @@ void vlogMessage(int level, const char *file, unsigned int line, const char *fun
unsigned int debugFlagMask(const char *flagname);
char *catv(const char *data, char *buf, size_t len);
int dump(char *name, unsigned char *addr, size_t len);
char *toprint(char *dstStr, size_t dstChars, const unsigned char *srcBuf, size_t srcBytes);
int log_backtrace();
char *toprint(char *dstStr, ssize_t dstStrLen, const char *srcBuf, size_t srcBytes);
size_t toprint_strlen(ssize_t dstStrLen, const char *srcBuf, size_t srcBytes);
#define alloca_toprint(dstlen,buf,len) toprint((char *)alloca((dstlen) + 1), (dstlen) + 1, (buf), (len))
#define alloca_toprint(dstlen,buf,len) toprint((char *)alloca(toprint_strlen((dstlen), (buf), (len)) + 1), (dstlen), (buf), (len))
#define alloca_tohex(buf,len) tohex((char *)alloca((len)*2+1), (buf), (len))
#define alloca_tohex_sid(sid) alloca_tohex((sid), SID_SIZE)
const char *trimbuildpath(const char *s);
#define LOGF(L,F,...) (logMessage(L, __FILE__, __LINE__, __FUNCTION__, F, ##__VA_ARGS__))
#define logMessage_perror(L,file,line,func,F,...) \
(logMessage(L, file, line, func, F ": %s [errno=%d]", ##__VA_ARGS__, strerror(errno), errno))
#define LOGF(L,F,...) logMessage(L, __FILE__, __LINE__, __FUNCTION__, F, ##__VA_ARGS__)
#define LOGF_perror(L,F,...) logMessage_perror(L, __FILE__, __LINE__, __FUNCTION__, F, ##__VA_ARGS__)
#define LOG_perror(L,X) LOGF_perror(L, "%s", (X))
#define FATALF(F,...) do { LOGF(LOG_LEVEL_FATAL, F, ##__VA_ARGS__); exit(-1); } while (1)
#define FATAL(X) FATALF("%s", (X))
#define FATAL_perror(X) FATALF("%s: %s [errno=%d]", (X), strerror(errno), errno)
#define FATALF_perror(F,...) do { LOGF_perror(LOG_LEVEL_FATAL, F, ##__VA_ARGS__); exit(-1); } while (1)
#define FATAL_perror(X) FATALF_perror("%s", (X))
#define WHYF(F,...) (LOGF(LOG_LEVEL_ERROR, F, ##__VA_ARGS__), -1)
#define WHY(X) WHYF("%s", (X))
#define WHYNULL(X) (LOGF(LOG_LEVEL_ERROR, "%s", X), NULL)
#define WHYF_perror(F,...) WHYF(F ": %s [errno=%d]", ##__VA_ARGS__, strerror(errno), errno)
#define WHY_perror(X) WHYF("%s: %s [errno=%d]", (X), strerror(errno), errno)
#define WHYF_perror(F,...) (LOGF_perror(LOG_LEVEL_ERROR, F, ##__VA_ARGS__), -1)
#define WHY_perror(X) WHYF_perror("%s", (X))
#define WARNF(F,...) LOGF(LOG_LEVEL_WARN, F, ##__VA_ARGS__)
#define WARN(X) WARNF("%s", (X))
#define WARN_perror(X) WARNF("%s: %s [errno=%d]", (X), strerror(errno), errno)
#define WARNF_perror(F,...) LOGF_perror(LOG_LEVEL_WARN, F, ##__VA_ARGS__)
#define WARN_perror(X) WARNF_perror("%s", (X))
#define INFOF(F,...) LOGF(LOG_LEVEL_INFO, F, ##__VA_ARGS__)
#define INFO(X) INFOF("%s", (X))
#define DEBUGF(F,...) LOGF(LOG_LEVEL_DEBUG, F, ##__VA_ARGS__)
#define DEBUG(X) DEBUGF("%s", (X))
#define DEBUGF_perror(F,...) DEBUGF(F ": %s [errno=%d]", ##__VA_ARGS__, strerror(errno), errno)
#define DEBUG_perror(X) DEBUGF("%s: %s [errno=%d]", (X), strerror(errno), errno)
#define D DEBUG("D")
#define DEBUGF_perror(F,...) LOGF_perror(LOG_LEVEL_DEBUG, F, ##__VA_ARGS__)
#define DEBUG_perror(X) DEBUGF_perror("%s", (X))
#define D DEBUG("D")
overlay_buffer *ob_new(int size);
overlay_buffer *ob_static(unsigned char *bytes, int size);
@ -1112,7 +1124,7 @@ int overlay_saw_mdp_containing_frame(overlay_frame *f,long long now);
#define DEBUG_VERBOSE_IO (1 << 3)
#define DEBUG_PEERS (1 << 4)
#define DEBUG_DNARESPONSES (1 << 5)
#define DEBUG_DNAREQUESTS (1 << 6)
#define DEBUG_DNAHELPER (1 << 6)
#define DEBUG_SIMULATION (1 << 7)
#define DEBUG_RHIZOME_RX (1 << 8)
#define DEBUG_PACKETFORMATS (1 << 9)
@ -1380,6 +1392,7 @@ int overlay_mdp_reply(int sock,struct sockaddr_un *recvaddr,int recvaddrlen,
int overlay_mdp_relevant_bytes(overlay_mdp_frame *mdp);
int overlay_mdp_dispatch(overlay_mdp_frame *mdp,int userGeneratedFrameP,
struct sockaddr_un *recvaddr,int recvaddlen);
int overlay_mdp_dnalookup_reply(const overlay_mdp_data_frame *mdpd, const unsigned char *sid, const char *uri, const char *did, const char *name);
int dump_payload(overlay_frame *p,char *message);
@ -1575,11 +1588,11 @@ int stopAudio();
#define SERVER_RUNNING 4
int server_probe(int *pid);
int dna_helper_enqueue(char *did, unsigned char *requestorSid);
int dna_helper_shutdown();
int dna_helper_enqueue(overlay_mdp_frame *mdp, const char *did, const unsigned char *requestorSid);
int dna_return_resolution(overlay_mdp_frame *mdp, unsigned char *fromSid,
const char *did,const char *name,const char *uri);
int parseDnaReply(unsigned char *bytes, int count,
char *sidhex, char *did, char *name, char *uri);
int parseDnaReply(const char *buf, size_t len, char *token, char *did, char *name, char *uri, const char **bufp);
extern int sigPipeFlag;
extern int sigIoFlag;
void sigPipeHandler(int signal);
@ -1599,12 +1612,12 @@ void sigIoHandler(int signal);
int _set_nonblock(int fd, const char *file, unsigned int line, const char *function);
int _set_block(int fd, const char *file, unsigned int line, const char *function);
int _read_nonblock(int fd, void *buf, size_t len, const char *file, unsigned int line, const char *function);
int _write_all(int fd, const void *buf, size_t len, const char *file, unsigned int line, const char *function);
int _write_nonblock(int fd, const void *buf, size_t len, const char *file, unsigned int line, const char *function);
int _write_all_nonblock(int fd, const void *buf, size_t len, const char *file, unsigned int line, const char *function);
int _write_str(int fd, const char *str, const char *file, unsigned int line, const char *function);
int _write_str_nonblock(int fd, const char *str, const char *file, unsigned int line, const char *function);
ssize_t _read_nonblock(int fd, void *buf, size_t len, const char *file, unsigned int line, const char *function);
ssize_t _write_all(int fd, const void *buf, size_t len, const char *file, unsigned int line, const char *function);
ssize_t _write_nonblock(int fd, const void *buf, size_t len, const char *file, unsigned int line, const char *function);
ssize_t _write_all_nonblock(int fd, const void *buf, size_t len, const char *file, unsigned int line, const char *function);
ssize_t _write_str(int fd, const char *str, const char *file, unsigned int line, const char *function);
ssize_t _write_str_nonblock(int fd, const char *str, const char *file, unsigned int line, const char *function);
int rhizome_http_server_start();
int overlay_mdp_setup_sockets();

View File

@ -309,6 +309,7 @@ void serverCleanUp()
} else {
overlay_mdp_client_done();
}
dna_helper_shutdown();
}
static void signame(char *buf, size_t len, int signal)
@ -486,7 +487,7 @@ int processRequest(unsigned char *packet,int len,
while(pofs<len)
{
if (debug&DEBUG_DNAREQUESTS) DEBUGF(" processRequest: len=%d, pofs=%d, pofs_prev=%d",len,pofs,prev_pofs);
if (debug&DEBUG_DNARESPONSES) DEBUGF(" processRequest: len=%d, pofs=%d, pofs_prev=%d",len,pofs,prev_pofs);
/* Avoid infinite loops */
if (pofs<=prev_pofs) break;
prev_pofs=pofs;
@ -517,7 +518,7 @@ int processRequest(unsigned char *packet,int len,
}
else
{
if (debug&DEBUG_DNAREQUESTS) DEBUGF("Looking at action code 0x%02x @ packet offset 0x%x",
if (debug&DEBUG_DNARESPONSES) DEBUGF("Looking at action code 0x%02x @ packet offset 0x%x",
packet[pofs],pofs);
switch(packet[pofs])
{
@ -572,7 +573,7 @@ int processRequest(unsigned char *packet,int len,
pofs+=2;
if (debug&DEBUG_DNAREQUESTS) DEBUGF("Processing ACTION_GET (var_id=%02x, instance=%02x, pofs=0x%x, len=%d)",var_id,instance,pofs,len);
if (debug&DEBUG_DNARESPONSES) DEBUGF("Processing ACTION_GET (var_id=%02x, instance=%02x, pofs=0x%x, len=%d)",var_id,instance,pofs,len);
if (debug&DEBUG_HLR) DEBUGF("Looking for identities with sid='%s' / did='%s'",(sid&&sid[0])?sid:"null",did?did:"null");

View File

@ -69,6 +69,24 @@ strbuf strbuf_puts(strbuf sb, const char *text)
return sb;
}
strbuf strbuf_tohex(strbuf sb, const unsigned char *data, size_t len)
{
static char hexdigit[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
char *p = sb->current;
sb->current += len * 2;
if (sb->start) {
char *e = sb->current < sb->end ? sb->current : sb->end;
// The following loop could overwrite the '\0' at *sp->end.
for (; p < e; ++data) {
*p++ = hexdigit[*data >> 4];
*p++ = hexdigit[*data & 0xf];
}
// This will restore the '\0' at *sp->end if it was overwritten.
*e = '\0';
}
return sb;
}
strbuf strbuf_putc(strbuf sb, char ch)
{
if (sb->start && sb->current < sb->end) {

View File

@ -254,6 +254,23 @@ strbuf strbuf_ncat(strbuf sb, const char *text, size_t len);
strbuf strbuf_puts(strbuf sb, const char *text);
/** Append binary data strbuf, in uppercase hexadecimal format, truncating if
* necessary to avoid buffer overrun. Return a pointer to the strbuf.
*
* After these operations:
* n = strbuf_len(sb);
* c = strbuf_count(sb);
* strbuf_tohex(data, len);
* the following invariants hold:
* strbuf_count(sb) == c + len * 2
* strbuf_len(sb) >= n
* strbuf_len(sb) <= n + len * 2
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
strbuf strbuf_tohex(strbuf sb, const unsigned char *data, size_t len);
/** Append a single character to the strbuf if there is space, and place a
* terminating nul after it. Return a pointer to the strbuf so that
* concatenations can be chained in a single line.

View File

@ -20,6 +20,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "strbuf_helpers.h"
#include <poll.h>
#include <ctype.h>
#include <string.h>
#include <sys/wait.h>
strbuf strbuf_append_poll_events(strbuf sb, short events)
{
@ -88,3 +90,22 @@ strbuf strbuf_append_shell_quotemeta(strbuf sb, const char *word)
}
return sb;
}
strbuf strbuf_append_exit_status(strbuf sb, int status)
{
if (WIFEXITED(status))
strbuf_sprintf(sb, "exited normally with status %u", WEXITSTATUS(status));
else if (WIFSIGNALED(status)) {
strbuf_sprintf(sb, "terminated by signal %u (%s)", WTERMSIG(status), strsignal(WTERMSIG(status)));
#ifdef WCOREDUMP
if (WCOREDUMP(status))
strbuf_puts(sb, " and dumped core");
#endif
} else if (WIFSTOPPED(status))
strbuf_sprintf(sb, "stopped by signal %u (%s)", WSTOPSIG(status), strsignal(WSTOPSIG(status)));
#ifdef WIFCONTINUED
else if (WIFCONTINUED(status))
strbuf_sprintf(sb, "continued by signal %u (SIGCONT)", SIGCONT);
#endif
return sb;
}

View File

@ -32,4 +32,10 @@ strbuf strbuf_append_poll_events(strbuf sb, short events);
*/
strbuf strbuf_append_shell_quotemeta(strbuf sb, const char *word);
/* Append a textual description of a process exit status as produced by wait(2)
* and waitpid(2).
* @author Andrew Bettison <andrew@servalproject.com>
*/
strbuf strbuf_append_exit_status(strbuf sb, int status);
#endif //__STRBUF_HELPERS_H__

View File

@ -941,7 +941,7 @@ _tfw_assert_stdxxx_is() {
'') ln -f "$_tfw_tmp/$qual" "$_tfw_tmp/content";;
*) sed -n -e "${_tfw_opt_line}p" "$_tfw_tmp/$qual" >"$_tfw_tmp/content";;
esac
local message="${_tfw_message:-${_tfw_opt_line:+line $_tfw_opt_line of }$qual of ($executed) is $*}"
local message="${_tfw_message:-${_tfw_opt_line:+line $_tfw_opt_line of }$qual of ($executed) is $(_tfw_shellarg "$@")}"
echo -n "$@" >$_tfw_tmp/stdxxx_is.tmp
if ! cmp --quiet $_tfw_tmp/stdxxx_is.tmp "$_tfw_tmp/content"; then
_tfw_failmsg "assertion failed: $message"

View File

@ -25,6 +25,7 @@ setup() {
setup_servald
assert_no_servald_processes
setup_dnahelper
start_servald_instances +A
}
teardown() {
@ -36,69 +37,366 @@ teardown() {
# Called by start_servald_instances immediately before starting the server
# process in each instance.
configure_servald_server() {
executeOk_servald config set dna.helper "$TFWTMP/dnahelper"
executeOk_servald config set log.show_pid on
executeOk_servald config set log.show_time on
executeOk_servald config set debug.dnahelper on
executeOk_servald config set dna.helper.executable "$dnahelper"
}
setup_dnahelper() {
cat >"$TFWTMP/dnahelper" <<EOF
#!/usr/bin/env python
# Sample DNA Helper application for testing
import sys;
def main():
print "STARTED"
while True:
line = sys.stdin.readline().strip()
if line == "":
# EOF detection is broken :(
break
s = line.split('|')
if len(s) != 3:
print "ERROR"
continue
(token, number, xxx) = s
if number == "12345":
# Multiple results (SID/VoMP results)
print "%s|sid:%s|%s|%s|" % (token, token, number, "Agent A. Smith")
print "%s|sid:%s|%s|%s|" % (token, token, number, "Agent B. Smith")
if number == "5551234":
# Single result, SIP URI
print "%s|sip://5551234@10.1.2.3|%s|%s|" % (token, number, "Will Smith")
if number == "5551001":
# Empty URI field
print "%s||%s|%s|" % (token, number, "Empty URI")
if number == "5551002":
# Empty DID field
print "%s|sip://123@1.2.3.4||%s|" % (token, "Empty DID")
if number == "5551003":
# Empty CALLERID field
print "%s|sip://empty-callerid@1.2.3.4|%s||" % (token, number)
if number == "5551004":
# Excessively long callerid
print "%s|sip://long-callerid@1.2.3.4|%s|askjdhfkjashdfkljahsdflkjhasdljkfhasldjkfhaslkjdfhalskdjfhklajsdhflkajsdhflkjasdhflkjashdflkjashdflkjahsdflkjahsdfjklhasdljkfhasjkdfhakljsdfhklajsdhflkjashdfljkashdflkjashdf|" % (token, number)
if number == "5551005":
# Excessively long DID
print "%s|sip://long-did@1.2.3.4|askjdhfkjashdfkljahsdflkjhasdljkfhasldjkfhaslkjdfhalskdjfhklajsdhflkajsdhflkjasdhflkjashdflkjashdflkjahsdflkjahsdfjklhasdljkfhasjkdfhakljsdfhklajsdhflkjashdfljkashdflkjashdf|%s|" % (token, "Agent Smith")
if number == "5551006":
# Excessively long URI
print "%s|sip://askjdhfkjashdfkljahsdflkjhasdljkfhasldjkfhaslkjdfhalskdjfhklajsdhflkajsdhflkjasdhflkjashdflkjashdflkjahsdflkjahsdfjklhasdljkfhasjkdfhakljsdfhklajsdhflkjashdfljkashdflkjashdfasdjfkjahsdfjkhasdfkjlhasjldkfhajksdhflkjasdhfkljashdfkljahsdfkljhasdfkljhasdlkjfhasdlkjfhaslkjdfhakljsdhfklajshdfkljashdfljkashdflkjashdflkjahsdfkjlahsdflkjhasdfljkhasdkfjlhaslkdjfhaslkjdfhaklsjdfhaklsjdhflkajsdhflkjasdhflkjashdfljkashdfkljashdflkjashdflkjashdflkjashdflkjashdflkjashdfljkahsdflkjahsdfjklahsdfljkahsdflkjhasdflkjhasdjkfhaskjdlfhaslkjdfhaskljdfhasljkdfhalskdfhalkjsdhflkjasdhflkjahsdflkjahsdflkjahsdflkjhasdflkjahsdflkjahsdflkjahsdfkljashdflkajshdflkajsdhflaksjdfhalksjdfhlasdkjfh|%s|%s|" % (token, number, "Agent Smith")
if number == "5551007":
# Incorrect token
print "cheeseburger|sip://incorrect-token@1.2.3.4|%s||" % (token, number)
print "DONE"
if __name__ == "__main__":
main()
export SID_JOE_A=1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEA
export SID_JOE_B=1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEB
export SID_JOE_C=1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEC
export SID_JOE_D=1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDED
export SID_JOE_E=1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEE
export SID_JOE_F=1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF
export SID_ECCLES=1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDE0
dnahelper="$TFWTMP/dnahelper"
cat >"$dnahelper" <<'EOF'
#!/bin/sh
echo STARTED
while read line
do
token="${line%%|*}"
line="${line#*|}"
did="${line%%|*}"
line="${line#*|}"
case "$token|$did|$line" in
'|'*'|')
echo "empty token" >&2
;;
*'||')
echo "empty DID" >&2
;;
*'|00000|')
# For verification during setup
echo "$token|A|$did|B|"
;;
*'|00001|')
# One valid reply
echo "$token|sip://$SID_JOE_A@10.1.1.1|$did|Joe A. Bloggs|"
;;
*'|00002|')
# Two valid replies
echo "$token|sip://$SID_JOE_A@10.1.1.1|$did|Joe A. Bloggs|"
sleep 0.1
echo "$token|sip://$SID_JOE_B@10.1.1.1|$did|Joe B. Bloggs|"
sleep 0.1
;;
*'|00003|')
# Three valid replies
echo "$token|sip://$SID_JOE_A@10.1.1.1|$did|Joe A. Bloggs|"
sleep 0.1
echo "$token|sip://$SID_JOE_B@10.1.1.1|$did|Joe B. Bloggs|"
sleep 0.1
echo "$token|sip://$SID_JOE_C@10.1.1.1|$did|Joe C. Bloggs|"
sleep 0.1
;;
*'|00004|')
# Empty URI
echo "$token||$did|Eccles|"
;;
*'|000051|')
# Malformed URI
echo "$token|Bluebottle|$did|Eccles|"
;;
*'|000052|')
# Malformed URI
echo "$token|sip://Sea goon|$did|Eccles|"
;;
*'|000053|')
# Malformed URI
echo "$token|sip:|$did|Eccles|"
;;
*'|000061|')
# Mismatched token
echo "$SID_ECCLES|did://$SID_ECCLES/$did|$did|Eccles|"
;;
*'|000062|')
# Empty token
echo "|did://$SID_ECCLES/$did|$did|Eccles|"
;;
*'|000063|')
# Invalid token (not a SID)
echo "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEX|did://$SID_ECCLES/$did|$did|Eccles|"
;;
*'|000064|')
# Long token
echo "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF0|did://$SID_ECCLES/$did|$did|Eccles|"
;;
*'|000065|')
# Short token
echo "1234567890ABCDEF1234567890ABCDEF1234567890ABCDEF1234567890ABCDE|did://$SID_ECCLES/$did|$did|Eccles|"
;;
*'|000071|')
# Mismatched DID
echo "$token|sip://$SID_ECCLES/$did|99999|Eccles|"
;;
*'|000072|')
# Empty DID
echo "$token|sip://$SID_ECCLES/$did||Eccles|"
;;
*'|000073|')
# Invalid DID
echo "$token|sip://$SID_ECCLES/$did|9999X|Eccles|"
;;
*'|000074|')
# Long DID
echo "$token|sip://$SID_ECCLES/$did|123456789012345678901234567890123|Eccles|"
;;
*'|000075|')
# Short DID
echo "$token|sip://$SID_ECCLES/$did|9999|Eccles|"
;;
*'|000081|')
# Malformed reply, missing final delimiter
echo "$token|sip://$SID_ECCLES/$did|9999|Eccles"
;;
*'|000082|')
# Malformed reply, long name
echo "$token|sip://$SID_ECCLES/$did|9999|Abcd efgh ijkl mnop qrst uvwx yzab cdef ghij klmn opqr stuv wxyz abcd efgh ijkl|"
;;
*'|000083|')
# Malformed reply, empty line
echo
;;
*'|000084|')
# Malformed reply, missing \n (which swallows the following DONE line)
echo -n "$token|sip://$SID_JOE_A@10.1.1.1|$did|Joe A. Bloggs|"
;;
*'|000085|')
# Malformed reply, line too long
for i in 1 2 3 4 5 6 7 8 9 0; do
echo -n 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
echo -n 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
echo -n 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
echo -n 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
echo -n 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
echo -n 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
echo -n 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
echo -n 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
echo -n 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
echo -n 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
done
echo
;;
*'|00009|')
# Take too long to respond
sleep 2
echo "$token|sip://$SID_JOE_D@10.1.1.1|$did|Joe D. Bloggs|"
;;
*'|00010|')
# Spurious output after DONE
echo "$token|sip://$SID_JOE_E@10.1.1.1|$did|Joe E. Bloggs|"
echo DONE
echo "$token|sip://$SID_JOE_F@10.1.1.1|$did|Joe F. Bloggs|"
;;
*'|00011|')
# Die unexpectedly
echo "goodbye cruel world" >&2
exit 42
;;
*'|'*'|')
echo "token=$token did=$did line=$line" >&2
;;
*)
echo "garbage line" >&2
;;
esac
echo DONE
done
EOF
chmod 0755 "$dnahelper"
executeOk "$dnahelper" <<EOF
ToKeN|00000|
EOF
assertStdoutIs -e "STARTED\nToKeN|A|00000|B|\nDONE\n"
}
doc_MultiServer="Start three servald servers with dna helper"
test_MultiServer() {
start_servald_instances +A +B +C
doc_ExecError="Non-existent DNA helper executable"
setup_ExecError() {
setup_servald
assert_no_servald_processes
dnahelper=/non/existent
assert [ ! -e "$dnahelper" ]
start_servald_instances +A
}
test_ExecError() {
executeOk_servald dna lookup 12345
}
doc_ReplyOk1="DNA helper returns one valid reply"
test_ReplyOk1() {
executeOk_servald dna lookup 00001
assertStdoutIs -e "sip://$SID_JOE_A@10.1.1.1:00001:Joe A. Bloggs\n"
}
doc_ReplyOk2="DNA helper returns two valid replies"
test_ReplyOk2() {
executeOk_servald dna lookup 00002
assertStdoutIs -e "sip://$SID_JOE_A@10.1.1.1:00002:Joe A. Bloggs\nsip://$SID_JOE_B@10.1.1.1:00002:Joe B. Bloggs\n"
}
doc_ReplyOk3="DNA helper returns three valid replies"
test_ReplyOk3() {
executeOk_servald dna lookup 00003
assertStdoutIs -e "sip://$SID_JOE_A@10.1.1.1:00003:Joe A. Bloggs\nsip://$SID_JOE_B@10.1.1.1:00003:Joe B. Bloggs\nsip://$SID_JOE_C@10.1.1.1:00003:Joe C. Bloggs\n"
}
doc_UriEmpty="DNA helper returns empty URI"
test_UriEmpty() {
executeOk_servald dna lookup 00004
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*empty URI'
}
doc_UriInvalid1="DNA helper returns invalid URI, missing scheme"
test_UriInvalid1() {
executeOk_servald dna lookup 000051
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*Bluebottle.*invalid URI'
}
doc_UriInvalid2="DNA helper returns invalid URI, invalid char"
test_UriInvalid2() {
executeOk_servald dna lookup 000052
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*sip://Sea goon.*invalid URI'
}
doc_UriInvalid3="DNA helper returns invalid URI, empty hierarchical part"
test_UriInvalid3() {
executeOk_servald dna lookup 000053
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*sip:.*invalid URI'
}
doc_TokenMismatch="DNA helper returns mismatched token"
test_TokenMismatch() {
executeOk_servald dna lookup 000061
assertStdoutIs ""
assertGrep --matches=0 "$LOGA" 'ERROR:.*DNAHELPER'
}
doc_TokenEmpty="DNA helper returns empty token"
test_TokenEmpty() {
executeOk_servald dna lookup 000062
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*empty token'
}
doc_TokenInvalid="DNA helper returns invalid token"
test_TokenInvalid() {
executeOk_servald dna lookup 000063
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*invalid token'
}
doc_TokenInvalidLong="DNA helper returns invalid token, too long"
test_TokenInvalidLong() {
executeOk_servald dna lookup 000064
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply.*invalid'
}
doc_TokenInvalidShort="DNA helper returns invalid token, too short"
test_TokenInvalidShort() {
executeOk_servald dna lookup 000065
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*invalid token'
}
doc_DidMismatch="DNA helper returns mismatched DID"
test_DidMismatch() {
executeOk_servald dna lookup 000071
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*mismatched DID'
}
doc_DidEmpty="DNA helper returns empty DID"
test_DidEmpty() {
executeOk_servald dna lookup 000072
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*empty DID'
}
doc_DidInvalid="DNA helper returns invalid DID"
test_DidInvalid() {
executeOk_servald dna lookup 000073
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*invalid DID'
}
doc_DidInvalidLong="DNA helper returns invalid DID, too long"
test_DidInvalidLong() {
executeOk_servald dna lookup 000074
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply.*invalid'
}
doc_DidInvalidShort="DNA helper returns invalid DID, too short"
test_DidInvalidShort() {
executeOk_servald dna lookup 000075
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*invalid DID'
}
doc_ReplyInvalidMissingDelim="DNA helper returns invalid reply, missing delimiter"
test_ReplyInvalidMissingDelim() {
executeOk_servald dna lookup 000081
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply.*invalid'
}
doc_ReplyInvalidLongName="DNA helper returns invalid reply, name too long"
test_ReplyInvalidLongName() {
executeOk_servald dna lookup 000082
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply.*invalid'
}
doc_ReplyInvalidEmpty="DNA helper returns invalid reply, empty line"
test_ReplyInvalidEmpty() {
executeOk_servald dna lookup 000083
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply "\\n" invalid'
}
doc_ReplyInvalidMissingNewline="DNA helper returns invalid reply, missing newline"
test_ReplyInvalidMissingNewline() {
executeOk_servald dna lookup 000084
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply.*spurious'
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply timeout'
}
doc_HelperTimeout="DNA helper process takes too long to reply and is restarted"
test_HelperTimeout() {
executeOk_servald dna lookup 00009
assertStdoutIs ""
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply timeout'
assertGrep "$LOGA" 'INFO:.*DNAHELPER.*process.*terminated by signal 15'
executeOk_servald dna lookup 00001
assertStdoutIs -e "sip://$SID_JOE_A@10.1.1.1:00001:Joe A. Bloggs\n"
}
doc_ReplySpurious="DNA helper spurious output after DONE is ignored"
test_ReplySpurious() {
executeOk_servald dna lookup 00010
assertStdoutIs -e "sip://$SID_JOE_E@10.1.1.1:00010:Joe E. Bloggs\n"
assertGrep "$LOGA" 'WARN:.*DNAHELPER.*spurious output'
executeOk_servald dna lookup 00001
assertStdoutIs -e "sip://$SID_JOE_A@10.1.1.1:00001:Joe A. Bloggs\n"
}
doc_HelperDies="DNA helper process dies unexpectedly and is restarted"
test_HelperDies() {
executeOk_servald dna lookup 00011
assertStdoutIs ""
assertGrep "$LOGA" 'INFO:.*DNAHELPER.*process.*exited normally with status 42'
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*"goodbye cruel world\\n"'
executeOk_servald dna lookup 00001
assertStdoutIs -e "sip://$SID_JOE_A@10.1.1.1:00001:Joe A. Bloggs\n"
}
runTests "$@"