Improve Rhizome HTTP API diagnostics

Add RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG enum option to indicate
that the manifest exceeded 8 KiB in size.

Refactor rhizome_add_manifest() and rhizome_manifest_finalise()
to return 'struct rhizome_bundle_result' instead of 'enum
rhizome_bundle_status', so that that their detailed failure messages
can reach the HTTP API layer instead of just being logged.

Fix HTTP response status codes produced Rhizome direct HTTP requests
to be consistent with the Rhizome RESTful API.
This commit is contained in:
Andrew Bettison 2015-12-07 22:00:52 +10:30
parent 74735339aa
commit 3f8f0f6fc7
9 changed files with 219 additions and 174 deletions

View File

@ -86,6 +86,7 @@ static enum meshms_status get_my_conversation_bundle(const sid_t *my_sidp, rhizo
case RHIZOME_BUNDLE_STATUS_OLD:
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
WARNF("Cannot create conversation manifest: %s", alloca_rhizome_bundle_result(result));
rhizome_bundle_result_free(&result);
return MESHMS_STATUS_PROTOCOL_FAULT;
@ -235,10 +236,12 @@ static enum meshms_status create_ply(const sid_t *my_sid, struct meshms_conversa
WHYF("Error creating ply manifest: %s", alloca_rhizome_bundle_result(result));
rhizome_bundle_result_free(&result);
return MESHMS_STATUS_ERROR;
case RHIZOME_BUNDLE_STATUS_BUSY:
// TODO
case RHIZOME_BUNDLE_STATUS_OLD:
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_BUSY:
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
WARNF("Cannot create ply manifest: %s", alloca_rhizome_bundle_result(result));
rhizome_bundle_result_free(&result);
return MESHMS_STATUS_PROTOCOL_FAULT;
@ -416,9 +419,8 @@ static enum meshms_status append_meshms_buffer(const sid_t *my_sid, struct meshm
status = MESHMS_STATUS_ERROR;
goto end;
}
enum rhizome_bundle_status bstatus = rhizome_manifest_finalise(m, &mout, 1);
DEBUGF(meshms, "bstatus=%d", bstatus);
switch (bstatus) {
struct rhizome_bundle_result result = rhizome_manifest_finalise(m, &mout, 1);
switch (result.status) {
case RHIZOME_BUNDLE_STATUS_ERROR:
// error has already been logged
status = MESHMS_STATUS_ERROR;
@ -430,24 +432,33 @@ static enum meshms_status append_meshms_buffer(const sid_t *my_sid, struct meshm
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_OLD:
status = MESHMS_STATUS_PROTOCOL_FAULT;
WHYF("MeshMS ply manifest (version=%"PRIu64") gazumped by Rhizome store (version=%"PRIu64")",
WARNF("MeshMS ply manifest (version=%"PRIu64") gazumped by Rhizome store (version=%"PRIu64")",
m->version, mout->version);
break;
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
status = MESHMS_STATUS_PROTOCOL_FAULT;
WARNF("MeshMS ply manifest evicted from store");
break;
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
status = MESHMS_STATUS_PROTOCOL_FAULT;
WHYF("MeshMS ply manifest not consistent with payload");
WARNF("MeshMS ply manifest not consistent with payload");
break;
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_READONLY:
status = MESHMS_STATUS_PROTOCOL_FAULT;
WHYF("MeshMS ply manifest is not signed");
WARNF("MeshMS ply manifest is not signed");
break;
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
status = MESHMS_STATUS_PROTOCOL_FAULT;
WHYF("MeshMS ply manifest is invalid");
WARNF("MeshMS ply manifest is invalid");
break;
case RHIZOME_BUNDLE_STATUS_BUSY:
status = MESHMS_STATUS_PROTOCOL_FAULT;
WARNF("MeshMS ply manifest not stored due to database locking");
break;
default:
FATALF("bstatus=%d", bstatus);
}
rhizome_bundle_result_free(&result);
end:
if (mout && mout!=m)
rhizome_manifest_free(mout);
@ -738,8 +749,8 @@ static enum meshms_status write_known_conversations(rhizome_manifest *m, struct
goto end;
rhizome_manifest_set_filehash(m, &write.id);
enum rhizome_bundle_status bstatus = rhizome_manifest_finalise(m, &mout, 1);
switch (bstatus) {
struct rhizome_bundle_result result = rhizome_manifest_finalise(m, &mout, 1);
switch (result.status) {
case RHIZOME_BUNDLE_STATUS_ERROR:
// error is already logged
break;
@ -749,25 +760,34 @@ static enum meshms_status write_known_conversations(rhizome_manifest *m, struct
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_OLD:
WHYF("MeshMS conversation manifest (version=%"PRIu64") gazumped by Rhizome store (version=%"PRIu64")",
m->version, mout->version);
status = MESHMS_STATUS_PROTOCOL_FAULT;
WARNF("MeshMS conversation manifest (version=%"PRIu64") gazumped by Rhizome store (version=%"PRIu64")",
m->version, mout->version);
break;
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
status = MESHMS_STATUS_PROTOCOL_FAULT;
WARNF("MeshMS ply manifest evicted from store");
break;
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
WHY("MeshMS conversation manifest not consistent with payload");
status = MESHMS_STATUS_PROTOCOL_FAULT;
WARN("MeshMS conversation manifest not consistent with payload");
break;
case RHIZOME_BUNDLE_STATUS_FAKE:
WHY("MeshMS conversation manifest is not signed");
case RHIZOME_BUNDLE_STATUS_READONLY:
status = MESHMS_STATUS_PROTOCOL_FAULT;
WARN("MeshMS conversation manifest is not signed");
break;
case RHIZOME_BUNDLE_STATUS_INVALID:
WHY("MeshMS conversation manifest is invalid");
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
status = MESHMS_STATUS_PROTOCOL_FAULT;
WARN("MeshMS conversation manifest is invalid");
break;
case RHIZOME_BUNDLE_STATUS_BUSY:
status = MESHMS_STATUS_PROTOCOL_FAULT;
WARNF("MeshMS conversation manifest not stored due to database locking");
break;
default:
FATALF("bstatus=%d", bstatus);
}
rhizome_bundle_result_free(&result);
end:
if (meshms_failed(status))
rhizome_fail_write(&write);

View File

@ -229,6 +229,7 @@ struct rhizome_bundle_result rhizome_manifest_add_file(int appending,
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
FATALF("rhizome_retrieve_manifest() returned %s", rhizome_bundle_status_message(result.status));
}
}
@ -325,16 +326,7 @@ error:
case RHIZOME_BUNDLE_STATUS_NEW:
*mout = new_manifest;
return result;
case RHIZOME_BUNDLE_STATUS_ERROR:
case RHIZOME_BUNDLE_STATUS_SAME:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_OLD:
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_BUSY:
default:
if (new_manifest && new_manifest != m && new_manifest != existing_manifest)
rhizome_manifest_free(new_manifest);
if (existing_manifest)
@ -490,7 +482,7 @@ int rhizome_manifest_add_bundle_key(rhizome_manifest *m)
rhizome_manifest_del_bundle_key(m);
switch (m->authorship) {
case AUTHOR_UNKNOWN:
WHYF("Cannot set BK because author=%s is not in keyring", alloca_tohex_sid_t(m->author));
INFOF("Cannot set BK because author=%s is not in keyring", alloca_tohex_sid_t(m->author));
break;
case AUTHENTICATION_ERROR:
WHY("Cannot set BK due to error");
@ -581,12 +573,12 @@ enum rhizome_bundle_status rhizome_manifest_check_stored(rhizome_manifest *m, rh
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
enum rhizome_bundle_status rhizome_add_manifest(rhizome_manifest *m, rhizome_manifest **mout)
enum rhizome_bundle_status rhizome_add_manifest_to_store(rhizome_manifest *m, rhizome_manifest **mout)
{
if (mout == NULL)
DEBUGF(rhizome, "rhizome_add_manifest(m=manifest[%d](%p), mout=NULL)", m->manifest_record_number, m);
DEBUGF(rhizome, "%s(m=manifest[%d](%p), mout=NULL)", __func__, m->manifest_record_number, m);
else
DEBUGF(rhizome, "rhizome_add_manifest(m=manifest[%d](%p), *mout=manifest[%d](%p))", m->manifest_record_number, m, *mout ? (*mout)->manifest_record_number : -1, *mout);
DEBUGF(rhizome, "%s(m=manifest[%d](%p), *mout=manifest[%d](%p))", __func__, m->manifest_record_number, m, *mout ? (*mout)->manifest_record_number : -1, *mout);
if (!m->finalised && !rhizome_manifest_validate(m))
return RHIZOME_BUNDLE_STATUS_INVALID;
assert(m->finalised);
@ -597,7 +589,7 @@ enum rhizome_bundle_status rhizome_add_manifest(rhizome_manifest *m, rhizome_man
return WHY("Payload has not been stored");
enum rhizome_bundle_status status = rhizome_manifest_check_stored(m, mout);
if (status == RHIZOME_BUNDLE_STATUS_NEW && rhizome_store_manifest(m) == -1)
return -1;
status = RHIZOME_BUNDLE_STATUS_ERROR;
return status;
}
@ -615,17 +607,18 @@ int rhizome_saw_voice_traffic()
const char *rhizome_bundle_status_message(enum rhizome_bundle_status status)
{
switch (status) {
case RHIZOME_BUNDLE_STATUS_NEW: return "Bundle new to store";
case RHIZOME_BUNDLE_STATUS_SAME: return "Bundle already in store";
case RHIZOME_BUNDLE_STATUS_DUPLICATE: return "Duplicate bundle already in store";
case RHIZOME_BUNDLE_STATUS_OLD: return "Newer bundle already in store";
case RHIZOME_BUNDLE_STATUS_INVALID: return "Invalid manifest";
case RHIZOME_BUNDLE_STATUS_FAKE: return "Manifest signature does not verify";
case RHIZOME_BUNDLE_STATUS_INCONSISTENT: return "Manifest inconsistent with supplied payload";
case RHIZOME_BUNDLE_STATUS_NO_ROOM: return "No room in store for bundle";
case RHIZOME_BUNDLE_STATUS_READONLY: return "Bundle is read-only";
case RHIZOME_BUNDLE_STATUS_BUSY: return "Internal error";
case RHIZOME_BUNDLE_STATUS_ERROR: return "Internal error";
case RHIZOME_BUNDLE_STATUS_NEW: return "Bundle new to store";
case RHIZOME_BUNDLE_STATUS_SAME: return "Bundle already in store";
case RHIZOME_BUNDLE_STATUS_DUPLICATE: return "Duplicate bundle already in store";
case RHIZOME_BUNDLE_STATUS_OLD: return "Newer bundle already in store";
case RHIZOME_BUNDLE_STATUS_INVALID: return "Invalid manifest";
case RHIZOME_BUNDLE_STATUS_FAKE: return "Manifest signature does not verify";
case RHIZOME_BUNDLE_STATUS_INCONSISTENT: return "Manifest inconsistent with supplied payload";
case RHIZOME_BUNDLE_STATUS_NO_ROOM: return "No room in store for bundle";
case RHIZOME_BUNDLE_STATUS_READONLY: return "Bundle is read-only";
case RHIZOME_BUNDLE_STATUS_BUSY: return "Internal error";
case RHIZOME_BUNDLE_STATUS_ERROR: return "Internal error";
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG: return "Manifest too big";
}
return NULL;
}
@ -669,17 +662,18 @@ void rhizome_bundle_result_free(struct rhizome_bundle_result *resultp)
static const char *rhizome_bundle_status_symbol(enum rhizome_bundle_status status)
{
switch (status) {
case RHIZOME_BUNDLE_STATUS_NEW: return "NEW";
case RHIZOME_BUNDLE_STATUS_SAME: return "SAME";
case RHIZOME_BUNDLE_STATUS_DUPLICATE: return "DUPLICATE";
case RHIZOME_BUNDLE_STATUS_OLD: return "OLD";
case RHIZOME_BUNDLE_STATUS_INVALID: return "INVALID";
case RHIZOME_BUNDLE_STATUS_FAKE: return "FAKE";
case RHIZOME_BUNDLE_STATUS_INCONSISTENT: return "INCONSISTENT";
case RHIZOME_BUNDLE_STATUS_NO_ROOM: return "NO_ROOM";
case RHIZOME_BUNDLE_STATUS_READONLY: return "READONLY";
case RHIZOME_BUNDLE_STATUS_BUSY: return "BUSY";
case RHIZOME_BUNDLE_STATUS_ERROR: return "ERROR";
case RHIZOME_BUNDLE_STATUS_NEW: return "NEW";
case RHIZOME_BUNDLE_STATUS_SAME: return "SAME";
case RHIZOME_BUNDLE_STATUS_DUPLICATE: return "DUPLICATE";
case RHIZOME_BUNDLE_STATUS_OLD: return "OLD";
case RHIZOME_BUNDLE_STATUS_INVALID: return "INVALID";
case RHIZOME_BUNDLE_STATUS_FAKE: return "FAKE";
case RHIZOME_BUNDLE_STATUS_INCONSISTENT: return "INCONSISTENT";
case RHIZOME_BUNDLE_STATUS_NO_ROOM: return "NO_ROOM";
case RHIZOME_BUNDLE_STATUS_READONLY: return "READONLY";
case RHIZOME_BUNDLE_STATUS_BUSY: return "BUSY";
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG: return "MANIFEST_TOO_BIG";
case RHIZOME_BUNDLE_STATUS_ERROR: return "ERROR";
}
FATALF("status=%d", (int)status);
}
@ -696,6 +690,7 @@ static void log_rhizome_bundle_result(struct __sourceloc __whence, struct rhizom
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
DEBUG(rhizome, alloca_rhizome_bundle_result(result));
return;
case RHIZOME_BUNDLE_STATUS_BUSY:
@ -772,6 +767,7 @@ strbuf strbuf_append_rhizome_bundle_result(strbuf sb, struct rhizome_bundle_resu
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_BUSY:
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
case RHIZOME_BUNDLE_STATUS_ERROR:
strbuf_puts(sb, "RHIZOME_BUNDLE_STATUS_");
strbuf_puts(sb, rhizome_bundle_status_symbol(result.status));

View File

@ -412,6 +412,7 @@ enum rhizome_bundle_status {
RHIZOME_BUNDLE_STATUS_NO_ROOM = 7, // doesn't fit; store may contain more important bundles
RHIZOME_BUNDLE_STATUS_READONLY = 8, // cannot modify manifest; secret unknown
RHIZOME_BUNDLE_STATUS_BUSY = 9, // the database is currently busy
RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG = 10, // manifest + signature exceeds size limit
};
// Useful for initialising a variable then checking later that it was set to a
@ -443,8 +444,8 @@ struct rhizome_bundle_result _rhizome_bundle_result_strdup(struct __sourceloc, e
struct rhizome_bundle_result _rhizome_bundle_result_sprintf(struct __sourceloc, enum rhizome_bundle_status, const char *fmt, ...);
#define rhizome_bundle_result(status) _rhizome_bundle_result(__WHENCE__, status)
#define rhizome_bundle_result_static(status, str) _rhizome_bundle_result_static(__WHENCE__, status, str);
#define rhizome_bundle_result_strdup(status, str) _rhizome_bundle_result_strdup(__WHENCE__, status, str);
#define rhizome_bundle_result_static(status, str) _rhizome_bundle_result_static(__WHENCE__, status, str)
#define rhizome_bundle_result_strdup(status, str) _rhizome_bundle_result_strdup(__WHENCE__, status, str)
#define rhizome_bundle_result_sprintf(status, fmt, ...) _rhizome_bundle_result_sprintf(__WHENCE__, status, fmt, ## __VA_ARGS__);
// Functions for extracting information from a struct rhizome_bundle_result.
@ -475,7 +476,6 @@ const char *rhizome_payload_status_message(enum rhizome_payload_status);
const char *rhizome_payload_status_message_nonnull(enum rhizome_payload_status);
int rhizome_write_manifest_file(rhizome_manifest *m, const char *filename, char append);
int rhizome_manifest_selfsign(rhizome_manifest *m);
int rhizome_read_manifest_from_file(rhizome_manifest *m, const char *filename);
int rhizome_manifest_validate(rhizome_manifest *m);
const char *rhizome_manifest_validate_reason(rhizome_manifest *m);
@ -513,9 +513,9 @@ void rhizome_find_bundle_author_and_secret(rhizome_manifest *m);
int rhizome_lookup_author(rhizome_manifest *m);
void rhizome_authenticate_author(rhizome_manifest *m);
enum rhizome_bundle_status rhizome_manifest_finalise(rhizome_manifest *m, rhizome_manifest **m_out, int deduplicate);
struct rhizome_bundle_result rhizome_manifest_finalise(rhizome_manifest *m, rhizome_manifest **m_out, int deduplicate);
enum rhizome_bundle_status rhizome_manifest_check_stored(rhizome_manifest *m, rhizome_manifest **m_out);
enum rhizome_bundle_status rhizome_add_manifest(rhizome_manifest *m_in, rhizome_manifest **m_out);
enum rhizome_bundle_status rhizome_add_manifest_to_store(rhizome_manifest *m_in, rhizome_manifest **m_out);
void rhizome_bytes_to_hex_upper(unsigned const char *in, char *out, int byteCount);
int rhizome_find_privatekey(rhizome_manifest *m);

View File

@ -1322,9 +1322,10 @@ void _rhizome_manifest_free(struct __sourceloc __whence, rhizome_manifest *m)
return;
}
/* Convert variable list into manifest text body and compute the hash. Do not sign.
/* Converts the variable list into manifest text body and computes the hash. Does not sign.
* Returns 0 if successful, -1 if the result exceeds the manifest size limit.
*/
static int rhizome_manifest_pack_variables(rhizome_manifest *m)
static struct rhizome_bundle_result rhizome_manifest_pack_variables(rhizome_manifest *m)
{
assert(m->var_count <= NELS(m->vars));
strbuf sb = strbuf_local_buf(m->manifestdata);
@ -1335,42 +1336,47 @@ static int rhizome_manifest_pack_variables(rhizome_manifest *m)
strbuf_puts(sb, m->values[i]);
strbuf_putc(sb, '\n');
}
if (strbuf_overrun(sb))
return WHYF("Manifest overflow: body of %zu bytes exceeds limit of %zu", strbuf_count(sb) + 1, sizeof m->manifestdata);
if (strbuf_overrun(sb)) {
return rhizome_bundle_result_sprintf(
RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG,
"Manifest too big: body of %zu bytes exceeds limit of %zu",
strbuf_count(sb) + 1, sizeof m->manifestdata);
}
m->manifest_body_bytes = strbuf_len(sb) + 1;
DEBUGF(rhizome, "Repacked variables into manifest: %zu bytes", m->manifest_body_bytes);
m->manifest_all_bytes = m->manifest_body_bytes;
m->selfSigned = 0;
return 0;
return rhizome_bundle_result(RHIZOME_BUNDLE_STATUS_NEW);
}
/* Sign this manifest using it's own BID secret key. Manifest must not already be signed.
* Manifest body hash must already be computed.
*/
int rhizome_manifest_selfsign(rhizome_manifest *m)
static struct rhizome_bundle_result rhizome_manifest_selfsign(rhizome_manifest *m)
{
assert(m->manifest_body_bytes > 0);
assert(m->manifest_body_bytes <= sizeof m->manifestdata);
assert(m->manifestdata[m->manifest_body_bytes - 1] == '\0');
assert(m->manifest_body_bytes == m->manifest_all_bytes); // no signature yet
if (!m->haveSecret)
return WHY("Need private key to sign manifest");
return rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_READONLY, "Missing bundle secret");
crypto_hash_sha512(m->manifesthash, m->manifestdata, m->manifest_body_bytes);
rhizome_signature sig;
if (rhizome_sign_hash(m, &sig) == -1)
return WHY("rhizome_sign_hash() failed");
return rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_ERROR, "rhizome_sign_hash() failed");
assert(sig.signatureLength > 0);
/* Append signature to end of manifest data */
if (sig.signatureLength + m->manifest_body_bytes > sizeof m->manifestdata)
return WHYF("Manifest overflow: body %zu + signature %zu bytes exceeds limit of %zu",
m->manifest_body_bytes,
sig.signatureLength,
sizeof m->manifestdata
);
if (sig.signatureLength + m->manifest_body_bytes > sizeof m->manifestdata) {
return rhizome_bundle_result_sprintf(RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG,
"Manifest too big: body of %zu + signature of %zu bytes exceeds limit of %zu",
m->manifest_body_bytes,
sig.signatureLength,
sizeof m->manifestdata);
}
bcopy(sig.signature, m->manifestdata + m->manifest_body_bytes, sig.signatureLength);
m->manifest_all_bytes = m->manifest_body_bytes + sig.signatureLength;
m->selfSigned = 1;
return 0;
return rhizome_bundle_result(RHIZOME_BUNDLE_STATUS_NEW);
}
int rhizome_write_manifest_file(rhizome_manifest *m, const char *path, char append)
@ -1410,12 +1416,15 @@ int rhizome_manifest_dump(rhizome_manifest *m, const char *msg)
return 0;
}
enum rhizome_bundle_status rhizome_manifest_finalise(rhizome_manifest *m, rhizome_manifest **mout, int deduplicate)
struct rhizome_bundle_result rhizome_manifest_finalise(rhizome_manifest *m, rhizome_manifest **mout, int deduplicate)
{
IN();
assert(*mout == NULL);
if (!m->finalised && !rhizome_manifest_validate(m))
RETURN(RHIZOME_BUNDLE_STATUS_INVALID);
if (!m->finalised) {
const char *reason = rhizome_manifest_validate_reason(m);
if (reason)
RETURN(rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_INVALID, reason));
}
// The duplicate detection logic exists to filter out files repeatedly added with no existing
// manifest (ie, "de-bounce" for the "Add File" user interface action).
// 1. If a manifest was supplied with a bundle ID, don't check for a duplicate.
@ -1427,13 +1436,13 @@ enum rhizome_bundle_status rhizome_manifest_finalise(rhizome_manifest *m, rhizom
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
assert(*mout != NULL);
assert(*mout != m);
RETURN(status);
RETURN(rhizome_bundle_result(status));
case RHIZOME_BUNDLE_STATUS_ERROR:
if (*mout != NULL && *mout != m) {
rhizome_manifest_free(*mout);
*mout = NULL;
}
RETURN(status);
RETURN(rhizome_bundle_result(status));
case RHIZOME_BUNDLE_STATUS_NEW:
break;
default:
@ -1444,18 +1453,22 @@ enum rhizome_bundle_status rhizome_manifest_finalise(rhizome_manifest *m, rhizom
*mout = m;
/* Convert to final form for signing and writing to disk */
if (rhizome_manifest_pack_variables(m))
RETURN(WHY("Could not convert manifest to wire format"));
struct rhizome_bundle_result result = rhizome_manifest_pack_variables(m);
if (result.status != RHIZOME_BUNDLE_STATUS_NEW)
RETURN(result);
rhizome_bundle_result_free(&result);
/* Sign it */
assert(!m->selfSigned);
if (rhizome_manifest_selfsign(m))
RETURN(WHY("Could not sign manifest"));
assert(m->selfSigned);
result = rhizome_manifest_selfsign(m);
if (result.status == RHIZOME_BUNDLE_STATUS_NEW) {
assert(m->selfSigned);
rhizome_bundle_result_free(&result);
/* mark manifest as finalised */
result.status = rhizome_add_manifest_to_store(m, mout);
}
/* mark manifest as finalised */
enum rhizome_bundle_status status = rhizome_add_manifest(m, mout);
RETURN(status);
RETURN(result);
OUT();
}

View File

@ -273,10 +273,12 @@ static int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_cont
if (!rhizome_manifest_validate(m) || m->malformed)
result.status = RHIZOME_BUNDLE_STATUS_INVALID;
else {
result.status = rhizome_manifest_finalise(m, &mout, !force_new);
rhizome_bundle_result_free(&result);
result = rhizome_manifest_finalise(m, &mout, !force_new);
if (mout && mout != m && !rhizome_manifest_validate(mout)) {
WHYF("Stored manifest id=%s is invalid -- overwriting", alloca_tohex_rhizome_bid_t(mout->cryptoSignPublic));
result.status = RHIZOME_BUNDLE_STATUS_NEW;
WHYF("Stored manifest id=%s is invalid -- overwriting", alloca_tohex_rhizome_bid_t(mout->cryptoSignPublic));
rhizome_bundle_result_free(&result);
result = rhizome_bundle_result(RHIZOME_BUNDLE_STATUS_NEW);
}
}
}
@ -305,6 +307,7 @@ static int app_rhizome_add_file(const struct cli_parsed *parsed, struct cli_cont
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_BUSY:
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
status_valid = 1;
break;
// Do not use a default: label! With no default, if a new value is added to the enum, then the

View File

@ -61,6 +61,42 @@ static void rhizome_direct_clear_temporary_files(httpd_request *r)
}
}
static void http_request_rhizome_bundle_status_response(httpd_request *r, struct rhizome_bundle_result result, rhizome_manifest *m)
{
int http_status = 500;
switch (result.status) {
case RHIZOME_BUNDLE_STATUS_NEW:
http_status = 201; // Created
break;
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_SAME:
http_status = 200; // OK
break;
case RHIZOME_BUNDLE_STATUS_OLD:
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
http_status = 202; // Accepted
break;
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
http_status = 422; // Unprocessable Entity
break;
case RHIZOME_BUNDLE_STATUS_BUSY:
http_status = 423; // Locked
break;
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_FAKE:
http_status = 419; // Authentication Timeout
break;
case RHIZOME_BUNDLE_STATUS_ERROR:
break;
}
if (m)
http_request_response_static(&r->http, http_status, CONTENT_TYPE_TEXT, (const char *)m->manifestdata, m->manifest_all_bytes);
else
http_request_simple_response(&r->http, http_status, rhizome_bundle_result_message(result));
}
static int rhizome_direct_import_end(struct http_request *hr)
{
httpd_request *r = (httpd_request *) hr;
@ -85,52 +121,17 @@ static int rhizome_direct_import_end(struct http_request *hr)
alloca_str_toprint(manifest_path),
alloca_str_toprint(payload_path)
);
enum rhizome_bundle_status status = 0;
rhizome_manifest *m = rhizome_new_manifest();
if (!m)
status = WHY("Out of manifests");
else {
status = rhizome_bundle_import_files(m, NULL, manifest_path, payload_path);
rhizome_manifest_free(m);
if (!m) {
http_request_simple_response(&r->http, 429, "Manifest table full"); // Too Many Requests
return 0;
}
struct rhizome_bundle_result result = INVALID_RHIZOME_BUNDLE_RESULT;
result.status = rhizome_bundle_import_files(m, NULL, manifest_path, payload_path);
rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r);
/* report back to caller.
200 = ok, which is probably appropriate for when we already had the bundle.
201 = content created, which is probably appropriate for when we successfully
import a bundle (or if we already have it).
403 = forbidden, which might be appropriate if we refuse to accept it, e.g.,
the import fails due to malformed data etc.
(should probably also indicate if we have a newer version if possible)
*/
switch (status) {
case RHIZOME_BUNDLE_STATUS_NEW:
http_request_simple_response(&r->http, 201, "Bundle succesfully imported");
return 0;
case RHIZOME_BUNDLE_STATUS_SAME:
http_request_simple_response(&r->http, 200, "Bundle already imported"); // OK
return 0;
case RHIZOME_BUNDLE_STATUS_OLD:
http_request_simple_response(&r->http, 202, "Newer bundle already stored"); // Accepted
return 0;
case RHIZOME_BUNDLE_STATUS_INVALID:
http_request_simple_response(&r->http, 422, "Manifest is invalid"); // Unprocessable Entity
return 0;
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
http_request_simple_response(&r->http, 422, "Manifest is inconsistent with file"); // Unprocessable Entity
return 0;
case RHIZOME_BUNDLE_STATUS_FAKE:
http_request_simple_response(&r->http, 403, "Manifest not signed"); // Forbidden
return 0;
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
http_request_simple_response(&r->http, 202, "Not enough space"); // Accepted
return 0;
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
case RHIZOME_BUNDLE_STATUS_ERROR:
case RHIZOME_BUNDLE_STATUS_BUSY:
break;
}
http_request_simple_response(&r->http, 500, "Internal Error: Rhizome import failed");
http_request_rhizome_bundle_status_response(r, result, NULL);
rhizome_bundle_result_free(&result);
return 0;
}
@ -242,50 +243,34 @@ static int rhizome_direct_addfile_end(struct http_request *hr)
http_request_simple_response(&r->http, 500, "Internal Error: Could not store file");
return 0;
}
// If manifest template did not specify a service field, then by default it is "file".
if (!rhizome_is_bk_none(&config.rhizome.api.addfile.bundle_secret_key))
rhizome_apply_bundle_secret(m, &config.rhizome.api.addfile.bundle_secret_key);
// If manifest template did not specify a service field, then by default it is "file".
if (m->service == NULL)
rhizome_manifest_set_service(m, RHIZOME_SERVICE_FILE);
const sid_t *author = is_sid_t_any(config.rhizome.api.addfile.default_author) ? NULL : &config.rhizome.api.addfile.default_author;
struct rhizome_bundle_result result = rhizome_fill_manifest(m, r->u.direct_import.data_file_name, author);
if (result.status != RHIZOME_BUNDLE_STATUS_NEW) {
rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r);
http_request_simple_response(&r->http, 500, result.message);
rhizome_manifest *mout = NULL;
if (result.status == RHIZOME_BUNDLE_STATUS_NEW) {
rhizome_bundle_result_free(&result);
return 0;
}
rhizome_bundle_result_free(&result);
rhizome_manifest_set_crypt(m, PAYLOAD_CLEAR);
// import file contents
// TODO, stream file into database
assert(m->filesize != RHIZOME_SIZE_UNSET);
if (m->filesize > 0) {
if (rhizome_store_payload_file(m, payload_path) != RHIZOME_PAYLOAD_STATUS_NEW) {
rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r);
http_request_simple_response(&r->http, 500, "Internal Error: Could not store file");
return 0;
rhizome_manifest_set_crypt(m, PAYLOAD_CLEAR);
// import file contents
// TODO, stream file into database
assert(m->filesize != RHIZOME_SIZE_UNSET);
if (m->filesize == 0 || rhizome_store_payload_file(m, payload_path) == RHIZOME_PAYLOAD_STATUS_NEW) {
result = rhizome_manifest_finalise(m, &mout, 1);
if (mout)
DEBUGF(rhizome, "Import sans-manifest appeared to succeed");
}
}
rhizome_manifest *mout = NULL;
if (rhizome_manifest_finalise(m, &mout, 1) == -1) {
if (mout && mout != m)
rhizome_manifest_free(mout);
rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r);
http_request_simple_response(&r->http, 500, "Internal Error: Could not finalise manifest");
return 0;
}
DEBUGF(rhizome, "Import sans-manifest appeared to succeed");
/* Respond with the manifest that was added. */
http_request_response_static(&r->http, 200, CONTENT_TYPE_TEXT, (const char *)m->manifestdata, m->manifest_all_bytes);
/* clean up after ourselves */
http_request_rhizome_bundle_status_response(r, result, mout);
/* Clean up after ourselves. */
rhizome_bundle_result_free(&result);
rhizome_direct_clear_temporary_files(r);
if (mout && mout != m)
rhizome_manifest_free(mout);
rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r);
return 0;
} else {
http_request_simple_response(&r->http, 501, "Not Implemented: Rhizome add with manifest");

View File

@ -471,7 +471,7 @@ static int rhizome_import_received_bundle(struct rhizome_manifest *m)
m->manifest_all_bytes, m->sig_count, m->filesize);
if (IF_DEBUG(rhizome_rx))
dump("manifest", m->manifestdata, m->manifest_all_bytes);
enum rhizome_bundle_status status = rhizome_add_manifest(m, NULL);
enum rhizome_bundle_status status = rhizome_add_manifest_to_store(m, NULL);
switch (status) {
case RHIZOME_BUNDLE_STATUS_NEW:
return 0;
@ -483,7 +483,7 @@ static int rhizome_import_received_bundle(struct rhizome_manifest *m)
case RHIZOME_BUNDLE_STATUS_INVALID:
return -1;
default:
FATALF("rhizome_add_manifest() returned %d", status);
FATALF("rhizome_add_manifest_to_store() returned %d", status);
}
}
@ -750,7 +750,7 @@ rhizome_fetch(struct rhizome_fetch_slot *slot, rhizome_manifest *m,
// If the payload is already available, no need to fetch, so import now.
if (result == IMPORTED) {
DEBUGF(rhizome_rx, " fetch not started - payload already present, so importing instead");
if (rhizome_add_manifest(m, NULL) == -1)
if (rhizome_add_manifest_to_store(m, NULL) == -1)
RETURN(WHY("add manifest failed"));
}
RETURN(result);

View File

@ -91,6 +91,7 @@ static int http_request_rhizome_response(struct httpd_request *r, uint16_t http_
break;
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_INCONSISTENT:
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
rhizome_http_status = 422; // Unprocessable Entity
break;
case RHIZOME_BUNDLE_STATUS_BUSY:
@ -453,6 +454,7 @@ static int insert_make_manifest(httpd_request *r)
case RHIZOME_BUNDLE_STATUS_BUSY:
case RHIZOME_BUNDLE_STATUS_FAKE:
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
case RHIZOME_BUNDLE_STATUS_ERROR:
return http_request_rhizome_response(r, 0, NULL);
}
@ -654,7 +656,7 @@ static int restful_rhizome_insert_end(struct http_request *hr)
assert(r->u.insert.write.file_length != RHIZOME_SIZE_UNSET);
if (r->u.insert.appending) {
// For journal appends, the user cannot supply a 'filesize' field. This will have been caught
// by previous logic. The existing manifest should have a 'filesize' field. The new payload
// by previous logic. The manifest should also have a 'filesize' field by now. The new payload
// size should be the sum of 'filesize' and the appended portion.
assert(r->manifest->is_journal);
assert(r->manifest->filesize != RHIZOME_SIZE_UNSET);
@ -721,9 +723,8 @@ static int restful_rhizome_insert_end(struct http_request *hr)
}
rhizome_manifest *mout = NULL;
rhizome_bundle_result_free(&r->bundle_result);
r->bundle_result.status = rhizome_manifest_finalise(r->manifest, &mout, !r->u.insert.force_new);
r->bundle_result = rhizome_manifest_finalise(r->manifest, &mout, !r->u.insert.force_new);
int http_status = 500;
DEBUGF(rhizome, "r->bundle_result = %s", alloca_rhizome_bundle_result(r->bundle_result));
switch (r->bundle_result.status) {
case RHIZOME_BUNDLE_STATUS_NEW:
if (mout && mout != r->manifest)
@ -740,7 +741,8 @@ static int restful_rhizome_insert_end(struct http_request *hr)
http_status = 201;
break;
case RHIZOME_BUNDLE_STATUS_ERROR:
r->bundle_result.message = "Error in manifest finalise";
rhizome_bundle_result_free(&r->bundle_result);
r->bundle_result = rhizome_bundle_result_static(RHIZOME_BUNDLE_STATUS_ERROR, "Error in manifest finalise");
// fall through
case RHIZOME_BUNDLE_STATUS_INVALID:
case RHIZOME_BUNDLE_STATUS_FAKE:
@ -748,6 +750,7 @@ static int restful_rhizome_insert_end(struct http_request *hr)
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_BUSY:
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
if (mout && mout != r->manifest)
rhizome_manifest_free(mout);
rhizome_manifest_free(r->manifest);
@ -755,7 +758,7 @@ static int restful_rhizome_insert_end(struct http_request *hr)
return http_request_rhizome_response(r, 0, NULL);
}
if (http_status == 500)
FATALF("rhizome_manifest_finalise() returned status = %d", r->bundle_result.status);
FATALF("rhizome_manifest_finalise() returned status=%d", r->bundle_result.status);
rhizome_authenticate_author(r->manifest);
http_request_response_static(&r->http, http_status, "rhizome-manifest/text",
(const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes
@ -971,6 +974,7 @@ static void render_manifest_headers(struct http_request *hr, strbuf sb)
case RHIZOME_BUNDLE_STATUS_NO_ROOM:
case RHIZOME_BUNDLE_STATUS_READONLY:
case RHIZOME_BUNDLE_STATUS_BUSY:
case RHIZOME_BUNDLE_STATUS_MANIFEST_TOO_BIG:
case RHIZOME_BUNDLE_STATUS_ERROR:
strbuf_sprintf(sb, "Serval-Rhizome-Result-Bundle-Status-Code: %d\r\n", r->bundle_result.status);
strbuf_puts(sb, "Serval-Rhizome-Result-Bundle-Status-Message: ");

View File

@ -888,6 +888,30 @@ test_RhizomeInsertMissingManifest() {
assert_rhizome_list
}
doc_RhizomeInsertManifestOverflow="HTTP RESTful insert Rhizome bundle, 'manifest' form part overflow"
setup_RhizomeInsertManifestOverflow() {
setup
echo 'File one' >file1
{ echo -n 'foo='; create_file --omit="$LF" - 8185; echo; } >file1.manifest
}
test_RhizomeInsertManifestOverflow() {
execute curl \
--silent --show-error --write-out '%{http_code}' \
--output http.body \
--dump-header http.header \
--basic --user harry:potter \
--form "manifest=@file1.manifest;type=rhizome/manifest;format=\"text+binarysig\"" \
--form "payload=@file1" \
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
tfw_cat http.header http.body
assertExitStatus == 0
assertStdoutIs 422
assertJq http.body 'contains({"http_status_code": 422})'
assertJqGrep --ignore-case http.body '.http_status_message' 'manifest.*too.*big'
executeOk_servald rhizome list
assert_rhizome_list
}
doc_RhizomeInsertIncorrectManifestType="HTTP RESTful insert Rhizome bundle, incorrect 'manifest' content type"
setup_RhizomeInsertIncorrectManifestType() {
setup