2012-01-12 03:38:24 +00:00
|
|
|
/*
|
|
|
|
Serval Distributed Numbering Architecture (DNA)
|
|
|
|
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
|
|
|
|
as published by the Free Software Foundation; either version 2
|
|
|
|
of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
2012-04-12 09:00:05 +00:00
|
|
|
#include <time.h>
|
2012-02-23 02:15:42 +00:00
|
|
|
#include "serval.h"
|
2012-01-12 03:38:24 +00:00
|
|
|
#include "rhizome.h"
|
|
|
|
|
2012-01-13 06:51:06 +00:00
|
|
|
extern int sigPipeFlag;
|
|
|
|
extern int sigIoFlag;
|
|
|
|
|
|
|
|
|
2012-01-12 06:17:24 +00:00
|
|
|
typedef struct rhizome_file_fetch_record {
|
2012-07-02 03:49:54 +00:00
|
|
|
struct sched_ent alarm;
|
2012-01-12 06:17:24 +00:00
|
|
|
rhizome_manifest *manifest;
|
2012-05-23 06:34:00 +00:00
|
|
|
char fileid[RHIZOME_FILEHASH_STRLEN + 1];
|
2012-01-12 06:17:24 +00:00
|
|
|
FILE *file;
|
2012-01-13 06:51:06 +00:00
|
|
|
|
|
|
|
char request[1024];
|
|
|
|
int request_len;
|
|
|
|
int request_ofs;
|
|
|
|
|
2012-06-27 07:24:42 +00:00
|
|
|
long long file_len;
|
|
|
|
long long file_ofs;
|
2012-01-13 06:51:06 +00:00
|
|
|
|
|
|
|
int state;
|
2012-06-27 07:24:42 +00:00
|
|
|
|
|
|
|
#define RHIZOME_FETCH_CONNECTING 1
|
|
|
|
#define RHIZOME_FETCH_SENDINGHTTPREQUEST 2
|
|
|
|
#define RHIZOME_FETCH_RXHTTPHEADERS 3
|
|
|
|
#define RHIZOME_FETCH_RXFILE 4
|
|
|
|
|
2012-01-12 06:17:24 +00:00
|
|
|
} rhizome_file_fetch_record;
|
|
|
|
|
2012-07-02 06:34:00 +00:00
|
|
|
struct profile_total fetch_stats;
|
2012-07-02 05:50:30 +00:00
|
|
|
|
2012-01-12 06:17:24 +00:00
|
|
|
/* List of queued transfers */
|
|
|
|
#define MAX_QUEUED_FILES 4
|
|
|
|
int rhizome_file_fetch_queue_count=0;
|
|
|
|
rhizome_file_fetch_record file_fetch_queue[MAX_QUEUED_FILES];
|
|
|
|
/*
|
|
|
|
Queue a manifest for importing.
|
|
|
|
|
|
|
|
There are three main cases that can occur here:
|
|
|
|
|
|
|
|
1. The manifest has no associated file (filesize=0);
|
|
|
|
2. The associated file is already in our database; or
|
|
|
|
3. The associated file is not already in our database, and so we need
|
|
|
|
to fetch it before we can import it.
|
|
|
|
|
|
|
|
Cases (1) and (2) are more or less identical, and all we need to do is to
|
|
|
|
import the manifest into the database.
|
|
|
|
|
|
|
|
Case (3) requires that we fetch the associated file.
|
|
|
|
|
|
|
|
This is where life gets interesting.
|
|
|
|
|
|
|
|
First, we need to make sure that we can free up enough space in the database
|
|
|
|
for the file.
|
|
|
|
|
|
|
|
Second, we need to work out how we are going to get the file.
|
|
|
|
If we are on an IPv4 wifi network, then HTTP is probably the way to go.
|
|
|
|
If we are not on an IPv4 wifi network, then HTTP is not an option, and we need
|
|
|
|
to use a Rhizome/Overlay protocol to fetch it. It might even be HTTP over MDP
|
|
|
|
(Serval Mesh Datagram Protocol) or MTCP (Serval Mesh Transmission Control Protocol
|
|
|
|
-- yet to be specified).
|
|
|
|
|
|
|
|
For efficiency, the MDP transfer protocol should allow multiple listeners to
|
|
|
|
receive the data. In contrast, it would be nice to have the data auth-crypted, if
|
|
|
|
only to deal with packet errors (but also naughty people who might want to mess
|
|
|
|
with the transfer.
|
|
|
|
|
|
|
|
For HTTP over IPv4, the biggest problem is that we don't know the IPv4 address of
|
|
|
|
the sender, or in fact that the link is over IPv4 and thus that HTTP over IPv4 is
|
2012-01-27 06:41:18 +00:00
|
|
|
an option. We probably need to be passed this information. This has since been
|
|
|
|
incorporated.
|
2012-01-12 06:17:24 +00:00
|
|
|
*/
|
2012-01-27 06:41:18 +00:00
|
|
|
|
|
|
|
/* As defined below uses 64KB */
|
|
|
|
#define RHIZOME_VERSION_CACHE_NYBLS 2 /* 256=2^8=2nybls */
|
|
|
|
#define RHIZOME_VERSION_CACHE_SHIFT 1
|
|
|
|
#define RHIZOME_VERSION_CACHE_SIZE 128
|
|
|
|
#define RHIZOME_VERSION_CACHE_ASSOCIATIVITY 16
|
2012-05-26 12:36:12 +00:00
|
|
|
typedef struct rhizome_manifest_version_cache_slot {
|
|
|
|
unsigned char idprefix[24];
|
|
|
|
long long version;
|
|
|
|
} rhizome_manifest_version_cache_slot;
|
|
|
|
rhizome_manifest_version_cache_slot rhizome_manifest_version_cache
|
|
|
|
[RHIZOME_VERSION_CACHE_SIZE][RHIZOME_VERSION_CACHE_ASSOCIATIVITY];
|
2012-01-27 06:41:18 +00:00
|
|
|
|
|
|
|
int rhizome_manifest_version_cache_store(rhizome_manifest *m)
|
|
|
|
{
|
|
|
|
int bin=0;
|
|
|
|
int slot;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
char *id=rhizome_manifest_get(m,"id",NULL,0);
|
|
|
|
if (!id) return 1; // dodgy manifest, so don't suggest that we want to RX it.
|
|
|
|
|
|
|
|
/* Work out bin number in cache */
|
|
|
|
for(i=0;i<RHIZOME_VERSION_CACHE_NYBLS;i++)
|
|
|
|
{
|
2012-06-25 05:20:38 +00:00
|
|
|
int nybl=hexvalue(id[i]);
|
2012-01-27 06:41:18 +00:00
|
|
|
bin=(bin<<4)|nybl;
|
|
|
|
}
|
|
|
|
bin=bin>>RHIZOME_VERSION_CACHE_SHIFT;
|
|
|
|
|
|
|
|
slot=random()%RHIZOME_VERSION_CACHE_ASSOCIATIVITY;
|
2012-05-26 12:36:12 +00:00
|
|
|
rhizome_manifest_version_cache_slot *entry
|
|
|
|
=&rhizome_manifest_version_cache[bin][slot];
|
2012-01-27 06:41:18 +00:00
|
|
|
unsigned long long manifest_version = rhizome_manifest_get_ll(m,"version");
|
|
|
|
|
2012-05-26 12:36:12 +00:00
|
|
|
entry->version=manifest_version;
|
2012-01-27 06:41:18 +00:00
|
|
|
for(i=0;i<24;i++)
|
|
|
|
{
|
2012-06-25 05:20:38 +00:00
|
|
|
int byte=(hexvalue(id[(i*2)])<<4)|hexvalue(id[(i*2)+1]);
|
2012-05-26 12:36:12 +00:00
|
|
|
entry->idprefix[i]=byte;
|
2012-01-27 06:41:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rhizome_manifest_version_cache_lookup(rhizome_manifest *m)
|
|
|
|
{
|
|
|
|
int bin=0;
|
|
|
|
int slot;
|
|
|
|
int i;
|
|
|
|
|
2012-05-24 02:07:03 +00:00
|
|
|
char id[RHIZOME_MANIFEST_ID_STRLEN + 1];
|
|
|
|
if (!rhizome_manifest_get(m, "id", id, sizeof id))
|
2012-05-27 06:30:51 +00:00
|
|
|
// dodgy manifest, we don't want to receive it
|
|
|
|
return WHY("Ignoring bad manifest (no ID field)");
|
2012-05-24 02:07:03 +00:00
|
|
|
str_toupper_inplace(id);
|
2012-05-28 09:54:02 +00:00
|
|
|
m->version = rhizome_manifest_get_ll(m, "version");
|
|
|
|
|
|
|
|
// skip the cache for now
|
2012-06-08 03:43:26 +00:00
|
|
|
long long dbVersion = -1;
|
|
|
|
if (sqlite_exec_int64(&dbVersion, "SELECT version FROM MANIFESTS WHERE id='%s';", id) == -1)
|
|
|
|
return WHY("Select failure");
|
2012-06-25 05:16:55 +00:00
|
|
|
if (dbVersion >= m->version) {
|
|
|
|
if (0) WHYF("We already have %s (%lld vs %lld)", id, dbVersion, m->version);
|
|
|
|
return -1;
|
|
|
|
}
|
2012-05-28 09:54:02 +00:00
|
|
|
return 0;
|
2012-01-27 06:41:18 +00:00
|
|
|
|
|
|
|
/* Work out bin number in cache */
|
|
|
|
for(i=0;i<RHIZOME_VERSION_CACHE_NYBLS;i++)
|
|
|
|
{
|
2012-06-25 05:20:38 +00:00
|
|
|
int nybl=hexvalue(id[i]);
|
2012-01-27 06:41:18 +00:00
|
|
|
bin=(bin<<4)|nybl;
|
|
|
|
}
|
|
|
|
bin=bin>>RHIZOME_VERSION_CACHE_SHIFT;
|
|
|
|
|
|
|
|
for(slot=0;slot<RHIZOME_VERSION_CACHE_ASSOCIATIVITY;slot++)
|
|
|
|
{
|
2012-05-26 12:36:12 +00:00
|
|
|
rhizome_manifest_version_cache_slot *entry
|
|
|
|
=&rhizome_manifest_version_cache[bin][slot];
|
2012-01-27 06:41:18 +00:00
|
|
|
for(i=0;i<24;i++)
|
|
|
|
{
|
|
|
|
int byte=
|
2012-06-25 05:20:38 +00:00
|
|
|
(hexvalue(id[(i*2)])<<4)
|
|
|
|
|hexvalue(id[(i*2)+1]);
|
2012-05-26 12:36:12 +00:00
|
|
|
if (byte!=entry->idprefix[i]) break;
|
2012-01-27 06:41:18 +00:00
|
|
|
}
|
|
|
|
if (i==24) {
|
|
|
|
/* Entries match -- so check version */
|
2012-06-08 03:43:26 +00:00
|
|
|
long long rev = rhizome_manifest_get_ll(m,"version");
|
|
|
|
if (1) DEBUGF("cached version %lld vs manifest version %lld", entry->version,rev);
|
|
|
|
if (rev > entry->version) {
|
|
|
|
/* If we only have an old version, try refreshing the cache
|
2012-05-27 16:13:49 +00:00
|
|
|
by querying the database */
|
2012-06-08 03:43:26 +00:00
|
|
|
if (sqlite_exec_int64(&entry->version, "select version from manifests where id='%s'", id) != 1)
|
|
|
|
return WHY("failed to select stored manifest version");
|
|
|
|
DEBUGF("Refreshed stored version from database: entry->version=%lld", entry->version);
|
2012-05-27 16:13:49 +00:00
|
|
|
}
|
2012-06-08 03:43:26 +00:00
|
|
|
if (rev < entry->version) {
|
2012-01-27 06:41:18 +00:00
|
|
|
/* the presented manifest is older than we have.
|
|
|
|
This allows the caller to know that they can tell whoever gave them the
|
2012-05-26 12:36:12 +00:00
|
|
|
manifest it's time to get with the times. May or not ever be
|
2012-01-27 06:41:18 +00:00
|
|
|
implemented, but it would be nice. XXX */
|
2012-05-28 03:29:38 +00:00
|
|
|
WHYF("cached version is NEWER than presented version (%lld is newer than %lld)",
|
|
|
|
entry->version,rev);
|
2012-01-27 06:41:18 +00:00
|
|
|
return -2;
|
2012-05-26 12:36:12 +00:00
|
|
|
} else if (rev<=entry->version) {
|
2012-01-27 06:41:18 +00:00
|
|
|
/* the presented manifest is already stored. */
|
2012-06-08 03:43:26 +00:00
|
|
|
if (1) DEBUG("cached version is NEWER/SAME as presented version");
|
2012-01-27 06:41:18 +00:00
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
/* the presented manifest is newer than we have */
|
2012-06-08 03:43:26 +00:00
|
|
|
DEBUG("cached version is older than presented version");
|
2012-01-27 06:41:18 +00:00
|
|
|
return 0;
|
2012-06-08 03:43:26 +00:00
|
|
|
}
|
2012-01-27 06:41:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-06-08 03:43:26 +00:00
|
|
|
DEBUG("Not in manifest cache");
|
2012-05-27 06:30:51 +00:00
|
|
|
|
2012-01-27 06:41:18 +00:00
|
|
|
/* Not in cache, so all is well, well, maybe.
|
|
|
|
What we do know is that it is unlikely to be in the database, so it probably
|
|
|
|
doesn't hurt to try to receive it.
|
|
|
|
|
|
|
|
Of course, we can just ask the database if it is there already, and populate
|
|
|
|
the cache in the process if we find it. The tradeoff is that the whole point
|
|
|
|
of the cache is to AVOID database lookups, not incurr them whenever the cache
|
|
|
|
has a negative result. But if we don't ask the database, then we can waste
|
|
|
|
more effort fetching the file associated with the manifest, and will ultimately
|
|
|
|
incurr a database lookup (and more), so while it seems a little false economy
|
|
|
|
we need to do the lookup now.
|
|
|
|
|
|
|
|
What this all suggests is that we need fairly high associativity so that misses
|
|
|
|
are rare events. But high associativity then introduces a linear search cost,
|
|
|
|
although that is unlikely to be nearly as much cost as even thinking about a
|
|
|
|
database query.
|
|
|
|
|
|
|
|
It also says that on a busy network that things will eventually go pear-shaped
|
|
|
|
and require regular database queries, and that memory allowing, we should use
|
|
|
|
a fairly large cache here.
|
|
|
|
*/
|
2012-05-25 10:12:45 +00:00
|
|
|
long long manifest_version = rhizome_manifest_get_ll(m, "version");
|
2012-06-08 03:43:26 +00:00
|
|
|
long long count;
|
|
|
|
switch (sqlite_exec_int64(&count, "select count(*) from manifests where id='%s' and version>=%lld", id, manifest_version)) {
|
|
|
|
case -1:
|
|
|
|
return WHY("database error reading stored manifest version");
|
|
|
|
case 1:
|
|
|
|
if (count) {
|
|
|
|
/* Okay, we have a stored version which is newer, so update the cache
|
|
|
|
using a random replacement strategy. */
|
|
|
|
long long stored_version;
|
|
|
|
if (sqlite_exec_int64(&stored_version, "select version from manifests where id='%s'", id) < 1)
|
|
|
|
return WHY("database error reading stored manifest version"); // database is broken, we can't confirm that it is here
|
|
|
|
DEBUGF("stored version=%lld, manifest_version=%lld (not fetching; remembering in cache)",
|
|
|
|
stored_version,manifest_version);
|
|
|
|
slot=random()%RHIZOME_VERSION_CACHE_ASSOCIATIVITY;
|
|
|
|
rhizome_manifest_version_cache_slot *entry
|
|
|
|
=&rhizome_manifest_version_cache[bin][slot];
|
|
|
|
entry->version=stored_version;
|
|
|
|
for(i=0;i<24;i++)
|
|
|
|
{
|
2012-06-25 05:20:38 +00:00
|
|
|
int byte=(hexvalue(id[(i*2)])<<4)|hexvalue(id[(i*2)+1]);
|
2012-06-08 03:43:26 +00:00
|
|
|
entry->idprefix[i]=byte;
|
|
|
|
}
|
|
|
|
/* Finally, say that it isn't worth RXing this manifest */
|
|
|
|
return stored_version > manifest_version ? -2 : -1;
|
2012-01-27 06:41:18 +00:00
|
|
|
}
|
2012-06-08 03:43:26 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return WHY("bad select result");
|
2012-01-27 06:41:18 +00:00
|
|
|
}
|
2012-05-27 06:30:51 +00:00
|
|
|
/* At best we hold an older version of this manifest, and at worst we
|
|
|
|
don't hold any copy. */
|
2012-05-25 10:12:45 +00:00
|
|
|
return 0;
|
2012-01-27 06:41:18 +00:00
|
|
|
}
|
|
|
|
|
2012-05-22 04:45:35 +00:00
|
|
|
typedef struct ignored_manifest {
|
|
|
|
unsigned char bid[crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES];
|
|
|
|
struct sockaddr_in peer;
|
|
|
|
long long timeout;
|
|
|
|
} ignored_manifest;
|
|
|
|
|
|
|
|
#define IGNORED_BIN_SIZE 8
|
|
|
|
#define IGNORED_BIN_COUNT 64
|
|
|
|
#define IGNORED_BIN_BITS 6
|
|
|
|
typedef struct ignored_manifest_bin {
|
|
|
|
int bins_used;
|
|
|
|
ignored_manifest m[IGNORED_BIN_SIZE];
|
|
|
|
} ignored_manifest_bin;
|
|
|
|
|
|
|
|
typedef struct ignored_manifest_cache {
|
|
|
|
ignored_manifest_bin bins[IGNORED_BIN_COUNT];
|
|
|
|
} ignored_manifest_cache;
|
|
|
|
|
|
|
|
/* used uninitialised, since the probability of
|
|
|
|
a collision is exceedingly remote */
|
|
|
|
ignored_manifest_cache ignored;
|
|
|
|
|
|
|
|
int rhizome_ignore_manifest_check(rhizome_manifest *m,
|
|
|
|
struct sockaddr_in *peerip)
|
|
|
|
{
|
|
|
|
int bin = m->cryptoSignPublic[0]>>(8-IGNORED_BIN_BITS);
|
|
|
|
int slot;
|
2012-05-25 06:59:12 +00:00
|
|
|
for(slot = 0; slot != IGNORED_BIN_SIZE; ++slot)
|
2012-05-22 04:45:35 +00:00
|
|
|
{
|
|
|
|
if (!memcmp(ignored.bins[bin].m[slot].bid,
|
|
|
|
m->cryptoSignPublic,
|
|
|
|
crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES))
|
|
|
|
{
|
|
|
|
if (ignored.bins[bin].m[slot].timeout>overlay_gettime_ms())
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int rhizome_queue_ignore_manifest(rhizome_manifest *m,
|
|
|
|
struct sockaddr_in *peerip,int timeout)
|
|
|
|
{
|
|
|
|
/* The supplied manifest from a given IP has errors, so remember
|
|
|
|
that it isn't worth considering */
|
|
|
|
int bin = m->cryptoSignPublic[0]>>(8-IGNORED_BIN_BITS);
|
|
|
|
int slot;
|
2012-05-25 06:59:12 +00:00
|
|
|
for(slot = 0; slot != IGNORED_BIN_SIZE; ++slot)
|
2012-05-22 04:45:35 +00:00
|
|
|
{
|
|
|
|
if (!memcmp(ignored.bins[bin].m[slot].bid,
|
|
|
|
m->cryptoSignPublic,
|
|
|
|
crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (slot>=IGNORED_BIN_SIZE) slot=random()%IGNORED_BIN_SIZE;
|
|
|
|
bcopy(&m->cryptoSignPublic[0],
|
|
|
|
&ignored.bins[bin].m[slot].bid[0],
|
|
|
|
crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES);
|
|
|
|
/* ignore for a while */
|
|
|
|
ignored.bins[bin].m[slot].timeout=overlay_gettime_ms()+timeout;
|
|
|
|
bcopy(peerip,
|
|
|
|
&ignored.bins[bin].m[slot].peer,
|
|
|
|
sizeof(struct sockaddr_in));
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-05-22 05:32:28 +00:00
|
|
|
typedef struct rhizome_candidates {
|
|
|
|
rhizome_manifest *manifest;
|
2012-05-22 06:18:12 +00:00
|
|
|
struct sockaddr_in peer;
|
2012-05-22 05:32:28 +00:00
|
|
|
long long size;
|
|
|
|
/* XXX Need group memberships/priority level here */
|
|
|
|
int priority;
|
|
|
|
} rhizome_candidates;
|
|
|
|
|
|
|
|
rhizome_candidates candidates[MAX_CANDIDATES];
|
|
|
|
int candidate_count=0;
|
|
|
|
|
|
|
|
/* sort indicated candidate from starting position down
|
|
|
|
(or up) */
|
|
|
|
int rhizome_position_candidate(int position)
|
|
|
|
{
|
|
|
|
while(position<candidate_count&&position>=0) {
|
|
|
|
rhizome_candidates *c1=&candidates[position];
|
|
|
|
rhizome_candidates *c2=&candidates[position+1];
|
|
|
|
if (c1->priority>c2->priority
|
|
|
|
||(c1->priority==c2->priority
|
|
|
|
&&c1->size>c2->size))
|
|
|
|
{
|
|
|
|
rhizome_candidates c=*c1;
|
|
|
|
*c1=*c2;
|
|
|
|
*c2=c;
|
|
|
|
position++;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* doesn't need moving down, but does it need moving up? */
|
|
|
|
if (!position) return 0;
|
|
|
|
rhizome_candidates *c0=&candidates[position-1];
|
|
|
|
if (c1->priority<c0->priority
|
|
|
|
||(c1->priority==c0->priority
|
|
|
|
&&c1->size<c0->size))
|
|
|
|
{
|
|
|
|
rhizome_candidates c=*c1;
|
|
|
|
*c1=*c2;
|
|
|
|
*c2=c;
|
|
|
|
position--;
|
|
|
|
}
|
|
|
|
else return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-26 07:24:40 +00:00
|
|
|
/* Verifies manifests as late as possible to avoid wasting time. */
|
2012-05-22 05:32:28 +00:00
|
|
|
int rhizome_suggest_queue_manifest_import(rhizome_manifest *m,
|
|
|
|
struct sockaddr_in *peerip)
|
|
|
|
{
|
2012-06-26 07:24:40 +00:00
|
|
|
IN();
|
2012-05-22 05:32:28 +00:00
|
|
|
/* must free manifest when done with it */
|
2012-05-22 06:06:21 +00:00
|
|
|
char *id=rhizome_manifest_get(m,"id",NULL,0);
|
|
|
|
long long filesize=rhizome_manifest_get_ll(m,"filesize");
|
|
|
|
int priority=100; /* normal priority */
|
|
|
|
int i;
|
|
|
|
|
2012-05-26 12:36:12 +00:00
|
|
|
if (1) DEBUGF("Rhizome considering %s (size=%lld, priority=%d)",
|
2012-05-27 15:29:50 +00:00
|
|
|
id,filesize,priority);
|
2012-05-27 16:13:49 +00:00
|
|
|
m->version=rhizome_manifest_get_ll(m,"version");
|
2012-05-22 05:32:28 +00:00
|
|
|
|
|
|
|
if (rhizome_manifest_version_cache_lookup(m)) {
|
|
|
|
/* We already have this version or newer */
|
2012-05-22 06:06:21 +00:00
|
|
|
if (1||debug&DEBUG_RHIZOMESYNC) {
|
2012-05-24 01:58:32 +00:00
|
|
|
DEBUGF("manifest id=%s, version=%lld",
|
2012-05-22 05:32:28 +00:00
|
|
|
rhizome_manifest_get(m,"id",NULL,0),
|
|
|
|
rhizome_manifest_get_ll(m,"version"));
|
2012-05-24 01:58:32 +00:00
|
|
|
DEBUG("We already have that manifest or newer.");
|
2012-05-22 05:32:28 +00:00
|
|
|
}
|
|
|
|
rhizome_manifest_free(m);
|
2012-06-26 07:24:40 +00:00
|
|
|
RETURN(-1);
|
2012-05-22 05:32:28 +00:00
|
|
|
} else {
|
2012-05-26 12:36:12 +00:00
|
|
|
if (1||debug&DEBUG_RHIZOMESYNC) {
|
2012-06-08 03:43:26 +00:00
|
|
|
long long stored_version;
|
|
|
|
if (sqlite_exec_int64(&stored_version, "select version from manifests where id='%s'",id) > 0)
|
|
|
|
DEBUGF("manifest id=%s, version=%lld is new to us (we only have version %lld).",
|
|
|
|
id,
|
|
|
|
m->version,
|
|
|
|
stored_version);
|
2012-05-22 05:32:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* work out where to put it in the list */
|
|
|
|
for(i=0;i<candidate_count;i++)
|
|
|
|
{
|
|
|
|
/* If this manifest is already in the list, stop.
|
|
|
|
(also replace older manifest versions with newer ones,
|
|
|
|
which can upset the ordering.) */
|
2012-05-22 06:06:21 +00:00
|
|
|
if (candidates[i].manifest==NULL) continue;
|
2012-05-25 07:31:56 +00:00
|
|
|
if (!strcasecmp(id,rhizome_manifest_get(candidates[i].manifest,"id",NULL,0)))
|
2012-05-22 06:06:21 +00:00
|
|
|
{
|
|
|
|
/* duplicate.
|
|
|
|
XXX - Check versions! We should replace older with newer,
|
|
|
|
and then update position in queue based on size */
|
2012-05-25 07:31:56 +00:00
|
|
|
long long list_version=rhizome_manifest_get_ll(candidates[i].manifest, "version");
|
2012-05-22 05:32:28 +00:00
|
|
|
long long this_version=rhizome_manifest_get_ll(m,"version");
|
|
|
|
if (list_version>=this_version) {
|
|
|
|
/* this version is older than the one in the list,
|
|
|
|
so don't list this one */
|
|
|
|
rhizome_manifest_free(m);
|
2012-06-26 07:24:40 +00:00
|
|
|
RETURN(0);
|
2012-05-22 05:32:28 +00:00
|
|
|
} else {
|
|
|
|
/* replace listed version with this newer version */
|
2012-06-26 07:24:40 +00:00
|
|
|
if (rhizome_manifest_verify(m)) {
|
|
|
|
WHY("Error verifying manifest when considering queuing for import");
|
2012-06-26 11:51:46 +00:00
|
|
|
/* Don't waste time looking at this manifest again for a while */
|
|
|
|
rhizome_queue_ignore_manifest(m,peerip,60000);
|
2012-06-26 07:24:40 +00:00
|
|
|
rhizome_manifest_free(m);
|
|
|
|
RETURN(-1);
|
|
|
|
}
|
|
|
|
|
2012-05-22 05:32:28 +00:00
|
|
|
rhizome_manifest_free(candidates[i].manifest);
|
|
|
|
candidates[i].manifest=m;
|
|
|
|
/* update position in list */
|
|
|
|
rhizome_position_candidate(i);
|
2012-06-26 07:24:40 +00:00
|
|
|
RETURN(0);
|
2012-05-22 05:32:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if we have a higher priority file than the one at this
|
|
|
|
point in the list, stop, and we will shuffle the rest of
|
|
|
|
the list down. */
|
|
|
|
if (candidates[i].priority>priority
|
|
|
|
||(candidates[i].priority==priority
|
|
|
|
&&candidates[i].size>filesize))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i>=MAX_CANDIDATES) {
|
|
|
|
/* our list is already full of higher-priority items */
|
|
|
|
rhizome_manifest_free(m);
|
2012-06-26 07:24:40 +00:00
|
|
|
RETURN(-1);
|
2012-05-22 05:32:28 +00:00
|
|
|
}
|
2012-06-26 07:24:40 +00:00
|
|
|
|
|
|
|
if (rhizome_manifest_verify(m)) {
|
|
|
|
WHY("Error verifying manifest when considering queuing for import");
|
2012-06-26 11:51:46 +00:00
|
|
|
/* Don't waste time looking at this manifest again for a while */
|
|
|
|
rhizome_queue_ignore_manifest(m,peerip,60000);
|
2012-06-26 07:24:40 +00:00
|
|
|
rhizome_manifest_free(m);
|
|
|
|
RETURN(-1);
|
2012-05-22 05:32:28 +00:00
|
|
|
}
|
2012-06-26 07:24:40 +00:00
|
|
|
|
2012-05-22 05:32:28 +00:00
|
|
|
if (candidate_count==MAX_CANDIDATES) {
|
|
|
|
/* release manifest structure for whoever we are bumping from the list */
|
|
|
|
rhizome_manifest_free(candidates[MAX_CANDIDATES-1].manifest);
|
|
|
|
candidates[MAX_CANDIDATES-1].manifest=NULL;
|
2012-05-22 06:06:21 +00:00
|
|
|
} else candidate_count++;
|
2012-05-22 05:32:28 +00:00
|
|
|
/* shuffle down */
|
2012-05-22 06:06:21 +00:00
|
|
|
int bytes=(candidate_count-(i+1))*sizeof(rhizome_candidates);
|
2012-05-23 09:31:07 +00:00
|
|
|
if (0) DEBUGF("Moving slot %d to slot %d (%d bytes = %d slots)",
|
2012-05-22 07:34:24 +00:00
|
|
|
i,i+1,bytes,bytes/sizeof(rhizome_candidates));
|
2012-05-22 05:32:28 +00:00
|
|
|
bcopy(&candidates[i],
|
|
|
|
&candidates[i+1],
|
2012-05-22 06:06:21 +00:00
|
|
|
bytes);
|
2012-05-22 05:32:28 +00:00
|
|
|
/* put new candidate in */
|
|
|
|
candidates[i].manifest=m;
|
|
|
|
candidates[i].size=filesize;
|
|
|
|
candidates[i].priority=priority;
|
2012-05-22 06:18:12 +00:00
|
|
|
candidates[i].peer=*peerip;
|
2012-05-22 05:32:28 +00:00
|
|
|
|
|
|
|
int j;
|
2012-05-22 07:34:24 +00:00
|
|
|
if (0) {
|
2012-05-23 09:31:07 +00:00
|
|
|
DEBUG("Rhizome priorities fetch list now:");
|
2012-05-22 07:34:24 +00:00
|
|
|
for(j=0;j<candidate_count;j++)
|
2012-05-23 09:31:07 +00:00
|
|
|
DEBUGF("%02d:%s:size=%lld, priority=%d",
|
2012-05-22 07:34:24 +00:00
|
|
|
j,
|
|
|
|
rhizome_manifest_get(candidates[j].manifest,"id",NULL,0),
|
|
|
|
candidates[j].size,candidates[j].priority);
|
|
|
|
}
|
2012-05-22 05:32:28 +00:00
|
|
|
|
2012-06-26 07:24:40 +00:00
|
|
|
RETURN(0);
|
2012-05-22 05:32:28 +00:00
|
|
|
}
|
|
|
|
|
2012-07-02 03:49:54 +00:00
|
|
|
void rhizome_enqueue_suggestions(struct sched_ent *alarm)
|
2012-05-22 06:18:12 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i=0;i<candidate_count;i++)
|
|
|
|
{
|
|
|
|
if (rhizome_file_fetch_queue_count>=MAX_QUEUED_FILES)
|
|
|
|
break;
|
2012-05-25 07:31:56 +00:00
|
|
|
int manifest_kept = 0;
|
|
|
|
rhizome_queue_manifest_import(candidates[i].manifest,&candidates[i].peer, &manifest_kept);
|
|
|
|
if (!manifest_kept) {
|
|
|
|
rhizome_manifest_free(candidates[i].manifest);
|
|
|
|
candidates[i].manifest = NULL;
|
|
|
|
}
|
2012-05-22 06:18:12 +00:00
|
|
|
}
|
|
|
|
if (i) {
|
|
|
|
/* now shuffle up */
|
|
|
|
int bytes=(candidate_count-i)*sizeof(rhizome_candidates);
|
2012-05-23 09:31:07 +00:00
|
|
|
if (0) DEBUGF("Moving slot %d to slot 0 (%d bytes = %d slots)",
|
2012-05-22 07:38:12 +00:00
|
|
|
i,bytes,bytes/sizeof(rhizome_candidates));
|
2012-05-22 06:18:12 +00:00
|
|
|
bcopy(&candidates[i],&candidates[0],bytes);
|
|
|
|
candidate_count-=i;
|
|
|
|
}
|
2012-07-02 03:49:54 +00:00
|
|
|
alarm->alarm = overlay_gettime_ms() + 3000;
|
|
|
|
schedule(alarm);
|
2012-06-22 03:55:41 +00:00
|
|
|
return;
|
2012-05-22 06:18:12 +00:00
|
|
|
}
|
|
|
|
|
2012-05-25 07:31:56 +00:00
|
|
|
int rhizome_queue_manifest_import(rhizome_manifest *m, struct sockaddr_in *peerip, int *manifest_kept)
|
2012-01-12 03:38:24 +00:00
|
|
|
{
|
2012-05-25 07:31:56 +00:00
|
|
|
*manifest_kept = 0;
|
2012-01-27 05:51:48 +00:00
|
|
|
int i;
|
|
|
|
|
2012-01-27 06:41:18 +00:00
|
|
|
/* Do the quick rejection tests first, before the more expensive once,
|
|
|
|
like querying the database for manifests.
|
|
|
|
|
|
|
|
We probably need a cache of recently rejected manifestid:versionid
|
|
|
|
pairs so that we can avoid database lookups in most cases. Probably
|
|
|
|
the first 64bits of manifestid is sufficient to make it resistant to
|
|
|
|
collission attacks, but using 128bits or the full 256 bits would be safer.
|
|
|
|
Let's make the cache use 256 bit (32byte) entries for power of two
|
|
|
|
efficiency, and so use the last 64bits for version id, thus using 192 bits
|
|
|
|
for collission avoidance --- probably sufficient for many years yet (from
|
|
|
|
time of writing in 2012). We get a little more than 192 bits by using
|
|
|
|
the cache slot number to implicitly store the first bits.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (rhizome_manifest_version_cache_lookup(m)) {
|
|
|
|
/* We already have this version or newer */
|
2012-05-27 15:29:50 +00:00
|
|
|
if (1||debug&DEBUG_RHIZOMESYNC) {
|
2012-05-24 01:58:32 +00:00
|
|
|
DEBUGF("manifest id=%s, version=%lld",
|
2012-05-22 00:31:17 +00:00
|
|
|
rhizome_manifest_get(m,"id",NULL,0),
|
|
|
|
rhizome_manifest_get_ll(m,"version"));
|
2012-05-24 01:58:32 +00:00
|
|
|
DEBUG("We already have that manifest or newer.");
|
2012-01-27 06:41:18 +00:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
} else {
|
2012-05-27 15:29:50 +00:00
|
|
|
if (1||debug&DEBUG_RHIZOMESYNC) {
|
2012-05-24 01:58:32 +00:00
|
|
|
DEBUGF("manifest id=%s, version=%lld is new to us.",
|
2012-05-22 00:31:17 +00:00
|
|
|
rhizome_manifest_get(m,"id",NULL,0),
|
|
|
|
rhizome_manifest_get_ll(m,"version"));
|
2012-01-27 06:48:14 +00:00
|
|
|
}
|
2012-01-27 06:41:18 +00:00
|
|
|
}
|
2012-01-27 06:48:14 +00:00
|
|
|
|
2012-01-27 05:51:48 +00:00
|
|
|
/* Don't queue if queue slots already full */
|
2012-01-12 06:17:24 +00:00
|
|
|
if (rhizome_file_fetch_queue_count>=MAX_QUEUED_FILES) {
|
2012-05-27 15:29:50 +00:00
|
|
|
if (1||debug&DEBUG_RHIZOME) DEBUG("Already busy fetching files");
|
2012-01-12 06:17:24 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2012-01-27 05:51:48 +00:00
|
|
|
/* Don't queue if already queued */
|
|
|
|
char *id=rhizome_manifest_get(m,"id",NULL,0);
|
2012-05-24 01:58:32 +00:00
|
|
|
if (!rhizome_str_is_manifest_id(id))
|
|
|
|
return WHYF("Invalid manifest ID: %s", id);
|
2012-01-27 05:51:48 +00:00
|
|
|
for(i=0;i<rhizome_file_fetch_queue_count;i++) {
|
|
|
|
rhizome_file_fetch_record
|
|
|
|
*q=&file_fetch_queue[i];
|
|
|
|
if (!strcasecmp(id,rhizome_manifest_get(q->manifest,"id",NULL,0))) {
|
2012-05-27 15:29:50 +00:00
|
|
|
if (1||debug&DEBUG_RHIZOMESYNC)
|
2012-05-24 01:58:32 +00:00
|
|
|
DEBUGF("Already have %s in the queue.",id);
|
2012-01-27 05:51:48 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2012-01-12 06:17:24 +00:00
|
|
|
|
2012-05-24 01:58:32 +00:00
|
|
|
if (!rhizome_manifest_get(m, "filehash", m->fileHexHash, sizeof m->fileHexHash))
|
|
|
|
return WHY("Manifest missing filehash");
|
|
|
|
if (!rhizome_str_is_file_hash(m->fileHexHash))
|
|
|
|
return WHYF("Invalid file hash: %s", m->fileHexHash);
|
|
|
|
str_toupper_inplace(m->fileHexHash);
|
|
|
|
m->fileHashedP = 1;
|
|
|
|
|
|
|
|
long long filesize = rhizome_manifest_get_ll(m, "filesize");
|
2012-01-12 06:17:24 +00:00
|
|
|
|
2012-05-27 15:29:50 +00:00
|
|
|
if (1||debug&DEBUG_RHIZOMESYNC)
|
2012-05-24 01:58:32 +00:00
|
|
|
DEBUGF("Getting ready to fetch file %s for manifest %s", m->fileHexHash, id);
|
2012-01-13 06:51:06 +00:00
|
|
|
|
2012-05-24 01:58:32 +00:00
|
|
|
if (filesize > 0 && m->fileHexHash[0])
|
|
|
|
{
|
2012-06-08 03:43:26 +00:00
|
|
|
long long gotfile = 0;
|
|
|
|
if (sqlite_exec_int64(&gotfile, "SELECT COUNT(*) FROM FILES WHERE ID='%s' and datavalid=1;", m->fileHexHash) != 1)
|
|
|
|
return WHY("select failed");
|
2012-01-12 06:17:24 +00:00
|
|
|
if (gotfile!=1) {
|
|
|
|
/* We need to get the file */
|
|
|
|
/* Discard request if the same manifest is already queued for reception.
|
|
|
|
*/
|
|
|
|
int i,j;
|
|
|
|
for(i=0;i<rhizome_file_fetch_queue_count;i++)
|
|
|
|
{
|
|
|
|
for(j=0;j<crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES;j++)
|
|
|
|
if (m->cryptoSignPublic[j]
|
|
|
|
!=file_fetch_queue[i].manifest->cryptoSignPublic[j]) break;
|
|
|
|
if (j==crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES)
|
|
|
|
{
|
|
|
|
/* We are already fetching this manifest */
|
2012-05-27 15:29:50 +00:00
|
|
|
if (1||debug&DEBUG_RHIZOME) DEBUGF("Already fetching manifest");
|
2012-01-12 06:17:24 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2012-05-23 06:34:00 +00:00
|
|
|
for(j=0;j<=RHIZOME_FILEHASH_STRLEN;j++)
|
2012-05-24 01:58:32 +00:00
|
|
|
if (m->fileHexHash[j]!=file_fetch_queue[i].fileid[j]) break;
|
2012-05-23 06:34:00 +00:00
|
|
|
if (j==RHIZOME_FILEHASH_STRLEN + 1)
|
2012-01-12 06:17:24 +00:00
|
|
|
{
|
|
|
|
/* We are already fetching this file */
|
2012-05-27 15:29:50 +00:00
|
|
|
if (1||debug&DEBUG_RHIZOME) DEBUGF("Already fetching file %s", m->fileHexHash);
|
2012-01-12 06:17:24 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (peerip)
|
|
|
|
{
|
|
|
|
/* Transfer via HTTP over IPv4 */
|
2012-07-10 07:03:39 +00:00
|
|
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
if (sock == -1)
|
|
|
|
return WHY_perror("socket");
|
|
|
|
if (set_nonblock(sock) == -1) {
|
|
|
|
close(sock);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
INFOF("HTTP CONNECT family=%u port=%u addr=%u.%u.%u.%u",
|
|
|
|
peerip->sin_family, peerip->sin_port,
|
|
|
|
((unsigned char*)&peerip->sin_addr.s_addr)[0],
|
|
|
|
((unsigned char*)&peerip->sin_addr.s_addr)[1],
|
|
|
|
((unsigned char*)&peerip->sin_addr.s_addr)[2],
|
|
|
|
((unsigned char*)&peerip->sin_addr.s_addr)[3]
|
|
|
|
);
|
|
|
|
if (connect(sock, (struct sockaddr*)peerip, sizeof *peerip) == -1 && errno != EINPROGRESS) {
|
2012-05-14 06:02:28 +00:00
|
|
|
WHY_perror("connect");
|
2012-07-10 07:03:39 +00:00
|
|
|
WHY("Failed to open socket to peer's rhizome web server");
|
|
|
|
close(sock);
|
2012-01-12 06:17:24 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2012-07-10 07:03:39 +00:00
|
|
|
rhizome_file_fetch_record *q=&file_fetch_queue[rhizome_file_fetch_queue_count];
|
2012-05-25 07:31:56 +00:00
|
|
|
q->manifest = m;
|
|
|
|
*manifest_kept = 1;
|
2012-07-02 03:49:54 +00:00
|
|
|
q->alarm.poll.fd=sock;
|
2012-05-24 01:58:32 +00:00
|
|
|
strncpy(q->fileid, m->fileHexHash, RHIZOME_FILEHASH_STRLEN + 1);
|
2012-01-13 06:51:06 +00:00
|
|
|
snprintf(q->request,1024,"GET /rhizome/file/%s HTTP/1.0\r\n\r\n",
|
|
|
|
q->fileid);
|
|
|
|
q->request_len=strlen(q->request);
|
|
|
|
q->request_ofs=0;
|
2012-06-27 07:24:42 +00:00
|
|
|
q->state=RHIZOME_FETCH_CONNECTING;
|
2012-01-13 06:51:06 +00:00
|
|
|
q->file_len=-1;
|
|
|
|
q->file_ofs=0;
|
2012-07-02 03:49:54 +00:00
|
|
|
|
2012-01-13 06:51:06 +00:00
|
|
|
/* XXX Don't forget to implement resume */
|
|
|
|
/* XXX We should stream file straight into the database */
|
2012-05-14 06:02:28 +00:00
|
|
|
const char *id = rhizome_manifest_get(q->manifest, "id", NULL, 0);
|
|
|
|
if (id == NULL) {
|
|
|
|
close(sock);
|
|
|
|
return WHY("Manifest missing ID");
|
|
|
|
}
|
2012-05-24 01:58:32 +00:00
|
|
|
if (create_rhizome_import_dir() == -1)
|
2012-05-23 08:41:34 +00:00
|
|
|
return -1;
|
2012-05-24 01:58:32 +00:00
|
|
|
char filename[1024];
|
|
|
|
if (!FORM_RHIZOME_IMPORT_PATH(filename, "file.%s", id)) {
|
2012-05-14 06:02:28 +00:00
|
|
|
close(sock);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
q->manifest->dataFileName = strdup(filename);
|
2012-05-24 09:16:08 +00:00
|
|
|
q->file=fopen(q->manifest->dataFileName,"w");
|
2012-01-13 06:51:06 +00:00
|
|
|
if (!q->file) {
|
2012-05-14 06:02:28 +00:00
|
|
|
WHY_perror("fopen");
|
2012-05-27 15:29:50 +00:00
|
|
|
if (1||debug&DEBUG_RHIZOME)
|
2012-05-24 09:16:08 +00:00
|
|
|
DEBUGF("Could not open '%s' to write received file.", q->manifest->dataFileName);
|
2012-05-14 06:02:28 +00:00
|
|
|
close(sock);
|
|
|
|
return -1;
|
2012-01-13 06:51:06 +00:00
|
|
|
}
|
2012-07-02 05:50:30 +00:00
|
|
|
|
2012-06-22 03:55:41 +00:00
|
|
|
/* Watch for activity on the socket */
|
2012-07-02 03:49:54 +00:00
|
|
|
q->alarm.function=rhizome_fetch_poll;
|
2012-07-02 05:50:30 +00:00
|
|
|
fetch_stats.name="rhizome_fetch_poll";
|
|
|
|
q->alarm.stats=&fetch_stats;
|
2012-07-02 03:49:54 +00:00
|
|
|
q->alarm.poll.events=POLLIN|POLLOUT;
|
|
|
|
watch(&q->alarm);
|
|
|
|
/* And schedule a timeout alarm */
|
|
|
|
q->alarm.alarm=overlay_gettime_ms() + RHIZOME_IDLE_TIMEOUT;
|
|
|
|
schedule(&q->alarm);
|
2012-06-27 07:24:42 +00:00
|
|
|
|
2012-05-14 06:02:28 +00:00
|
|
|
rhizome_file_fetch_queue_count++;
|
2012-05-27 15:29:50 +00:00
|
|
|
if (1||debug&DEBUG_RHIZOME) DEBUGF("Queued file for fetching into %s (%d in queue)",
|
2012-05-24 09:16:08 +00:00
|
|
|
q->manifest->dataFileName, rhizome_file_fetch_queue_count);
|
2012-01-12 06:17:24 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Transfer via overlay */
|
|
|
|
return WHY("Rhizome fetching via overlay not implemented");
|
|
|
|
}
|
|
|
|
}
|
2012-01-27 05:51:48 +00:00
|
|
|
else
|
|
|
|
{
|
2012-05-27 15:29:50 +00:00
|
|
|
if (1||debug&DEBUG_RHIZOMESYNC)
|
2012-05-24 01:58:32 +00:00
|
|
|
DEBUGF("We already have the file for this manifest; importing from manifest alone.");
|
|
|
|
if (create_rhizome_import_dir() == -1)
|
|
|
|
return -1;
|
2012-01-27 05:51:48 +00:00
|
|
|
char filename[1024];
|
2012-05-24 01:58:32 +00:00
|
|
|
if (!FORM_RHIZOME_IMPORT_PATH(filename, "manifest.%s", id))
|
2012-05-14 06:02:28 +00:00
|
|
|
return -1;
|
|
|
|
if (!rhizome_write_manifest_file(m, filename)) {
|
2012-05-25 15:20:48 +00:00
|
|
|
rhizome_bundle_import(m, NULL, id, m->ttl-1);
|
2012-01-27 05:51:48 +00:00
|
|
|
}
|
|
|
|
}
|
2012-01-12 06:17:24 +00:00
|
|
|
}
|
2012-05-24 01:58:32 +00:00
|
|
|
|
2012-01-27 05:08:06 +00:00
|
|
|
return 0;
|
2012-01-12 03:38:24 +00:00
|
|
|
}
|
2012-05-24 01:58:32 +00:00
|
|
|
|
2012-07-02 03:49:54 +00:00
|
|
|
int rhizome_fetch_close(rhizome_file_fetch_record *q){
|
2012-06-27 07:24:42 +00:00
|
|
|
/* Free ephemeral data */
|
2012-07-02 03:49:54 +00:00
|
|
|
if (q->file) fclose(q->file);
|
|
|
|
q->file=NULL;
|
|
|
|
if (q->manifest)
|
|
|
|
rhizome_manifest_free(q->manifest);
|
|
|
|
q->manifest=NULL;
|
2012-06-27 07:24:42 +00:00
|
|
|
|
|
|
|
/* close socket and stop watching it */
|
2012-07-02 03:49:54 +00:00
|
|
|
unwatch(&q->alarm);
|
|
|
|
unschedule(&q->alarm);
|
|
|
|
close(q->alarm.poll.fd);
|
|
|
|
q->alarm.poll.fd=-1;
|
2012-06-27 07:24:42 +00:00
|
|
|
|
|
|
|
/* Reduce count of open connections */
|
|
|
|
rhizome_file_fetch_queue_count--;
|
|
|
|
|
|
|
|
if (debug&DEBUG_RHIZOME)
|
|
|
|
DEBUGF("Released rhizome fetch slot (%d used)",
|
|
|
|
rhizome_file_fetch_queue_count);
|
2012-07-02 03:49:54 +00:00
|
|
|
return 0;
|
2012-01-13 06:51:06 +00:00
|
|
|
}
|
|
|
|
|
2012-07-02 03:49:54 +00:00
|
|
|
void rhizome_fetch_write(rhizome_file_fetch_record *q){
|
2012-07-10 10:29:46 +00:00
|
|
|
int bytes = write_nonblock(q->alarm.poll.fd, &q->request[q->request_ofs], q->request_len-q->request_ofs);
|
|
|
|
if (bytes == -1) {
|
|
|
|
WHY("Got error while sending HTTP request. Closing.");
|
|
|
|
rhizome_fetch_close(q);
|
|
|
|
} else {
|
2012-07-02 03:49:54 +00:00
|
|
|
// reset timeout
|
|
|
|
unschedule(&q->alarm);
|
|
|
|
q->alarm.alarm=overlay_gettime_ms() + RHIZOME_IDLE_TIMEOUT;
|
|
|
|
schedule(&q->alarm);
|
2012-06-27 07:24:42 +00:00
|
|
|
q->request_ofs+=bytes;
|
|
|
|
if (q->request_ofs>=q->request_len) {
|
|
|
|
/* Sent all of request. Switch to listening for HTTP response headers.
|
|
|
|
*/
|
|
|
|
q->request_len=0; q->request_ofs=0;
|
|
|
|
q->state=RHIZOME_FETCH_RXHTTPHEADERS;
|
2012-07-02 03:49:54 +00:00
|
|
|
q->alarm.poll.events=POLLIN;
|
|
|
|
watch(&q->alarm);
|
2012-06-27 07:24:42 +00:00
|
|
|
}else if(q->state==RHIZOME_FETCH_CONNECTING)
|
|
|
|
q->state = RHIZOME_FETCH_SENDINGHTTPREQUEST;
|
|
|
|
}
|
|
|
|
}
|
2012-05-22 06:18:12 +00:00
|
|
|
|
2012-07-02 03:49:54 +00:00
|
|
|
void rhizome_fetch_poll(struct sched_ent *alarm)
|
2012-01-13 06:51:06 +00:00
|
|
|
{
|
2012-07-02 03:49:54 +00:00
|
|
|
rhizome_file_fetch_record *q=(rhizome_file_fetch_record *)alarm;
|
|
|
|
|
|
|
|
if (alarm->poll.revents==0){
|
|
|
|
// timeout, close the socket
|
|
|
|
rhizome_fetch_close(q);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-06-27 07:24:42 +00:00
|
|
|
switch(q->state)
|
2012-01-13 06:51:06 +00:00
|
|
|
{
|
2012-06-27 07:24:42 +00:00
|
|
|
case RHIZOME_FETCH_CONNECTING:
|
|
|
|
case RHIZOME_FETCH_SENDINGHTTPREQUEST:
|
2012-07-02 03:49:54 +00:00
|
|
|
rhizome_fetch_write(q);
|
2012-06-27 07:24:42 +00:00
|
|
|
break;
|
|
|
|
case RHIZOME_FETCH_RXFILE:
|
|
|
|
/* Keep reading until we have the promised amount of data */
|
|
|
|
|
|
|
|
sigPipeFlag=0;
|
|
|
|
|
|
|
|
errno=0;
|
|
|
|
char buffer[8192];
|
2012-01-13 06:51:06 +00:00
|
|
|
|
2012-07-02 03:49:54 +00:00
|
|
|
int bytes=read(q->alarm.poll.fd,buffer,8192);
|
2012-06-27 07:24:42 +00:00
|
|
|
|
|
|
|
/* If we got some data, see if we have found the end of the HTTP request */
|
|
|
|
if (bytes>0) {
|
2012-07-02 03:49:54 +00:00
|
|
|
|
|
|
|
// reset timeout
|
|
|
|
unschedule(&q->alarm);
|
|
|
|
q->alarm.alarm=overlay_gettime_ms() + RHIZOME_IDLE_TIMEOUT;
|
|
|
|
schedule(&q->alarm);
|
2012-01-13 06:51:06 +00:00
|
|
|
|
2012-06-27 07:24:42 +00:00
|
|
|
if (bytes>(q->file_len-q->file_ofs))
|
|
|
|
bytes=q->file_len-q->file_ofs;
|
|
|
|
if (fwrite(buffer,bytes,1,q->file)!=1)
|
|
|
|
{
|
|
|
|
if (debug&DEBUG_RHIZOME) DEBUGF("Failed writing %d bytes to file. @ offset %d",bytes,q->file_ofs);
|
2012-07-02 03:49:54 +00:00
|
|
|
rhizome_fetch_close(q);
|
2012-06-27 07:24:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
q->file_ofs+=bytes;
|
|
|
|
|
|
|
|
} else if (bytes==0) {
|
|
|
|
WHY("Got zero bytes, assume socket dead.");
|
2012-07-02 03:49:54 +00:00
|
|
|
rhizome_fetch_close(q);
|
2012-06-27 07:24:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (q->file_ofs>=q->file_len)
|
2012-01-13 06:51:06 +00:00
|
|
|
{
|
2012-06-27 07:24:42 +00:00
|
|
|
/* got all of file */
|
|
|
|
if (debug&DEBUG_RHIZOME) DEBUGF("Received all of file via rhizome -- now to import it");
|
|
|
|
{
|
|
|
|
fclose(q->file); q->file=NULL;
|
|
|
|
const char *id = rhizome_manifest_get(q->manifest, "id", NULL, 0);
|
|
|
|
if (id == NULL)
|
|
|
|
{ WHY("Manifest missing ID"); return; }
|
|
|
|
if (create_rhizome_import_dir() == -1)
|
|
|
|
return;
|
|
|
|
char filename[1024];
|
|
|
|
if (!FORM_RHIZOME_IMPORT_PATH(filename,"manifest.%s", id))
|
|
|
|
return;
|
|
|
|
/* Do really write the manifest unchanged */
|
|
|
|
if (debug&DEBUG_RHIZOME) {
|
|
|
|
DEBUGF("manifest has %d signatories",q->manifest->sig_count);
|
|
|
|
DEBUGF("manifest id = %s, len=%d",
|
|
|
|
rhizome_manifest_get(q->manifest,"id",NULL,0),
|
|
|
|
q->manifest->manifest_bytes);
|
|
|
|
dump("manifest",&q->manifest->manifestdata[0],
|
|
|
|
q->manifest->manifest_all_bytes);
|
2012-01-13 06:51:06 +00:00
|
|
|
}
|
2012-06-27 07:24:42 +00:00
|
|
|
q->manifest->finalised=1;
|
|
|
|
q->manifest->manifest_bytes=q->manifest->manifest_all_bytes;
|
|
|
|
if (rhizome_write_manifest_file(q->manifest,filename) != -1) {
|
|
|
|
rhizome_bundle_import(q->manifest, NULL, id,
|
|
|
|
q->manifest->ttl - 1 /* TTL */);
|
2012-01-13 06:51:06 +00:00
|
|
|
}
|
2012-06-27 07:24:42 +00:00
|
|
|
rhizome_manifest_free(q->manifest);
|
|
|
|
q->manifest=NULL;
|
2012-01-13 06:51:06 +00:00
|
|
|
}
|
2012-07-02 03:49:54 +00:00
|
|
|
rhizome_fetch_close(q);
|
2012-06-27 07:24:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case RHIZOME_FETCH_RXHTTPHEADERS:
|
|
|
|
/* Keep reading until we have two CR/LFs in a row */
|
|
|
|
sigPipeFlag=0;
|
|
|
|
|
|
|
|
errno=0;
|
2012-07-02 03:49:54 +00:00
|
|
|
bytes=read(q->alarm.poll.fd,&q->request[q->request_len],
|
2012-06-27 07:24:42 +00:00
|
|
|
1024-q->request_len-1);
|
|
|
|
|
|
|
|
/* If we got some data, see if we have found the end of the HTTP request */
|
|
|
|
if (bytes>0) {
|
|
|
|
int lfcount=0;
|
|
|
|
int i=q->request_len-160;
|
2012-07-02 03:49:54 +00:00
|
|
|
|
|
|
|
// reset timeout
|
|
|
|
unschedule(&q->alarm);
|
|
|
|
q->alarm.alarm=overlay_gettime_ms() + RHIZOME_IDLE_TIMEOUT;
|
|
|
|
schedule(&q->alarm);
|
|
|
|
|
2012-06-27 07:24:42 +00:00
|
|
|
if (i<0) i=0;
|
|
|
|
q->request_len+=bytes;
|
|
|
|
if (q->request_len<1024)
|
|
|
|
q->request[q->request_len]=0;
|
|
|
|
|
|
|
|
for(;i<(q->request_len+bytes);i++)
|
|
|
|
{
|
|
|
|
switch(q->request[i]) {
|
|
|
|
case '\n': lfcount++; break;
|
|
|
|
case '\r': /* ignore CR */ break;
|
|
|
|
case 0: /* ignore NUL (telnet inserts them) */ break;
|
|
|
|
default: lfcount=0; break;
|
|
|
|
}
|
|
|
|
if (lfcount==2) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debug&DEBUG_RHIZOME)
|
|
|
|
dump("http reply headers",(unsigned char *)q->request,lfcount==2?i:q->request_len);
|
|
|
|
|
|
|
|
if (lfcount==2) {
|
|
|
|
/* We have the response headers, so parse.
|
|
|
|
(we may also have some bytes of content, so we need to be a little
|
|
|
|
careful) */
|
|
|
|
|
|
|
|
/* Terminate string at end of headers */
|
|
|
|
q->request[i]=0;
|
|
|
|
|
|
|
|
/* Get HTTP result code */
|
|
|
|
char *s=strstr(q->request,"HTTP/1.0 ");
|
|
|
|
if (!s) {
|
|
|
|
if (debug&DEBUG_RHIZOME) DEBUGF("HTTP response lacked HTTP/1.0 response code.");
|
2012-07-02 03:49:54 +00:00
|
|
|
rhizome_fetch_close(q);
|
2012-06-27 07:24:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
int http_response_code=strtoll(&s[9],NULL,10);
|
|
|
|
if (http_response_code!=200) {
|
|
|
|
if (debug&DEBUG_RHIZOME) DEBUGF("Rhizome web server returned %d != 200 OK",http_response_code);
|
2012-07-02 03:49:54 +00:00
|
|
|
rhizome_fetch_close(q);
|
2012-06-27 07:24:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Get content length */
|
|
|
|
s=strstr(q->request,"Content-length: ");
|
|
|
|
if (!s) {
|
2012-01-13 06:51:06 +00:00
|
|
|
if (debug&DEBUG_RHIZOME)
|
2012-06-27 07:24:42 +00:00
|
|
|
DEBUGF("Missing Content-Length: header.");
|
2012-07-02 03:49:54 +00:00
|
|
|
rhizome_fetch_close(q);
|
2012-06-27 07:24:42 +00:00
|
|
|
return;
|
2012-05-22 02:15:54 +00:00
|
|
|
}
|
2012-06-27 07:24:42 +00:00
|
|
|
q->file_len=strtoll(&s[16],NULL,10);
|
2012-01-13 06:51:06 +00:00
|
|
|
|
2012-06-27 07:24:42 +00:00
|
|
|
if (q->file_len<0) {
|
|
|
|
if (debug&DEBUG_RHIZOME)
|
|
|
|
DEBUGF("Illegal file size (%d).",q->file_len);
|
2012-07-02 03:49:54 +00:00
|
|
|
rhizome_fetch_close(q);
|
2012-06-27 07:24:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Okay, we have both, and are all set.
|
|
|
|
File is already open, so just write out any initial bytes of the
|
|
|
|
file we read, and update state flag.
|
|
|
|
*/
|
|
|
|
int fileRxBytes=q->request_len-(i+1);
|
|
|
|
if (fileRxBytes>0)
|
|
|
|
if (fwrite(&q->request[i+1],fileRxBytes,1,q->file)!=1)
|
2012-01-13 06:51:06 +00:00
|
|
|
{
|
|
|
|
if (debug&DEBUG_RHIZOME)
|
2012-06-27 07:24:42 +00:00
|
|
|
DEBUGF("Failed writing initial %d bytes to file.",
|
|
|
|
fileRxBytes);
|
2012-07-02 03:49:54 +00:00
|
|
|
rhizome_fetch_close(q);
|
2012-06-27 07:24:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
q->file_ofs=fileRxBytes;
|
|
|
|
DEBUGF("Transferred (%lld of %lld)",
|
|
|
|
q->file_ofs,q->file_len);
|
|
|
|
q->state=RHIZOME_FETCH_RXFILE;
|
2012-01-13 06:51:06 +00:00
|
|
|
}
|
|
|
|
|
2012-06-27 07:24:42 +00:00
|
|
|
}
|
2012-01-13 06:51:06 +00:00
|
|
|
|
2012-06-27 07:24:42 +00:00
|
|
|
if (sigPipeFlag||((bytes==0)&&(errno==0))) {
|
|
|
|
/* broken pipe, so close connection */
|
2012-05-22 07:38:12 +00:00
|
|
|
if (debug&DEBUG_RHIZOME)
|
2012-06-27 07:24:42 +00:00
|
|
|
DEBUG("Closing rhizome fetch connection due to sigpipe");
|
2012-07-02 03:49:54 +00:00
|
|
|
rhizome_fetch_close(q);
|
2012-06-27 07:24:42 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (debug&DEBUG_RHIZOME)
|
|
|
|
DEBUG("Closing rhizome fetch connection due to illegal/unimplemented state.");
|
2012-07-02 03:49:54 +00:00
|
|
|
rhizome_fetch_close(q);
|
2012-06-27 07:24:42 +00:00
|
|
|
return;
|
2012-01-13 06:51:06 +00:00
|
|
|
}
|
2012-06-27 07:24:42 +00:00
|
|
|
return;
|
2012-01-13 06:51:06 +00:00
|
|
|
}
|