(libT) copyediting: modify more files to the new indentation/whitespace formatting

This commit is contained in:
Jordan Lee 2013-01-24 23:59:52 +00:00
parent 1c86069e7b
commit ad3407567b
6 changed files with 2015 additions and 1952 deletions

View File

@ -21,10 +21,12 @@
#include "utils.h"
#define dbgmsg(...) \
do { \
if (tr_deepLoggingIsActive ()) \
tr_deepLog (__FILE__, __LINE__, NULL, __VA_ARGS__); \
} while (0)
do \
{ \
if (tr_deepLoggingIsActive ()) \
tr_deepLog (__FILE__, __LINE__, NULL, __VA_ARGS__); \
} \
while (0)
/***
****
@ -33,48 +35,54 @@
static unsigned int
getSpeed_Bps (const struct bratecontrol * r, unsigned int interval_msec, uint64_t now)
{
if (!now)
now = tr_time_msec ();
if (!now)
now = tr_time_msec ();
if (now != r->cache_time)
if (now != r->cache_time)
{
int i = r->newest;
uint64_t bytes = 0;
const uint64_t cutoff = now - interval_msec;
struct bratecontrol * rvolatile = (struct bratecontrol*) r;
int i = r->newest;
uint64_t bytes = 0;
const uint64_t cutoff = now - interval_msec;
struct bratecontrol * rvolatile = (struct bratecontrol*) r;
for (;;)
for (;;)
{
if (r->transfers[i].date <= cutoff)
break;
if (r->transfers[i].date <= cutoff)
break;
bytes += r->transfers[i].size;
bytes += r->transfers[i].size;
if (--i == -1) i = HISTORY_SIZE - 1; /* circular history */
if (i == r->newest) break; /* we've come all the way around */
if (--i == -1)
i = HISTORY_SIZE - 1; /* circular history */
if (i == r->newest)
break; /* we've come all the way around */
}
rvolatile->cache_val = (unsigned int)((bytes * 1000u) / interval_msec);
rvolatile->cache_time = now;
rvolatile->cache_val = (unsigned int)((bytes * 1000u) / interval_msec);
rvolatile->cache_time = now;
}
return r->cache_val;
return r->cache_val;
}
static void
bytesUsed (const uint64_t now, struct bratecontrol * r, size_t size)
{
if (r->transfers[r->newest].date + GRANULARITY_MSEC >= now)
r->transfers[r->newest].size += size;
else
if (r->transfers[r->newest].date + GRANULARITY_MSEC >= now)
{
if (++r->newest == HISTORY_SIZE) r->newest = 0;
r->transfers[r->newest].date = now;
r->transfers[r->newest].size = size;
r->transfers[r->newest].size += size;
}
else
{
if (++r->newest == HISTORY_SIZE)
r->newest = 0;
r->transfers[r->newest].date = now;
r->transfers[r->newest].size = size;
}
/* invalidate cache_val*/
r->cache_time = 0;
/* invalidate cache_val*/
r->cache_time = 0;
}
/******
@ -85,9 +93,9 @@ bytesUsed (const uint64_t now, struct bratecontrol * r, size_t size)
static int
compareBandwidth (const void * va, const void * vb)
{
const tr_bandwidth * a = va;
const tr_bandwidth * b = vb;
return a->uniqueKey - b->uniqueKey;
const tr_bandwidth * a = va;
const tr_bandwidth * b = vb;
return a->uniqueKey - b->uniqueKey;
}
/***
@ -97,26 +105,26 @@ compareBandwidth (const void * va, const void * vb)
void
tr_bandwidthConstruct (tr_bandwidth * b, tr_session * session, tr_bandwidth * parent)
{
static unsigned int uniqueKey = 0;
static unsigned int uniqueKey = 0;
b->session = session;
b->children = TR_PTR_ARRAY_INIT;
b->magicNumber = BANDWIDTH_MAGIC_NUMBER;
b->uniqueKey = uniqueKey++;
b->band[TR_UP].honorParentLimits = true;
b->band[TR_DOWN].honorParentLimits = true;
tr_bandwidthSetParent (b, parent);
b->session = session;
b->children = TR_PTR_ARRAY_INIT;
b->magicNumber = BANDWIDTH_MAGIC_NUMBER;
b->uniqueKey = uniqueKey++;
b->band[TR_UP].honorParentLimits = true;
b->band[TR_DOWN].honorParentLimits = true;
tr_bandwidthSetParent (b, parent);
}
void
tr_bandwidthDestruct (tr_bandwidth * b)
{
assert (tr_isBandwidth (b));
assert (tr_isBandwidth (b));
tr_bandwidthSetParent (b, NULL);
tr_ptrArrayDestruct (&b->children, NULL);
tr_bandwidthSetParent (b, NULL);
tr_ptrArrayDestruct (&b->children, NULL);
memset (b, ~0, sizeof (tr_bandwidth));
memset (b, ~0, sizeof (tr_bandwidth));
}
/***
@ -127,31 +135,31 @@ void
tr_bandwidthSetParent (tr_bandwidth * b,
tr_bandwidth * parent)
{
assert (tr_isBandwidth (b));
assert (b != parent);
assert (tr_isBandwidth (b));
assert (b != parent);
if (b->parent)
if (b->parent)
{
void * removed;
void * removed;
assert (tr_isBandwidth (b->parent));
assert (tr_isBandwidth (b->parent));
removed = tr_ptrArrayRemoveSorted (&b->parent->children, b, compareBandwidth);
assert (removed == b);
assert (tr_ptrArrayFindSorted (&b->parent->children, b, compareBandwidth) == NULL);
removed = tr_ptrArrayRemoveSorted (&b->parent->children, b, compareBandwidth);
assert (removed == b);
assert (tr_ptrArrayFindSorted (&b->parent->children, b, compareBandwidth) == NULL);
b->parent = NULL;
b->parent = NULL;
}
if (parent)
if (parent)
{
assert (tr_isBandwidth (parent));
assert (parent->parent != b);
assert (tr_isBandwidth (parent));
assert (parent->parent != b);
assert (tr_ptrArrayFindSorted (&parent->children, b, compareBandwidth) == NULL);
tr_ptrArrayInsertSorted (&parent->children, b, compareBandwidth);
assert (tr_ptrArrayFindSorted (&parent->children, b, compareBandwidth) == b);
b->parent = parent;
assert (tr_ptrArrayFindSorted (&parent->children, b, compareBandwidth) == NULL);
tr_ptrArrayInsertSorted (&parent->children, b, compareBandwidth);
assert (tr_ptrArrayFindSorted (&parent->children, b, compareBandwidth) == b);
b->parent = parent;
}
}
@ -166,66 +174,69 @@ allocateBandwidth (tr_bandwidth * b,
unsigned int period_msec,
tr_ptrArray * peer_pool)
{
const tr_priority_t priority = MAX (parent_priority, b->priority);
const tr_priority_t priority = MAX (parent_priority, b->priority);
assert (tr_isBandwidth (b));
assert (tr_isDirection (dir));
assert (tr_isBandwidth (b));
assert (tr_isDirection (dir));
/* set the available bandwidth */
if (b->band[dir].isLimited)
/* set the available bandwidth */
if (b->band[dir].isLimited)
{
const uint64_t nextPulseSpeed = b->band[dir].desiredSpeed_Bps;
b->band[dir].bytesLeft = (unsigned int)(nextPulseSpeed * period_msec) / 1000u;
const uint64_t nextPulseSpeed = b->band[dir].desiredSpeed_Bps;
b->band[dir].bytesLeft = (unsigned int)(nextPulseSpeed * period_msec) / 1000u;
}
/* add this bandwidth's peer, if any, to the peer pool */
if (b->peer != NULL) {
b->peer->priority = priority;
tr_ptrArrayAppend (peer_pool, b->peer);
/* add this bandwidth's peer, if any, to the peer pool */
if (b->peer != NULL)
{
b->peer->priority = priority;
tr_ptrArrayAppend (peer_pool, b->peer);
}
/* traverse & repeat for the subtree */
if (1) {
int i;
struct tr_bandwidth ** children = (struct tr_bandwidth**) tr_ptrArrayBase (&b->children);
const int n = tr_ptrArraySize (&b->children);
for (i=0; i<n; ++i)
allocateBandwidth (children[i], priority, dir, period_msec, peer_pool);
/* traverse & repeat for the subtree */
if (1)
{
int i;
struct tr_bandwidth ** children = (struct tr_bandwidth**) tr_ptrArrayBase (&b->children);
const int n = tr_ptrArraySize (&b->children);
for (i=0; i<n; ++i)
allocateBandwidth (children[i], priority, dir, period_msec, peer_pool);
}
}
static void
phaseOne (tr_ptrArray * peerArray, tr_direction dir)
{
int n;
int peerCount = tr_ptrArraySize (peerArray);
struct tr_peerIo ** peers = (struct tr_peerIo**) tr_ptrArrayBase (peerArray);
int n;
int peerCount = tr_ptrArraySize (peerArray);
struct tr_peerIo ** peers = (struct tr_peerIo**) tr_ptrArrayBase (peerArray);
/* First phase of IO. Tries to distribute bandwidth fairly to keep faster
* peers from starving the others. Loop through the peers, giving each a
* small chunk of bandwidth. Keep looping until we run out of bandwidth
* and/or peers that can use it */
n = peerCount;
dbgmsg ("%d peers to go round-robin for %s", n, (dir==TR_UP?"upload":"download"));
while (n > 0)
/* First phase of IO. Tries to distribute bandwidth fairly to keep faster
* peers from starving the others. Loop through the peers, giving each a
* small chunk of bandwidth. Keep looping until we run out of bandwidth
* and/or peers that can use it */
n = peerCount;
dbgmsg ("%d peers to go round-robin for %s", n, (dir==TR_UP?"upload":"download"));
while (n > 0)
{
const int i = tr_cryptoWeakRandInt (n); /* pick a peer at random */
const int i = tr_cryptoWeakRandInt (n); /* pick a peer at random */
/* value of 3000 bytes chosen so that when using uTP we'll send a full-size
* frame right away and leave enough buffered data for the next frame to go
* out in a timely manner. */
const size_t increment = 3000;
/* value of 3000 bytes chosen so that when using uTP we'll send a full-size
* frame right away and leave enough buffered data for the next frame to go
* out in a timely manner. */
const size_t increment = 3000;
const int bytesUsed = tr_peerIoFlush (peers[i], dir, increment);
const int bytesUsed = tr_peerIoFlush (peers[i], dir, increment);
dbgmsg ("peer #%d of %d used %d bytes in this pass", i, n, bytesUsed);
dbgmsg ("peer #%d of %d used %d bytes in this pass", i, n, bytesUsed);
if (bytesUsed != (int)increment) {
/* peer is done writing for now; move it to the end of the list */
tr_peerIo * pio = peers[i];
peers[i] = peers[n-1];
peers[n-1] = pio;
--n;
if (bytesUsed != (int)increment)
{
/* peer is done writing for now; move it to the end of the list */
tr_peerIo * pio = peers[i];
peers[i] = peers[n-1];
peers[n-1] = pio;
--n;
}
}
}
@ -235,66 +246,67 @@ tr_bandwidthAllocate (tr_bandwidth * b,
tr_direction dir,
unsigned int period_msec)
{
int i, peerCount;
tr_ptrArray tmp = TR_PTR_ARRAY_INIT;
tr_ptrArray low = TR_PTR_ARRAY_INIT;
tr_ptrArray high = TR_PTR_ARRAY_INIT;
tr_ptrArray normal = TR_PTR_ARRAY_INIT;
struct tr_peerIo ** peers;
int i, peerCount;
tr_ptrArray tmp = TR_PTR_ARRAY_INIT;
tr_ptrArray low = TR_PTR_ARRAY_INIT;
tr_ptrArray high = TR_PTR_ARRAY_INIT;
tr_ptrArray normal = TR_PTR_ARRAY_INIT;
struct tr_peerIo ** peers;
/* allocateBandwidth () is a helper function with two purposes:
* 1. allocate bandwidth to b and its subtree
* 2. accumulate an array of all the peerIos from b and its subtree. */
allocateBandwidth (b, TR_PRI_LOW, dir, period_msec, &tmp);
peers = (struct tr_peerIo**) tr_ptrArrayBase (&tmp);
peerCount = tr_ptrArraySize (&tmp);
/* allocateBandwidth () is a helper function with two purposes:
* 1. allocate bandwidth to b and its subtree
* 2. accumulate an array of all the peerIos from b and its subtree. */
allocateBandwidth (b, TR_PRI_LOW, dir, period_msec, &tmp);
peers = (struct tr_peerIo**) tr_ptrArrayBase (&tmp);
peerCount = tr_ptrArraySize (&tmp);
for (i=0; i<peerCount; ++i)
for (i=0; i<peerCount; ++i)
{
tr_peerIo * io = peers[i];
tr_peerIoRef (io);
tr_peerIo * io = peers[i];
tr_peerIoRef (io);
tr_peerIoFlushOutgoingProtocolMsgs (io);
tr_peerIoFlushOutgoingProtocolMsgs (io);
switch (io->priority) {
case TR_PRI_HIGH: tr_ptrArrayAppend (&high, io); /* fall through */
case TR_PRI_NORMAL: tr_ptrArrayAppend (&normal, io); /* fall through */
default: tr_ptrArrayAppend (&low, io);
switch (io->priority)
{
case TR_PRI_HIGH: tr_ptrArrayAppend (&high, io); /* fall through */
case TR_PRI_NORMAL: tr_ptrArrayAppend (&normal, io); /* fall through */
default: tr_ptrArrayAppend (&low, io);
}
}
/* First phase of IO. Tries to distribute bandwidth fairly to keep faster
* peers from starving the others. Loop through the peers, giving each a
* small chunk of bandwidth. Keep looping until we run out of bandwidth
* and/or peers that can use it */
phaseOne (&high, dir);
phaseOne (&normal, dir);
phaseOne (&low, dir);
/* First phase of IO. Tries to distribute bandwidth fairly to keep faster
* peers from starving the others. Loop through the peers, giving each a
* small chunk of bandwidth. Keep looping until we run out of bandwidth
* and/or peers that can use it */
phaseOne (&high, dir);
phaseOne (&normal, dir);
phaseOne (&low, dir);
/* Second phase of IO. To help us scale in high bandwidth situations,
* enable on-demand IO for peers with bandwidth left to burn.
* This on-demand IO is enabled until (1) the peer runs out of bandwidth,
* or (2) the next tr_bandwidthAllocate () call, when we start over again. */
for (i=0; i<peerCount; ++i)
tr_peerIoSetEnabled (peers[i], dir, tr_peerIoHasBandwidthLeft (peers[i], dir));
/* Second phase of IO. To help us scale in high bandwidth situations,
* enable on-demand IO for peers with bandwidth left to burn.
* This on-demand IO is enabled until (1) the peer runs out of bandwidth,
* or (2) the next tr_bandwidthAllocate () call, when we start over again. */
for (i=0; i<peerCount; ++i)
tr_peerIoSetEnabled (peers[i], dir, tr_peerIoHasBandwidthLeft (peers[i], dir));
for (i=0; i<peerCount; ++i)
tr_peerIoUnref (peers[i]);
for (i=0; i<peerCount; ++i)
tr_peerIoUnref (peers[i]);
/* cleanup */
tr_ptrArrayDestruct (&normal, NULL);
tr_ptrArrayDestruct (&high, NULL);
tr_ptrArrayDestruct (&low, NULL);
tr_ptrArrayDestruct (&tmp, NULL);
/* cleanup */
tr_ptrArrayDestruct (&normal, NULL);
tr_ptrArrayDestruct (&high, NULL);
tr_ptrArrayDestruct (&low, NULL);
tr_ptrArrayDestruct (&tmp, NULL);
}
void
tr_bandwidthSetPeer (tr_bandwidth * b, tr_peerIo * peer)
{
assert (tr_isBandwidth (b));
assert ((peer == NULL) || tr_isPeerIo (peer));
assert (tr_isBandwidth (b));
assert ((peer == NULL) || tr_isPeerIo (peer));
b->peer = peer;
b->peer = peer;
}
/***
@ -307,85 +319,85 @@ bandwidthClamp (const tr_bandwidth * b,
tr_direction dir,
unsigned int byteCount)
{
assert (tr_isBandwidth (b));
assert (tr_isDirection (dir));
assert (tr_isBandwidth (b));
assert (tr_isDirection (dir));
if (b)
if (b)
{
if (b->band[dir].isLimited)
if (b->band[dir].isLimited)
{
byteCount = MIN (byteCount, b->band[dir].bytesLeft);
byteCount = MIN (byteCount, b->band[dir].bytesLeft);
/* if we're getting close to exceeding the speed limit,
* clamp down harder on the bytes available */
if (byteCount > 0)
/* if we're getting close to exceeding the speed limit,
* clamp down harder on the bytes available */
if (byteCount > 0)
{
double current;
double desired;
double r;
double current;
double desired;
double r;
if (now == 0)
now = tr_time_msec ();
if (now == 0)
now = tr_time_msec ();
current = tr_bandwidthGetRawSpeed_Bps (b, now, TR_DOWN);
desired = tr_bandwidthGetDesiredSpeed_Bps (b, TR_DOWN);
r = desired >= 1 ? current / desired : 0;
current = tr_bandwidthGetRawSpeed_Bps (b, now, TR_DOWN);
desired = tr_bandwidthGetDesiredSpeed_Bps (b, TR_DOWN);
r = desired >= 1 ? current / desired : 0;
if (r > 1.0) byteCount = 0;
else if (r > 0.9) byteCount *= 0.8;
else if (r > 0.8) byteCount *= 0.9;
if (r > 1.0) byteCount = 0;
else if (r > 0.9) byteCount *= 0.8;
else if (r > 0.8) byteCount *= 0.9;
}
}
if (b->parent && b->band[dir].honorParentLimits && (byteCount > 0))
byteCount = bandwidthClamp (b->parent, now, dir, byteCount);
if (b->parent && b->band[dir].honorParentLimits && (byteCount > 0))
byteCount = bandwidthClamp (b->parent, now, dir, byteCount);
}
return byteCount;
return byteCount;
}
unsigned int
tr_bandwidthClamp (const tr_bandwidth * b,
tr_direction dir,
unsigned int byteCount)
{
return bandwidthClamp (b, 0, dir, byteCount);
return bandwidthClamp (b, 0, dir, byteCount);
}
unsigned int
tr_bandwidthGetRawSpeed_Bps (const tr_bandwidth * b, const uint64_t now, const tr_direction dir)
{
assert (tr_isBandwidth (b));
assert (tr_isDirection (dir));
assert (tr_isBandwidth (b));
assert (tr_isDirection (dir));
return getSpeed_Bps (&b->band[dir].raw, HISTORY_MSEC, now);
return getSpeed_Bps (&b->band[dir].raw, HISTORY_MSEC, now);
}
unsigned int
tr_bandwidthGetPieceSpeed_Bps (const tr_bandwidth * b, const uint64_t now, const tr_direction dir)
{
assert (tr_isBandwidth (b));
assert (tr_isDirection (dir));
assert (tr_isBandwidth (b));
assert (tr_isDirection (dir));
return getSpeed_Bps (&b->band[dir].piece, HISTORY_MSEC, now);
return getSpeed_Bps (&b->band[dir].piece, HISTORY_MSEC, now);
}
void
tr_bandwidthUsed (tr_bandwidth * b,
tr_direction dir,
size_t byteCount,
bool isPieceData,
bool isPieceData,
uint64_t now)
{
struct tr_band * band;
struct tr_band * band;
assert (tr_isBandwidth (b));
assert (tr_isDirection (dir));
assert (tr_isBandwidth (b));
assert (tr_isDirection (dir));
band = &b->band[dir];
band = &b->band[dir];
if (band->isLimited && isPieceData)
band->bytesLeft -= MIN (band->bytesLeft, byteCount);
if (band->isLimited && isPieceData)
band->bytesLeft -= MIN (band->bytesLeft, byteCount);
#ifdef DEBUG_DIRECTION
if ((dir == DEBUG_DIRECTION) && (band->isLimited))
@ -393,11 +405,11 @@ fprintf (stderr, "%p consumed %5zu bytes of %5s data... was %6zu, now %6zu left\
b, byteCount, (isPieceData?"piece":"raw"), oldBytesLeft, band->bytesLeft);
#endif
bytesUsed (now, &band->raw, byteCount);
bytesUsed (now, &band->raw, byteCount);
if (isPieceData)
bytesUsed (now, &band->piece, byteCount);
if (isPieceData)
bytesUsed (now, &band->piece, byteCount);
if (b->parent != NULL)
tr_bandwidthUsed (b->parent, dir, byteCount, isPieceData, now);
if (b->parent != NULL)
tr_bandwidthUsed (b->parent, dir, byteCount, isPieceData, now);
}

View File

@ -37,19 +37,19 @@
void
tr_sha1 (uint8_t * setme, const void * content1, int content1_len, ...)
{
va_list vl;
SHA_CTX sha;
const void * content;
va_list vl;
SHA_CTX sha;
const void * content;
SHA1_Init (&sha);
SHA1_Update (&sha, content1, content1_len);
SHA1_Init (&sha);
SHA1_Update (&sha, content1, content1_len);
va_start (vl, content1_len);
while ((content = va_arg (vl, const void*)))
SHA1_Update (&sha, content, va_arg (vl, int));
va_end (vl);
va_start (vl, content1_len);
while ((content = va_arg (vl, const void*)))
SHA1_Update (&sha, content, va_arg (vl, int));
va_end (vl);
SHA1_Final (setme, &sha);
SHA1_Final (setme, &sha);
}
/**
@ -65,14 +65,14 @@ tr_sha1 (uint8_t * setme, const void * content1, int content1_len, ...)
static const uint8_t dh_P[PRIME_LEN] =
{
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
0xA6, 0x3A, 0x36, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x63,
};
static const uint8_t dh_G[] = { 2 };
@ -82,68 +82,70 @@ static const uint8_t dh_G[] = { 2 };
**/
#define logErrorFromSSL(...) \
do { \
if (tr_msgLoggingIsActive (TR_MSG_ERR)) { \
char buf[512]; \
ERR_error_string_n (ERR_get_error (), buf, sizeof (buf)); \
tr_msg (__FILE__, __LINE__, TR_MSG_ERR, MY_NAME, "%s", buf); \
} \
} while (0)
do { \
if (tr_msgLoggingIsActive (TR_MSG_ERR)) { \
char buf[512]; \
ERR_error_string_n (ERR_get_error (), buf, sizeof (buf)); \
tr_msg (__FILE__, __LINE__, TR_MSG_ERR, MY_NAME, "%s", buf); \
} \
} while (0)
static void
ensureKeyExists (tr_crypto * crypto)
{
if (crypto->dh == NULL)
if (crypto->dh == NULL)
{
int len, offset;
DH * dh = DH_new ();
int len, offset;
DH * dh = DH_new ();
dh->p = BN_bin2bn (dh_P, sizeof (dh_P), NULL);
if (dh->p == NULL)
dh->p = BN_bin2bn (dh_P, sizeof (dh_P), NULL);
if (dh->p == NULL)
logErrorFromSSL ();
dh->g = BN_bin2bn (dh_G, sizeof (dh_G), NULL);
if (dh->g == NULL)
logErrorFromSSL ();
/* private DH value: strong random BN of DH_PRIVKEY_LEN*8 bits */
dh->priv_key = BN_new ();
do
{
if (BN_rand (dh->priv_key, DH_PRIVKEY_LEN * 8, -1, 0) != 1)
logErrorFromSSL ();
}
while (BN_num_bits (dh->priv_key) < DH_PRIVKEY_LEN_MIN * 8);
dh->g = BN_bin2bn (dh_G, sizeof (dh_G), NULL);
if (dh->g == NULL)
logErrorFromSSL ();
if (!DH_generate_key (dh))
logErrorFromSSL ();
/* private DH value: strong random BN of DH_PRIVKEY_LEN*8 bits */
dh->priv_key = BN_new ();
do {
if (BN_rand (dh->priv_key, DH_PRIVKEY_LEN * 8, -1, 0) != 1)
logErrorFromSSL ();
} while (BN_num_bits (dh->priv_key) < DH_PRIVKEY_LEN_MIN * 8);
/* DH can generate key sizes that are smaller than the size of
P with exponentially decreasing probability, in which case
the msb's of myPublicKey need to be zeroed appropriately. */
len = BN_num_bytes (dh->pub_key);
offset = KEY_LEN - len;
assert (len <= KEY_LEN);
memset (crypto->myPublicKey, 0, offset);
BN_bn2bin (dh->pub_key, crypto->myPublicKey + offset);
if (!DH_generate_key (dh))
logErrorFromSSL ();
/* DH can generate key sizes that are smaller than the size of
P with exponentially decreasing probability, in which case
the msb's of myPublicKey need to be zeroed appropriately. */
len = BN_num_bytes (dh->pub_key);
offset = KEY_LEN - len;
assert (len <= KEY_LEN);
memset (crypto->myPublicKey, 0, offset);
BN_bn2bin (dh->pub_key, crypto->myPublicKey + offset);
crypto->dh = dh;
crypto->dh = dh;
}
}
void
tr_cryptoConstruct (tr_crypto * crypto, const uint8_t * torrentHash, bool isIncoming)
{
memset (crypto, 0, sizeof (tr_crypto));
memset (crypto, 0, sizeof (tr_crypto));
crypto->dh = NULL;
crypto->isIncoming = isIncoming;
tr_cryptoSetTorrentHash (crypto, torrentHash);
crypto->dh = NULL;
crypto->isIncoming = isIncoming;
tr_cryptoSetTorrentHash (crypto, torrentHash);
}
void
tr_cryptoDestruct (tr_crypto * crypto)
{
if (crypto->dh != NULL)
DH_free (crypto->dh);
if (crypto->dh != NULL)
DH_free (crypto->dh);
}
/**
@ -154,39 +156,42 @@ const uint8_t*
tr_cryptoComputeSecret (tr_crypto * crypto,
const uint8_t * peerPublicKey)
{
int len;
uint8_t secret[KEY_LEN];
BIGNUM * bn = BN_bin2bn (peerPublicKey, KEY_LEN, NULL);
DH * dh;
DH * dh;
int len;
uint8_t secret[KEY_LEN];
BIGNUM * bn = BN_bin2bn (peerPublicKey, KEY_LEN, NULL);
ensureKeyExists (crypto);
dh = crypto->dh;
ensureKeyExists (crypto);
dh = crypto->dh;
assert (DH_size (dh) == KEY_LEN);
assert (DH_size (dh) == KEY_LEN);
len = DH_compute_key (secret, bn, dh);
if (len == -1)
logErrorFromSSL ();
else {
int offset;
assert (len <= KEY_LEN);
offset = KEY_LEN - len;
memset (crypto->mySecret, 0, offset);
memcpy (crypto->mySecret + offset, secret, len);
crypto->mySecretIsSet = 1;
len = DH_compute_key (secret, bn, dh);
if (len == -1)
{
logErrorFromSSL ();
}
else
{
int offset;
assert (len <= KEY_LEN);
offset = KEY_LEN - len;
memset (crypto->mySecret, 0, offset);
memcpy (crypto->mySecret + offset, secret, len);
crypto->mySecretIsSet = 1;
}
BN_free (bn);
return crypto->mySecret;
BN_free (bn);
return crypto->mySecret;
}
const uint8_t*
tr_cryptoGetMyPublicKey (const tr_crypto * crypto,
int * setme_len)
int * setme_len)
{
ensureKeyExists ((tr_crypto *) crypto);
*setme_len = KEY_LEN;
return crypto->myPublicKey;
ensureKeyExists ((tr_crypto *) crypto);
*setme_len = KEY_LEN;
return crypto->myPublicKey;
}
/**
@ -194,47 +199,47 @@ tr_cryptoGetMyPublicKey (const tr_crypto * crypto,
**/
static void
initRC4 (tr_crypto * crypto,
RC4_KEY * setme,
initRC4 (tr_crypto * crypto,
RC4_KEY * setme,
const char * key)
{
SHA_CTX sha;
uint8_t buf[SHA_DIGEST_LENGTH];
SHA_CTX sha;
uint8_t buf[SHA_DIGEST_LENGTH];
assert (crypto->torrentHashIsSet);
assert (crypto->mySecretIsSet);
assert (crypto->torrentHashIsSet);
assert (crypto->mySecretIsSet);
if (SHA1_Init (&sha)
&& SHA1_Update (&sha, key, 4)
&& SHA1_Update (&sha, crypto->mySecret, KEY_LEN)
&& SHA1_Update (&sha, crypto->torrentHash, SHA_DIGEST_LENGTH)
&& SHA1_Final (buf, &sha))
if (SHA1_Init (&sha)
&& SHA1_Update (&sha, key, 4)
&& SHA1_Update (&sha, crypto->mySecret, KEY_LEN)
&& SHA1_Update (&sha, crypto->torrentHash, SHA_DIGEST_LENGTH)
&& SHA1_Final (buf, &sha))
{
RC4_set_key (setme, SHA_DIGEST_LENGTH, buf);
RC4_set_key (setme, SHA_DIGEST_LENGTH, buf);
}
else
else
{
logErrorFromSSL ();
logErrorFromSSL ();
}
}
void
tr_cryptoDecryptInit (tr_crypto * crypto)
{
unsigned char discard[1024];
const char * txt = crypto->isIncoming ? "keyA" : "keyB";
unsigned char discard[1024];
const char * txt = crypto->isIncoming ? "keyA" : "keyB";
initRC4 (crypto, &crypto->dec_key, txt);
RC4 (&crypto->dec_key, sizeof (discard), discard, discard);
initRC4 (crypto, &crypto->dec_key, txt);
RC4 (&crypto->dec_key, sizeof (discard), discard, discard);
}
void
tr_cryptoDecrypt (tr_crypto * crypto,
tr_cryptoDecrypt (tr_crypto * crypto,
size_t buf_len,
const void * buf_in,
void * buf_out)
void * buf_out)
{
RC4 (&crypto->dec_key, buf_len,
RC4 (&crypto->dec_key, buf_len,
(const unsigned char*)buf_in,
(unsigned char*)buf_out);
}
@ -242,20 +247,20 @@ tr_cryptoDecrypt (tr_crypto * crypto,
void
tr_cryptoEncryptInit (tr_crypto * crypto)
{
unsigned char discard[1024];
const char * txt = crypto->isIncoming ? "keyB" : "keyA";
unsigned char discard[1024];
const char * txt = crypto->isIncoming ? "keyB" : "keyA";
initRC4 (crypto, &crypto->enc_key, txt);
RC4 (&crypto->enc_key, sizeof (discard), discard, discard);
initRC4 (crypto, &crypto->enc_key, txt);
RC4 (&crypto->enc_key, sizeof (discard), discard, discard);
}
void
tr_cryptoEncrypt (tr_crypto * crypto,
tr_cryptoEncrypt (tr_crypto * crypto,
size_t buf_len,
const void * buf_in,
void * buf_out)
void * buf_out)
{
RC4 (&crypto->enc_key, buf_len,
RC4 (&crypto->enc_key, buf_len,
(const unsigned char*)buf_in,
(unsigned char*)buf_out);
}
@ -265,75 +270,75 @@ tr_cryptoEncrypt (tr_crypto * crypto,
**/
void
tr_cryptoSetTorrentHash (tr_crypto * crypto,
tr_cryptoSetTorrentHash (tr_crypto * crypto,
const uint8_t * hash)
{
crypto->torrentHashIsSet = hash ? 1 : 0;
crypto->torrentHashIsSet = hash ? 1 : 0;
if (hash)
memcpy (crypto->torrentHash, hash, SHA_DIGEST_LENGTH);
else
memset (crypto->torrentHash, 0, SHA_DIGEST_LENGTH);
if (hash)
memcpy (crypto->torrentHash, hash, SHA_DIGEST_LENGTH);
else
memset (crypto->torrentHash, 0, SHA_DIGEST_LENGTH);
}
const uint8_t*
tr_cryptoGetTorrentHash (const tr_crypto * crypto)
{
assert (crypto);
assert (crypto->torrentHashIsSet);
assert (crypto);
assert (crypto->torrentHashIsSet);
return crypto->torrentHash;
return crypto->torrentHash;
}
int
tr_cryptoHasTorrentHash (const tr_crypto * crypto)
{
assert (crypto);
assert (crypto);
return crypto->torrentHashIsSet ? 1 : 0;
return crypto->torrentHashIsSet ? 1 : 0;
}
int
tr_cryptoRandInt (int upperBound)
{
int noise;
int val;
int noise;
int val;
assert (upperBound > 0);
assert (upperBound > 0);
if (RAND_pseudo_bytes ((unsigned char *) &noise, sizeof noise) >= 0)
if (RAND_pseudo_bytes ((unsigned char *) &noise, sizeof noise) >= 0)
{
val = abs (noise) % upperBound;
val = abs (noise) % upperBound;
}
else /* fall back to a weaker implementation... */
else /* fall back to a weaker implementation... */
{
val = tr_cryptoWeakRandInt (upperBound);
val = tr_cryptoWeakRandInt (upperBound);
}
return val;
return val;
}
int
tr_cryptoWeakRandInt (int upperBound)
{
static bool init = false;
static bool init = false;
assert (upperBound > 0);
assert (upperBound > 0);
if (!init)
if (!init)
{
srand (tr_time_msec ());
init = true;
srand (tr_time_msec ());
init = true;
}
return rand () % upperBound;
return rand () % upperBound;
}
void
tr_cryptoRandBuf (void * buf, size_t len)
{
if (RAND_pseudo_bytes ((unsigned char*)buf, len) != 1)
logErrorFromSSL ();
if (RAND_pseudo_bytes ((unsigned char*)buf, len) != 1)
logErrorFromSSL ();
}
/***
@ -343,61 +348,60 @@ tr_cryptoRandBuf (void * buf, size_t len)
char*
tr_ssha1 (const void * plaintext)
{
enum { saltval_len = 8,
salter_len = 64 };
static const char * salter = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"./";
enum { saltval_len = 8,
salter_len = 64 };
static const char * salter = "0123456789"
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"./";
size_t i;
unsigned char salt[saltval_len];
uint8_t sha[SHA_DIGEST_LENGTH];
char buf[2*SHA_DIGEST_LENGTH + saltval_len + 2];
size_t i;
unsigned char salt[saltval_len];
uint8_t sha[SHA_DIGEST_LENGTH];
char buf[2*SHA_DIGEST_LENGTH + saltval_len + 2];
tr_cryptoRandBuf (salt, saltval_len);
for (i=0; i<saltval_len; ++i)
salt[i] = salter[ salt[i] % salter_len ];
tr_cryptoRandBuf (salt, saltval_len);
for (i=0; i<saltval_len; ++i)
salt[i] = salter[ salt[i] % salter_len ];
tr_sha1 (sha, plaintext, strlen (plaintext), salt, saltval_len, NULL);
tr_sha1_to_hex (&buf[1], sha);
memcpy (&buf[1+2*SHA_DIGEST_LENGTH], &salt, saltval_len);
buf[1+2*SHA_DIGEST_LENGTH + saltval_len] = '\0';
buf[0] = '{'; /* signal that this is a hash. this makes saving/restoring
easier */
tr_sha1 (sha, plaintext, strlen (plaintext), salt, saltval_len, NULL);
tr_sha1_to_hex (&buf[1], sha);
memcpy (&buf[1+2*SHA_DIGEST_LENGTH], &salt, saltval_len);
buf[1+2*SHA_DIGEST_LENGTH + saltval_len] = '\0';
buf[0] = '{'; /* signal that this is a hash. this makes saving/restoring easier */
return tr_strdup (&buf);
return tr_strdup (&buf);
}
bool
tr_ssha1_matches (const char * source, const char * pass)
{
char * salt;
size_t saltlen;
char * hashed;
uint8_t buf[SHA_DIGEST_LENGTH];
bool result;
const size_t sourcelen = strlen (source);
char * salt;
size_t saltlen;
char * hashed;
uint8_t buf[SHA_DIGEST_LENGTH];
bool result;
const size_t sourcelen = strlen (source);
/* extract the salt */
if (sourcelen < 2*SHA_DIGEST_LENGTH-1)
return false;
saltlen = sourcelen - 2*SHA_DIGEST_LENGTH-1;
salt = tr_malloc (saltlen);
memcpy (salt, source + 2*SHA_DIGEST_LENGTH+1, saltlen);
/* extract the salt */
if (sourcelen < 2*SHA_DIGEST_LENGTH-1)
return false;
saltlen = sourcelen - 2*SHA_DIGEST_LENGTH-1;
salt = tr_malloc (saltlen);
memcpy (salt, source + 2*SHA_DIGEST_LENGTH+1, saltlen);
/* hash pass + salt */
hashed = tr_malloc (2*SHA_DIGEST_LENGTH + saltlen + 2);
tr_sha1 (buf, pass, strlen (pass), salt, saltlen, NULL);
tr_sha1_to_hex (&hashed[1], buf);
memcpy (hashed + 1+2*SHA_DIGEST_LENGTH, salt, saltlen);
hashed[1+2*SHA_DIGEST_LENGTH + saltlen] = '\0';
hashed[0] = '{';
/* hash pass + salt */
hashed = tr_malloc (2*SHA_DIGEST_LENGTH + saltlen + 2);
tr_sha1 (buf, pass, strlen (pass), salt, saltlen, NULL);
tr_sha1_to_hex (&hashed[1], buf);
memcpy (hashed + 1+2*SHA_DIGEST_LENGTH, salt, saltlen);
hashed[1+2*SHA_DIGEST_LENGTH + saltlen] = '\0';
hashed[0] = '{';
result = strcmp (source, hashed) == 0 ? true : false;
result = strcmp (source, hashed) == 0 ? true : false;
tr_free (hashed);
tr_free (salt);
tr_free (hashed);
tr_free (salt);
return result;
return result;
}

View File

@ -43,61 +43,61 @@
enum
{
/* how frequently to cull old atoms */
ATOM_PERIOD_MSEC = (60 * 1000),
/* how frequently to cull old atoms */
ATOM_PERIOD_MSEC = (60 * 1000),
/* how frequently to change which peers are choked */
RECHOKE_PERIOD_MSEC = (10 * 1000),
/* how frequently to change which peers are choked */
RECHOKE_PERIOD_MSEC = (10 * 1000),
/* an optimistically unchoked peer is immune from rechoking
for this many calls to rechokeUploads (). */
OPTIMISTIC_UNCHOKE_MULTIPLIER = 4,
/* an optimistically unchoked peer is immune from rechoking
for this many calls to rechokeUploads (). */
OPTIMISTIC_UNCHOKE_MULTIPLIER = 4,
/* how frequently to reallocate bandwidth */
BANDWIDTH_PERIOD_MSEC = 500,
/* how frequently to reallocate bandwidth */
BANDWIDTH_PERIOD_MSEC = 500,
/* how frequently to age out old piece request lists */
REFILL_UPKEEP_PERIOD_MSEC = (10 * 1000),
/* how frequently to age out old piece request lists */
REFILL_UPKEEP_PERIOD_MSEC = (10 * 1000),
/* how frequently to decide which peers live and die */
RECONNECT_PERIOD_MSEC = 500,
/* how frequently to decide which peers live and die */
RECONNECT_PERIOD_MSEC = 500,
/* when many peers are available, keep idle ones this long */
MIN_UPLOAD_IDLE_SECS = (60),
/* when many peers are available, keep idle ones this long */
MIN_UPLOAD_IDLE_SECS = (60),
/* when few peers are available, keep idle ones this long */
MAX_UPLOAD_IDLE_SECS = (60 * 5),
/* when few peers are available, keep idle ones this long */
MAX_UPLOAD_IDLE_SECS = (60 * 5),
/* max number of peers to ask for per second overall.
* this throttle is to avoid overloading the router */
MAX_CONNECTIONS_PER_SECOND = 12,
/* max number of peers to ask for per second overall.
* this throttle is to avoid overloading the router */
MAX_CONNECTIONS_PER_SECOND = 12,
MAX_CONNECTIONS_PER_PULSE = (int)(MAX_CONNECTIONS_PER_SECOND * (RECONNECT_PERIOD_MSEC/1000.0)),
MAX_CONNECTIONS_PER_PULSE = (int)(MAX_CONNECTIONS_PER_SECOND * (RECONNECT_PERIOD_MSEC/1000.0)),
/* number of bad pieces a peer is allowed to send before we ban them */
MAX_BAD_PIECES_PER_PEER = 5,
/* number of bad pieces a peer is allowed to send before we ban them */
MAX_BAD_PIECES_PER_PEER = 5,
/* amount of time to keep a list of request pieces lying around
before it's considered too old and needs to be rebuilt */
PIECE_LIST_SHELF_LIFE_SECS = 60,
/* amount of time to keep a list of request pieces lying around
before it's considered too old and needs to be rebuilt */
PIECE_LIST_SHELF_LIFE_SECS = 60,
/* use for bitwise operations w/peer_atom.flags2 */
MYFLAG_BANNED = 1,
/* use for bitwise operations w/peer_atom.flags2 */
MYFLAG_BANNED = 1,
/* use for bitwise operations w/peer_atom.flags2 */
/* unreachable for now... but not banned.
* if they try to connect to us it's okay */
MYFLAG_UNREACHABLE = 2,
/* use for bitwise operations w/peer_atom.flags2 */
/* unreachable for now... but not banned.
* if they try to connect to us it's okay */
MYFLAG_UNREACHABLE = 2,
/* the minimum we'll wait before attempting to reconnect to a peer */
MINIMUM_RECONNECT_INTERVAL_SECS = 5,
/* the minimum we'll wait before attempting to reconnect to a peer */
MINIMUM_RECONNECT_INTERVAL_SECS = 5,
/** how long we'll let requests we've made linger before we cancel them */
REQUEST_TTL_SECS = 90,
/** how long we'll let requests we've made linger before we cancel them */
REQUEST_TTL_SECS = 90,
NO_BLOCKS_CANCEL_HISTORY = 120,
NO_BLOCKS_CANCEL_HISTORY = 120,
CANCEL_HISTORY_SEC = 60
CANCEL_HISTORY_SEC = 60
};
const tr_peer_event TR_PEER_EVENT_INIT = { 0, 0, NULL, 0, 0, 0, false, 0 };
@ -117,27 +117,27 @@ const tr_peer_event TR_PEER_EVENT_INIT = { 0, 0, NULL, 0, 0, 0, false, 0 };
*/
struct peer_atom
{
uint8_t fromFirst; /* where the peer was first found */
uint8_t fromBest; /* the "best" value of where the peer has been found */
uint8_t flags; /* these match the added_f flags */
uint8_t flags2; /* flags that aren't defined in added_f */
int8_t seedProbability; /* how likely is this to be a seed... [0..100] or -1 for unknown */
int8_t blocklisted; /* -1 for unknown, true for blocklisted, false for not blocklisted */
uint8_t fromFirst; /* where the peer was first found */
uint8_t fromBest; /* the "best" value of where the peer has been found */
uint8_t flags; /* these match the added_f flags */
uint8_t flags2; /* flags that aren't defined in added_f */
int8_t seedProbability; /* how likely is this to be a seed... [0..100] or -1 for unknown */
int8_t blocklisted; /* -1 for unknown, true for blocklisted, false for not blocklisted */
tr_port port;
bool utp_failed; /* We recently failed to connect over uTP */
uint16_t numFails;
time_t time; /* when the peer's connection status last changed */
time_t piece_data_time;
tr_port port;
bool utp_failed; /* We recently failed to connect over uTP */
uint16_t numFails;
time_t time; /* when the peer's connection status last changed */
time_t piece_data_time;
time_t lastConnectionAttemptAt;
time_t lastConnectionAt;
time_t lastConnectionAttemptAt;
time_t lastConnectionAt;
/* similar to a TTL field, but less rigid --
* if the swarm is small, the atom will be kept past this date. */
time_t shelf_date;
tr_peer * peer; /* will be NULL if not connected */
tr_address addr;
/* similar to a TTL field, but less rigid --
* if the swarm is small, the atom will be kept past this date. */
time_t shelf_date;
tr_peer * peer; /* will be NULL if not connected */
tr_address addr;
};
#ifdef NDEBUG
@ -146,92 +146,92 @@ struct peer_atom
static bool
tr_isAtom (const struct peer_atom * atom)
{
return (atom != NULL)
&& (atom->fromFirst < TR_PEER_FROM__MAX)
&& (atom->fromBest < TR_PEER_FROM__MAX)
&& (tr_address_is_valid (&atom->addr));
return (atom != NULL)
&& (atom->fromFirst < TR_PEER_FROM__MAX)
&& (atom->fromBest < TR_PEER_FROM__MAX)
&& (tr_address_is_valid (&atom->addr));
}
#endif
static const char*
tr_atomAddrStr (const struct peer_atom * atom)
{
return atom ? tr_peerIoAddrStr (&atom->addr, atom->port) : "[no atom]";
return atom ? tr_peerIoAddrStr (&atom->addr, atom->port) : "[no atom]";
}
struct block_request
{
tr_block_index_t block;
tr_peer * peer;
time_t sentAt;
tr_block_index_t block;
tr_peer * peer;
time_t sentAt;
};
struct weighted_piece
{
tr_piece_index_t index;
int16_t salt;
int16_t requestCount;
tr_piece_index_t index;
int16_t salt;
int16_t requestCount;
};
enum piece_sort_state
{
PIECES_UNSORTED,
PIECES_SORTED_BY_INDEX,
PIECES_SORTED_BY_WEIGHT
PIECES_UNSORTED,
PIECES_SORTED_BY_INDEX,
PIECES_SORTED_BY_WEIGHT
};
/** @brief Opaque, per-torrent data structure for peer connection information */
typedef struct tr_torrent_peers
{
tr_ptrArray outgoingHandshakes; /* tr_handshake */
tr_ptrArray pool; /* struct peer_atom */
tr_ptrArray peers; /* tr_peer */
tr_ptrArray webseeds; /* tr_webseed */
tr_ptrArray outgoingHandshakes; /* tr_handshake */
tr_ptrArray pool; /* struct peer_atom */
tr_ptrArray peers; /* tr_peer */
tr_ptrArray webseeds; /* tr_webseed */
tr_torrent * tor;
struct tr_peerMgr * manager;
tr_torrent * tor;
struct tr_peerMgr * manager;
tr_peer * optimistic; /* the optimistic peer, or NULL if none */
int optimisticUnchokeTimeScaler;
tr_peer * optimistic; /* the optimistic peer, or NULL if none */
int optimisticUnchokeTimeScaler;
bool isRunning;
bool needsCompletenessCheck;
bool isRunning;
bool needsCompletenessCheck;
struct block_request * requests;
int requestCount;
int requestAlloc;
struct block_request * requests;
int requestCount;
int requestAlloc;
struct weighted_piece * pieces;
int pieceCount;
enum piece_sort_state pieceSortState;
struct weighted_piece * pieces;
int pieceCount;
enum piece_sort_state pieceSortState;
/* An array of pieceCount items stating how many peers have each piece.
This is used to help us for downloading pieces "rarest first."
This may be NULL if we don't have metainfo yet, or if we're not
downloading and don't care about rarity */
uint16_t * pieceReplication;
size_t pieceReplicationSize;
/* An array of pieceCount items stating how many peers have each piece.
This is used to help us for downloading pieces "rarest first."
This may be NULL if we don't have metainfo yet, or if we're not
downloading and don't care about rarity */
uint16_t * pieceReplication;
size_t pieceReplicationSize;
int interestedCount;
int maxPeers;
time_t lastCancel;
int interestedCount;
int maxPeers;
time_t lastCancel;
/* Before the endgame this should be 0. In endgame, is contains the average
* number of pending requests per peer. Only peers which have more pending
* requests are considered 'fast' are allowed to request a block that's
* already been requested from another (slower?) peer. */
int endgame;
/* Before the endgame this should be 0. In endgame, is contains the average
* number of pending requests per peer. Only peers which have more pending
* requests are considered 'fast' are allowed to request a block that's
* already been requested from another (slower?) peer. */
int endgame;
}
Torrent;
struct tr_peerMgr
{
tr_session * session;
tr_ptrArray incomingHandshakes; /* tr_handshake */
struct event * bandwidthTimer;
struct event * rechokeTimer;
struct event * refillUpkeepTimer;
struct event * atomTimer;
tr_session * session;
tr_ptrArray incomingHandshakes; /* tr_handshake */
struct event * bandwidthTimer;
struct event * rechokeTimer;
struct event * refillUpkeepTimer;
struct event * atomTimer;
};
#define tordbg(t, ...) \
@ -257,31 +257,31 @@ struct tr_peerMgr
static inline void
managerLock (const struct tr_peerMgr * manager)
{
tr_sessionLock (manager->session);
tr_sessionLock (manager->session);
}
static inline void
managerUnlock (const struct tr_peerMgr * manager)
{
tr_sessionUnlock (manager->session);
tr_sessionUnlock (manager->session);
}
static inline void
torrentLock (Torrent * torrent)
{
managerLock (torrent->manager);
managerLock (torrent->manager);
}
static inline void
torrentUnlock (Torrent * torrent)
{
managerUnlock (torrent->manager);
managerUnlock (torrent->manager);
}
static inline int
torrentIsLocked (const Torrent * t)
{
return tr_sessionIsLocked (t->manager->session);
return tr_sessionIsLocked (t->manager->session);
}
/**
@ -291,42 +291,42 @@ torrentIsLocked (const Torrent * t)
static int
handshakeCompareToAddr (const void * va, const void * vb)
{
const tr_handshake * a = va;
const tr_handshake * a = va;
return tr_address_compare (tr_handshakeGetAddr (a, NULL), vb);
return tr_address_compare (tr_handshakeGetAddr (a, NULL), vb);
}
static int
handshakeCompare (const void * a, const void * b)
{
return handshakeCompareToAddr (a, tr_handshakeGetAddr (b, NULL));
return handshakeCompareToAddr (a, tr_handshakeGetAddr (b, NULL));
}
static inline tr_handshake*
getExistingHandshake (tr_ptrArray * handshakes, const tr_address * addr)
{
if (tr_ptrArrayEmpty (handshakes))
return NULL;
if (tr_ptrArrayEmpty (handshakes))
return NULL;
return tr_ptrArrayFindSorted (handshakes, addr, handshakeCompareToAddr);
return tr_ptrArrayFindSorted (handshakes, addr, handshakeCompareToAddr);
}
static int
comparePeerAtomToAddress (const void * va, const void * vb)
{
const struct peer_atom * a = va;
const struct peer_atom * a = va;
return tr_address_compare (&a->addr, vb);
return tr_address_compare (&a->addr, vb);
}
static int
compareAtomsByAddress (const void * va, const void * vb)
{
const struct peer_atom * b = vb;
const struct peer_atom * b = vb;
assert (tr_isAtom (b));
assert (tr_isAtom (b));
return comparePeerAtomToAddress (va, &b->addr);
return comparePeerAtomToAddress (va, &b->addr);
}
/**
@ -336,82 +336,82 @@ compareAtomsByAddress (const void * va, const void * vb)
const tr_address *
tr_peerAddress (const tr_peer * peer)
{
return &peer->atom->addr;
return &peer->atom->addr;
}
static Torrent*
getExistingTorrent (tr_peerMgr * manager,
const uint8_t * hash)
{
tr_torrent * tor = tr_torrentFindFromHash (manager->session, hash);
tr_torrent * tor = tr_torrentFindFromHash (manager->session, hash);
return tor == NULL ? NULL : tor->torrentPeers;
return tor == NULL ? NULL : tor->torrentPeers;
}
static int
peerCompare (const void * a, const void * b)
{
return tr_address_compare (tr_peerAddress (a), tr_peerAddress (b));
return tr_address_compare (tr_peerAddress (a), tr_peerAddress (b));
}
static struct peer_atom*
getExistingAtom (const Torrent * t,
const tr_address * addr)
{
Torrent * tt = (Torrent*)t;
return tr_ptrArrayFindSorted (&tt->pool, addr, comparePeerAtomToAddress);
Torrent * tt = (Torrent*)t;
return tr_ptrArrayFindSorted (&tt->pool, addr, comparePeerAtomToAddress);
}
static bool
peerIsInUse (const Torrent * ct, const struct peer_atom * atom)
{
Torrent * t = (Torrent*) ct;
Torrent * t = (Torrent*) ct;
assert (torrentIsLocked (t));
assert (torrentIsLocked (t));
return (atom->peer != NULL)
|| getExistingHandshake (&t->outgoingHandshakes, &atom->addr)
|| getExistingHandshake (&t->manager->incomingHandshakes, &atom->addr);
return (atom->peer != NULL)
|| getExistingHandshake (&t->outgoingHandshakes, &atom->addr)
|| getExistingHandshake (&t->manager->incomingHandshakes, &atom->addr);
}
void
tr_peerConstruct (tr_peer * peer)
{
memset (peer, 0, sizeof (tr_peer));
memset (peer, 0, sizeof (tr_peer));
peer->have = TR_BITFIELD_INIT;
peer->have = TR_BITFIELD_INIT;
}
static tr_peer*
peerNew (struct peer_atom * atom)
{
tr_peer * peer = tr_new (tr_peer, 1);
tr_peerConstruct (peer);
tr_peer * peer = tr_new (tr_peer, 1);
tr_peerConstruct (peer);
peer->atom = atom;
atom->peer = peer;
peer->atom = atom;
atom->peer = peer;
return peer;
return peer;
}
static tr_peer*
getPeer (Torrent * torrent, struct peer_atom * atom)
{
tr_peer * peer;
tr_peer * peer;
assert (torrentIsLocked (torrent));
assert (torrentIsLocked (torrent));
peer = atom->peer;
peer = atom->peer;
if (peer == NULL)
if (peer == NULL)
{
peer = peerNew (atom);
tr_bitfieldConstruct (&peer->have, torrent->tor->info.pieceCount);
tr_bitfieldConstruct (&peer->blame, torrent->tor->blockCount);
tr_ptrArrayInsertSorted (&torrent->peers, peer, peerCompare);
peer = peerNew (atom);
tr_bitfieldConstruct (&peer->have, torrent->tor->info.pieceCount);
tr_bitfieldConstruct (&peer->blame, torrent->tor->blockCount);
tr_ptrArrayInsertSorted (&torrent->peers, peer, peerCompare);
}
return peer;
return peer;
}
static void peerDeclinedAllRequests (Torrent *, const tr_peer *);
@ -419,93 +419,94 @@ static void peerDeclinedAllRequests (Torrent *, const tr_peer *);
void
tr_peerDestruct (tr_torrent * tor, tr_peer * peer)
{
assert (peer != NULL);
assert (peer != NULL);
peerDeclinedAllRequests (tor->torrentPeers, peer);
peerDeclinedAllRequests (tor->torrentPeers, peer);
if (peer->msgs != NULL)
tr_peerMsgsFree (peer->msgs);
if (peer->msgs != NULL)
tr_peerMsgsFree (peer->msgs);
if (peer->io) {
tr_peerIoClear (peer->io);
tr_peerIoUnref (peer->io); /* balanced by the ref in handshakeDoneCB () */
if (peer->io)
{
tr_peerIoClear (peer->io);
tr_peerIoUnref (peer->io); /* balanced by the ref in handshakeDoneCB () */
}
tr_bitfieldDestruct (&peer->have);
tr_bitfieldDestruct (&peer->blame);
tr_bitfieldDestruct (&peer->have);
tr_bitfieldDestruct (&peer->blame);
if (peer->atom)
peer->atom->peer = NULL;
if (peer->atom)
peer->atom->peer = NULL;
}
static void
peerDelete (Torrent * t, tr_peer * peer)
{
tr_peerDestruct (t->tor, peer);
tr_free (peer);
tr_peerDestruct (t->tor, peer);
tr_free (peer);
}
static bool
replicationExists (const Torrent * t)
{
return t->pieceReplication != NULL;
return t->pieceReplication != NULL;
}
static void
replicationFree (Torrent * t)
{
tr_free (t->pieceReplication);
t->pieceReplication = NULL;
t->pieceReplicationSize = 0;
tr_free (t->pieceReplication);
t->pieceReplication = NULL;
t->pieceReplicationSize = 0;
}
static void
replicationNew (Torrent * t)
{
tr_piece_index_t piece_i;
const tr_piece_index_t piece_count = t->tor->info.pieceCount;
tr_peer ** peers = (tr_peer**) tr_ptrArrayBase (&t->peers);
const int peer_count = tr_ptrArraySize (&t->peers);
tr_piece_index_t piece_i;
const tr_piece_index_t piece_count = t->tor->info.pieceCount;
tr_peer ** peers = (tr_peer**) tr_ptrArrayBase (&t->peers);
const int peer_count = tr_ptrArraySize (&t->peers);
assert (!replicationExists (t));
assert (!replicationExists (t));
t->pieceReplicationSize = piece_count;
t->pieceReplication = tr_new0 (uint16_t, piece_count);
t->pieceReplicationSize = piece_count;
t->pieceReplication = tr_new0 (uint16_t, piece_count);
for (piece_i=0; piece_i<piece_count; ++piece_i)
for (piece_i=0; piece_i<piece_count; ++piece_i)
{
int peer_i;
uint16_t r = 0;
int peer_i;
uint16_t r = 0;
for (peer_i=0; peer_i<peer_count; ++peer_i)
if (tr_bitfieldHas (&peers[peer_i]->have, piece_i))
++r;
for (peer_i=0; peer_i<peer_count; ++peer_i)
if (tr_bitfieldHas (&peers[peer_i]->have, piece_i))
++r;
t->pieceReplication[piece_i] = r;
t->pieceReplication[piece_i] = r;
}
}
static void
torrentFree (void * vt)
{
Torrent * t = vt;
Torrent * t = vt;
assert (t);
assert (!t->isRunning);
assert (torrentIsLocked (t));
assert (tr_ptrArrayEmpty (&t->outgoingHandshakes));
assert (tr_ptrArrayEmpty (&t->peers));
assert (t);
assert (!t->isRunning);
assert (torrentIsLocked (t));
assert (tr_ptrArrayEmpty (&t->outgoingHandshakes));
assert (tr_ptrArrayEmpty (&t->peers));
tr_ptrArrayDestruct (&t->webseeds, (PtrArrayForeachFunc)tr_webseedFree);
tr_ptrArrayDestruct (&t->pool, (PtrArrayForeachFunc)tr_free);
tr_ptrArrayDestruct (&t->outgoingHandshakes, NULL);
tr_ptrArrayDestruct (&t->peers, NULL);
tr_ptrArrayDestruct (&t->webseeds, (PtrArrayForeachFunc)tr_webseedFree);
tr_ptrArrayDestruct (&t->pool, (PtrArrayForeachFunc)tr_free);
tr_ptrArrayDestruct (&t->outgoingHandshakes, NULL);
tr_ptrArrayDestruct (&t->peers, NULL);
replicationFree (t);
replicationFree (t);
tr_free (t->requests);
tr_free (t->pieces);
tr_free (t);
tr_free (t->requests);
tr_free (t->pieces);
tr_free (t);
}
static void peerCallbackFunc (tr_peer *, const tr_peer_event *, void *);
@ -513,37 +514,37 @@ static void peerCallbackFunc (tr_peer *, const tr_peer_event *, void *);
static void
rebuildWebseedArray (Torrent * t, tr_torrent * tor)
{
unsigned int i;
const tr_info * inf = &tor->info;
unsigned int i;
const tr_info * inf = &tor->info;
/* clear the array */
tr_ptrArrayDestruct (&t->webseeds, (PtrArrayForeachFunc)tr_webseedFree);
t->webseeds = TR_PTR_ARRAY_INIT;
/* clear the array */
tr_ptrArrayDestruct (&t->webseeds, (PtrArrayForeachFunc)tr_webseedFree);
t->webseeds = TR_PTR_ARRAY_INIT;
/* repopulate it */
for (i = 0; i < inf->webseedCount; ++i)
/* repopulate it */
for (i = 0; i < inf->webseedCount; ++i)
{
tr_webseed * w = tr_webseedNew (tor, inf->webseeds[i], peerCallbackFunc, t);
tr_ptrArrayAppend (&t->webseeds, w);
tr_webseed * w = tr_webseedNew (tor, inf->webseeds[i], peerCallbackFunc, t);
tr_ptrArrayAppend (&t->webseeds, w);
}
}
static Torrent*
torrentNew (tr_peerMgr * manager, tr_torrent * tor)
{
Torrent * t;
Torrent * t;
t = tr_new0 (Torrent, 1);
t->manager = manager;
t->tor = tor;
t->pool = TR_PTR_ARRAY_INIT;
t->peers = TR_PTR_ARRAY_INIT;
t->webseeds = TR_PTR_ARRAY_INIT;
t->outgoingHandshakes = TR_PTR_ARRAY_INIT;
t = tr_new0 (Torrent, 1);
t->manager = manager;
t->tor = tor;
t->pool = TR_PTR_ARRAY_INIT;
t->peers = TR_PTR_ARRAY_INIT;
t->webseeds = TR_PTR_ARRAY_INIT;
t->outgoingHandshakes = TR_PTR_ARRAY_INIT;
rebuildWebseedArray (t, tor);
rebuildWebseedArray (t, tor);
return t;
return t;
}
static void ensureMgrTimersExist (struct tr_peerMgr * m);
@ -551,63 +552,63 @@ static void ensureMgrTimersExist (struct tr_peerMgr * m);
tr_peerMgr*
tr_peerMgrNew (tr_session * session)
{
tr_peerMgr * m = tr_new0 (tr_peerMgr, 1);
m->session = session;
m->incomingHandshakes = TR_PTR_ARRAY_INIT;
ensureMgrTimersExist (m);
return m;
tr_peerMgr * m = tr_new0 (tr_peerMgr, 1);
m->session = session;
m->incomingHandshakes = TR_PTR_ARRAY_INIT;
ensureMgrTimersExist (m);
return m;
}
static void
deleteTimer (struct event ** t)
{
if (*t != NULL)
if (*t != NULL)
{
event_free (*t);
*t = NULL;
event_free (*t);
*t = NULL;
}
}
static void
deleteTimers (struct tr_peerMgr * m)
{
deleteTimer (&m->atomTimer);
deleteTimer (&m->bandwidthTimer);
deleteTimer (&m->rechokeTimer);
deleteTimer (&m->refillUpkeepTimer);
deleteTimer (&m->atomTimer);
deleteTimer (&m->bandwidthTimer);
deleteTimer (&m->rechokeTimer);
deleteTimer (&m->refillUpkeepTimer);
}
void
tr_peerMgrFree (tr_peerMgr * manager)
{
managerLock (manager);
managerLock (manager);
deleteTimers (manager);
deleteTimers (manager);
/* free the handshakes. Abort invokes handshakeDoneCB (), which removes
* the item from manager->handshakes, so this is a little roundabout... */
while (!tr_ptrArrayEmpty (&manager->incomingHandshakes))
tr_handshakeAbort (tr_ptrArrayNth (&manager->incomingHandshakes, 0));
/* free the handshakes. Abort invokes handshakeDoneCB (), which removes
* the item from manager->handshakes, so this is a little roundabout... */
while (!tr_ptrArrayEmpty (&manager->incomingHandshakes))
tr_handshakeAbort (tr_ptrArrayNth (&manager->incomingHandshakes, 0));
tr_ptrArrayDestruct (&manager->incomingHandshakes, NULL);
tr_ptrArrayDestruct (&manager->incomingHandshakes, NULL);
managerUnlock (manager);
tr_free (manager);
managerUnlock (manager);
tr_free (manager);
}
static int
clientIsDownloadingFrom (const tr_torrent * tor, const tr_peer * peer)
{
if (!tr_torrentHasMetadata (tor))
return true;
if (!tr_torrentHasMetadata (tor))
return true;
return peer->clientIsInterested && !peer->clientIsChoked;
return peer->clientIsInterested && !peer->clientIsChoked;
}
static int
clientIsUploadingTo (const tr_peer * peer)
{
return peer->peerIsInterested && !peer->peerIsChoked;
return peer->peerIsInterested && !peer->peerIsChoked;
}
/***
@ -617,19 +618,20 @@ clientIsUploadingTo (const tr_peer * peer)
void
tr_peerMgrOnBlocklistChanged (tr_peerMgr * mgr)
{
tr_torrent * tor = NULL;
tr_session * session = mgr->session;
tr_torrent * tor = NULL;
tr_session * session = mgr->session;
/* we cache whether or not a peer is blocklisted...
since the blocklist has changed, erase that cached value */
while ((tor = tr_torrentNext (session, tor)))
/* we cache whether or not a peer is blocklisted...
since the blocklist has changed, erase that cached value */
while ((tor = tr_torrentNext (session, tor)))
{
int i;
Torrent * t = tor->torrentPeers;
const int n = tr_ptrArraySize (&t->pool);
for (i=0; i<n; ++i) {
struct peer_atom * atom = tr_ptrArrayNth (&t->pool, i);
atom->blocklisted = -1;
int i;
Torrent * t = tor->torrentPeers;
const int n = tr_ptrArraySize (&t->pool);
for (i=0; i<n; ++i)
{
struct peer_atom * atom = tr_ptrArrayNth (&t->pool, i);
atom->blocklisted = -1;
}
}
}
@ -637,11 +639,11 @@ tr_peerMgrOnBlocklistChanged (tr_peerMgr * mgr)
static bool
isAtomBlocklisted (tr_session * session, struct peer_atom * atom)
{
if (atom->blocklisted < 0)
atom->blocklisted = tr_sessionIsAddressBlocked (session, &atom->addr);
if (atom->blocklisted < 0)
atom->blocklisted = tr_sessionIsAddressBlocked (session, &atom->addr);
assert (tr_isBool (atom->blocklisted));
return atom->blocklisted;
assert (tr_isBool (atom->blocklisted));
return atom->blocklisted;
}
@ -652,31 +654,31 @@ isAtomBlocklisted (tr_session * session, struct peer_atom * atom)
static void
atomSetSeedProbability (struct peer_atom * atom, int seedProbability)
{
assert (atom != NULL);
assert (-1<=seedProbability && seedProbability<=100);
assert (atom != NULL);
assert (-1<=seedProbability && seedProbability<=100);
atom->seedProbability = seedProbability;
atom->seedProbability = seedProbability;
if (seedProbability == 100)
atom->flags |= ADDED_F_SEED_FLAG;
else if (seedProbability != -1)
atom->flags &= ~ADDED_F_SEED_FLAG;
if (seedProbability == 100)
atom->flags |= ADDED_F_SEED_FLAG;
else if (seedProbability != -1)
atom->flags &= ~ADDED_F_SEED_FLAG;
}
static inline bool
atomIsSeed (const struct peer_atom * atom)
{
return atom->seedProbability == 100;
return atom->seedProbability == 100;
}
static void
atomSetSeed (const Torrent * t, struct peer_atom * atom)
{
if (!atomIsSeed (atom))
if (!atomIsSeed (atom))
{
tordbg (t, "marking peer %s as a seed", tr_atomAddrStr (atom));
tordbg (t, "marking peer %s as a seed", tr_atomAddrStr (atom));
atomSetSeedProbability (atom, 100);
atomSetSeedProbability (atom, 100);
}
}
@ -685,32 +687,32 @@ bool
tr_peerMgrPeerIsSeed (const tr_torrent * tor,
const tr_address * addr)
{
bool isSeed = false;
const Torrent * t = tor->torrentPeers;
const struct peer_atom * atom = getExistingAtom (t, addr);
bool isSeed = false;
const Torrent * t = tor->torrentPeers;
const struct peer_atom * atom = getExistingAtom (t, addr);
if (atom)
isSeed = atomIsSeed (atom);
if (atom)
isSeed = atomIsSeed (atom);
return isSeed;
return isSeed;
}
void
tr_peerMgrSetUtpSupported (tr_torrent * tor, const tr_address * addr)
{
struct peer_atom * atom = getExistingAtom (tor->torrentPeers, addr);
struct peer_atom * atom = getExistingAtom (tor->torrentPeers, addr);
if (atom)
atom->flags |= ADDED_F_UTP_FLAGS;
if (atom)
atom->flags |= ADDED_F_UTP_FLAGS;
}
void
tr_peerMgrSetUtpFailed (tr_torrent *tor, const tr_address *addr, bool failed)
{
struct peer_atom * atom = getExistingAtom (tor->torrentPeers, addr);
struct peer_atom * atom = getExistingAtom (tor->torrentPeers, addr);
if (atom)
atom->utp_failed = failed;
if (atom)
atom->utp_failed = failed;
}

View File

@ -32,18 +32,18 @@ getKey (void) { return _("Port Forwarding"); }
struct tr_shared
{
bool isEnabled;
bool isShuttingDown;
bool doPortCheck;
bool isEnabled;
bool isShuttingDown;
bool doPortCheck;
tr_port_forwarding natpmpStatus;
tr_port_forwarding upnpStatus;
tr_port_forwarding natpmpStatus;
tr_port_forwarding upnpStatus;
tr_upnp * upnp;
tr_natpmp * natpmp;
tr_session * session;
tr_upnp * upnp;
tr_natpmp * natpmp;
tr_session * session;
struct event * timer;
struct event * timer;
};
/***
@ -53,90 +53,91 @@ struct tr_shared
static const char*
getNatStateStr (int state)
{
switch (state)
switch (state)
{
case TR_PORT_MAPPING: return _("Starting");
case TR_PORT_MAPPED: return _("Forwarded");
case TR_PORT_UNMAPPING: return _("Stopping");
case TR_PORT_UNMAPPED: return _("Not forwarded");
default: return "???";
case TR_PORT_MAPPING: return _("Starting");
case TR_PORT_MAPPED: return _("Forwarded");
case TR_PORT_UNMAPPING: return _("Stopping");
case TR_PORT_UNMAPPED: return _("Not forwarded");
default: return "???";
}
}
static void
natPulse (tr_shared * s, bool do_check)
{
const tr_port private_peer_port = s->session->private_peer_port;
const int is_enabled = s->isEnabled && !s->isShuttingDown;
tr_port public_peer_port;
int oldStatus;
int newStatus;
int oldStatus;
int newStatus;
tr_port public_peer_port;
const tr_port private_peer_port = s->session->private_peer_port;
const int is_enabled = s->isEnabled && !s->isShuttingDown;
if (s->natpmp == NULL)
s->natpmp = tr_natpmpInit ();
if (s->upnp == NULL)
s->upnp = tr_upnpInit ();
if (s->natpmp == NULL)
s->natpmp = tr_natpmpInit ();
oldStatus = tr_sharedTraversalStatus (s);
if (s->upnp == NULL)
s->upnp = tr_upnpInit ();
s->natpmpStatus = tr_natpmpPulse (s->natpmp, private_peer_port, is_enabled, &public_peer_port);
if (s->natpmpStatus == TR_PORT_MAPPED)
s->session->public_peer_port = public_peer_port;
oldStatus = tr_sharedTraversalStatus (s);
s->upnpStatus = tr_upnpPulse (s->upnp, private_peer_port, is_enabled, do_check);
s->natpmpStatus = tr_natpmpPulse (s->natpmp, private_peer_port, is_enabled, &public_peer_port);
if (s->natpmpStatus == TR_PORT_MAPPED)
s->session->public_peer_port = public_peer_port;
newStatus = tr_sharedTraversalStatus (s);
s->upnpStatus = tr_upnpPulse (s->upnp, private_peer_port, is_enabled, do_check);
if (newStatus != oldStatus)
tr_ninf (getKey (), _("State changed from \"%1$s\" to \"%2$s\""),
getNatStateStr (oldStatus),
getNatStateStr (newStatus));
newStatus = tr_sharedTraversalStatus (s);
if (newStatus != oldStatus)
tr_ninf (getKey (), _("State changed from \"%1$s\" to \"%2$s\""),
getNatStateStr (oldStatus),
getNatStateStr (newStatus));
}
static void
set_evtimer_from_status (tr_shared * s)
{
int sec=0, msec=0;
int sec=0, msec=0;
/* when to wake up again */
switch (tr_sharedTraversalStatus (s))
/* when to wake up again */
switch (tr_sharedTraversalStatus (s))
{
case TR_PORT_MAPPED:
/* if we're mapped, everything is fine... check back in 20 minutes
* to renew the port forwarding if it's expired */
s->doPortCheck = true;
sec = 60 * 20;
break;
case TR_PORT_MAPPED:
/* if we're mapped, everything is fine... check back in 20 minutes
* to renew the port forwarding if it's expired */
s->doPortCheck = true;
sec = 60 * 20;
break;
case TR_PORT_ERROR:
/* some kind of an error. wait 60 seconds and retry */
sec = 60;
break;
case TR_PORT_ERROR:
/* some kind of an error. wait 60 seconds and retry */
sec = 60;
break;
default:
/* in progress. pulse frequently. */
msec = 333000;
break;
default:
/* in progress. pulse frequently. */
msec = 333000;
break;
}
if (s->timer != NULL)
tr_timerAdd (s->timer, sec, msec);
if (s->timer != NULL)
tr_timerAdd (s->timer, sec, msec);
}
static void
onTimer (int fd UNUSED, short what UNUSED, void * vshared)
{
tr_shared * s = vshared;
tr_shared * s = vshared;
assert (s);
assert (s->timer);
assert (s);
assert (s->timer);
/* do something */
natPulse (s, s->doPortCheck);
s->doPortCheck = false;
/* do something */
natPulse (s, s->doPortCheck);
s->doPortCheck = false;
/* set up the timer for the next pulse */
set_evtimer_from_status (s);
/* set up the timer for the next pulse */
set_evtimer_from_status (s);
}
/***
@ -146,100 +147,100 @@ onTimer (int fd UNUSED, short what UNUSED, void * vshared)
tr_shared *
tr_sharedInit (tr_session * session)
{
tr_shared * s = tr_new0 (tr_shared, 1);
tr_shared * s = tr_new0 (tr_shared, 1);
s->session = session;
s->isEnabled = false;
s->upnpStatus = TR_PORT_UNMAPPED;
s->natpmpStatus = TR_PORT_UNMAPPED;
s->session = session;
s->isEnabled = false;
s->upnpStatus = TR_PORT_UNMAPPED;
s->natpmpStatus = TR_PORT_UNMAPPED;
#if 0
if (isEnabled)
if (isEnabled)
{
s->timer = tr_new0 (struct event, 1);
evtimer_set (s->timer, onTimer, s);
tr_timerAdd (s->timer, 0, 333000);
s->timer = tr_new0 (struct event, 1);
evtimer_set (s->timer, onTimer, s);
tr_timerAdd (s->timer, 0, 333000);
}
#endif
return s;
return s;
}
static void
stop_timer (tr_shared * s)
{
if (s->timer != NULL)
if (s->timer != NULL)
{
event_free (s->timer);
s->timer = NULL;
event_free (s->timer);
s->timer = NULL;
}
}
static void
stop_forwarding (tr_shared * s)
{
tr_ninf (getKey (), "%s", _("Stopped"));
natPulse (s, false);
tr_ninf (getKey (), "%s", _("Stopped"));
natPulse (s, false);
tr_natpmpClose (s->natpmp);
s->natpmp = NULL;
s->natpmpStatus = TR_PORT_UNMAPPED;
tr_natpmpClose (s->natpmp);
s->natpmp = NULL;
s->natpmpStatus = TR_PORT_UNMAPPED;
tr_upnpClose (s->upnp);
s->upnp = NULL;
s->upnpStatus = TR_PORT_UNMAPPED;
tr_upnpClose (s->upnp);
s->upnp = NULL;
s->upnpStatus = TR_PORT_UNMAPPED;
stop_timer (s);
stop_timer (s);
}
void
tr_sharedClose (tr_session * session)
{
tr_shared * s = session->shared;
tr_shared * s = session->shared;
s->isShuttingDown = true;
stop_forwarding (s);
s->session->shared = NULL;
tr_free (s);
s->isShuttingDown = true;
stop_forwarding (s);
s->session->shared = NULL;
tr_free (s);
}
static void
start_timer (tr_shared * s)
{
s->timer = evtimer_new (s->session->event_base, onTimer, s);
set_evtimer_from_status (s);
s->timer = evtimer_new (s->session->event_base, onTimer, s);
set_evtimer_from_status (s);
}
void
tr_sharedTraversalEnable (tr_shared * s, bool isEnabled)
{
if ((s->isEnabled = isEnabled))
start_timer (s);
else
stop_forwarding (s);
if ((s->isEnabled = isEnabled))
start_timer (s);
else
stop_forwarding (s);
}
void
tr_sharedPortChanged (tr_session * session)
{
tr_shared * s = session->shared;
tr_shared * s = session->shared;
if (s->isEnabled)
if (s->isEnabled)
{
stop_timer (s);
natPulse (s, false);
start_timer (s);
stop_timer (s);
natPulse (s, false);
start_timer (s);
}
}
bool
tr_sharedTraversalIsEnabled (const tr_shared * s)
{
return s->isEnabled;
return s->isEnabled;
}
int
tr_sharedTraversalStatus (const tr_shared * s)
{
return MAX (s->natpmpStatus, s->upnpStatus);
return MAX (s->natpmpStatus, s->upnpStatus);
}

File diff suppressed because it is too large Load Diff

View File

@ -38,21 +38,21 @@
enum
{
THREADFUNC_MAX_SLEEP_MSEC = 1000,
THREADFUNC_MAX_SLEEP_MSEC = 1000,
};
#if 0
#define dbgmsg(...) \
do { \
fprintf (stderr, __VA_ARGS__); \
fprintf (stderr, "\n"); \
} while (0)
do { \
fprintf (stderr, __VA_ARGS__); \
fprintf (stderr, "\n"); \
} while (0)
#else
#define dbgmsg(...) \
do { \
if (tr_deepLoggingIsActive ()) \
tr_deepLog (__FILE__, __LINE__, "web", __VA_ARGS__); \
} while (0)
do { \
if (tr_deepLoggingIsActive ()) \
tr_deepLog (__FILE__, __LINE__, "web", __VA_ARGS__); \
} while (0)
#endif
/***
@ -61,31 +61,31 @@ enum
struct tr_web_task
{
long code;
long timeout_secs;
bool did_connect;
bool did_timeout;
struct evbuffer * response;
struct evbuffer * freebuf;
char * url;
char * range;
char * cookies;
tr_session * session;
tr_web_done_func * done_func;
void * done_func_user_data;
CURL * curl_easy;
struct tr_web_task * next;
long code;
long timeout_secs;
bool did_connect;
bool did_timeout;
struct evbuffer * response;
struct evbuffer * freebuf;
char * url;
char * range;
char * cookies;
tr_session * session;
tr_web_done_func * done_func;
void * done_func_user_data;
CURL * curl_easy;
struct tr_web_task * next;
};
static void
task_free (struct tr_web_task * task)
{
if (task->freebuf)
evbuffer_free (task->freebuf);
tr_free (task->cookies);
tr_free (task->range);
tr_free (task->url);
tr_free (task);
if (task->freebuf)
evbuffer_free (task->freebuf);
tr_free (task->cookies);
tr_free (task->range);
tr_free (task->url);
tr_free (task);
}
/***
@ -94,13 +94,13 @@ task_free (struct tr_web_task * task)
struct tr_web
{
bool curl_verbose;
bool curl_ssl_verify;
const char * curl_ca_bundle;
int close_mode;
struct tr_web_task * tasks;
tr_lock * taskLock;
char * cookie_filename;
bool curl_verbose;
bool curl_ssl_verify;
const char * curl_ca_bundle;
int close_mode;
struct tr_web_task * tasks;
tr_lock * taskLock;
char * cookie_filename;
};
/***
@ -110,97 +110,101 @@ struct tr_web
static size_t
writeFunc (void * ptr, size_t size, size_t nmemb, void * vtask)
{
const size_t byteCount = size * nmemb;
struct tr_web_task * task = vtask;
evbuffer_add (task->response, ptr, byteCount);
dbgmsg ("wrote %zu bytes to task %p's buffer", byteCount, task);
return byteCount;
const size_t byteCount = size * nmemb;
struct tr_web_task * task = vtask;
evbuffer_add (task->response, ptr, byteCount);
dbgmsg ("wrote %zu bytes to task %p's buffer", byteCount, task);
return byteCount;
}
#ifdef USE_LIBCURL_SOCKOPT
static int
sockoptfunction (void * vtask, curl_socket_t fd, curlsocktype purpose UNUSED)
{
struct tr_web_task * task = vtask;
const bool isScrape = strstr (task->url, "scrape") != NULL;
const bool isAnnounce = strstr (task->url, "announce") != NULL;
struct tr_web_task * task = vtask;
const bool isScrape = strstr (task->url, "scrape") != NULL;
const bool isAnnounce = strstr (task->url, "announce") != NULL;
/* announce and scrape requests have tiny payloads. */
if (isScrape || isAnnounce)
/* announce and scrape requests have tiny payloads. */
if (isScrape || isAnnounce)
{
const int sndbuf = 1024;
const int rcvbuf = isScrape ? 2048 : 3072;
setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof (sndbuf));
setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof (rcvbuf));
const int sndbuf = 1024;
const int rcvbuf = isScrape ? 2048 : 3072;
setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof (sndbuf));
setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof (rcvbuf));
}
/* return nonzero if this function encountered an error */
return 0;
/* return nonzero if this function encountered an error */
return 0;
}
#endif
static long
getTimeoutFromURL (const struct tr_web_task * task)
{
long timeout;
const tr_session * session = task->session;
long timeout;
const tr_session * session = task->session;
if (!session || session->isClosed) timeout = 20L;
else if (strstr (task->url, "scrape") != NULL) timeout = 30L;
else if (strstr (task->url, "announce") != NULL) timeout = 90L;
else timeout = 240L;
if (!session || session->isClosed) timeout = 20L;
else if (strstr (task->url, "scrape") != NULL) timeout = 30L;
else if (strstr (task->url, "announce") != NULL) timeout = 90L;
else timeout = 240L;
return timeout;
return timeout;
}
static CURL *
createEasy (tr_session * s, struct tr_web * web, struct tr_web_task * task)
{
bool is_default_value;
const tr_address * addr;
CURL * e = task->curl_easy = curl_easy_init ();
bool is_default_value;
const tr_address * addr;
CURL * e = task->curl_easy = curl_easy_init ();
task->timeout_secs = getTimeoutFromURL (task);
task->timeout_secs = getTimeoutFromURL (task);
curl_easy_setopt (e, CURLOPT_AUTOREFERER, 1L);
curl_easy_setopt (e, CURLOPT_COOKIEFILE, web->cookie_filename);
curl_easy_setopt (e, CURLOPT_ENCODING, "gzip;q=1.0, deflate, identity");
curl_easy_setopt (e, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt (e, CURLOPT_MAXREDIRS, -1L);
curl_easy_setopt (e, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt (e, CURLOPT_PRIVATE, task);
curl_easy_setopt (e, CURLOPT_AUTOREFERER, 1L);
curl_easy_setopt (e, CURLOPT_COOKIEFILE, web->cookie_filename);
curl_easy_setopt (e, CURLOPT_ENCODING, "gzip;q=1.0, deflate, identity");
curl_easy_setopt (e, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt (e, CURLOPT_MAXREDIRS, -1L);
curl_easy_setopt (e, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt (e, CURLOPT_PRIVATE, task);
#ifdef USE_LIBCURL_SOCKOPT
curl_easy_setopt (e, CURLOPT_SOCKOPTFUNCTION, sockoptfunction);
curl_easy_setopt (e, CURLOPT_SOCKOPTDATA, task);
curl_easy_setopt (e, CURLOPT_SOCKOPTFUNCTION, sockoptfunction);
curl_easy_setopt (e, CURLOPT_SOCKOPTDATA, task);
#endif
if (web->curl_ssl_verify)
curl_easy_setopt (e, CURLOPT_CAINFO, web->curl_ca_bundle);
else {
curl_easy_setopt (e, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt (e, CURLOPT_SSL_VERIFYPEER, 0L);
if (web->curl_ssl_verify)
{
curl_easy_setopt (e, CURLOPT_CAINFO, web->curl_ca_bundle);
}
curl_easy_setopt (e, CURLOPT_TIMEOUT, task->timeout_secs);
curl_easy_setopt (e, CURLOPT_URL, task->url);
curl_easy_setopt (e, CURLOPT_USERAGENT, TR_NAME "/" SHORT_VERSION_STRING);
curl_easy_setopt (e, CURLOPT_VERBOSE, (long)(web->curl_verbose?1:0));
curl_easy_setopt (e, CURLOPT_WRITEDATA, task);
curl_easy_setopt (e, CURLOPT_WRITEFUNCTION, writeFunc);
else
{
curl_easy_setopt (e, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt (e, CURLOPT_SSL_VERIFYPEER, 0L);
}
curl_easy_setopt (e, CURLOPT_TIMEOUT, task->timeout_secs);
curl_easy_setopt (e, CURLOPT_URL, task->url);
curl_easy_setopt (e, CURLOPT_USERAGENT, TR_NAME "/" SHORT_VERSION_STRING);
curl_easy_setopt (e, CURLOPT_VERBOSE, (long)(web->curl_verbose?1:0));
curl_easy_setopt (e, CURLOPT_WRITEDATA, task);
curl_easy_setopt (e, CURLOPT_WRITEFUNCTION, writeFunc);
if (((addr = tr_sessionGetPublicAddress (s, TR_AF_INET, &is_default_value))) && !is_default_value)
curl_easy_setopt (e, CURLOPT_INTERFACE, tr_address_to_string (addr));
else if (((addr = tr_sessionGetPublicAddress (s, TR_AF_INET6, &is_default_value))) && !is_default_value)
curl_easy_setopt (e, CURLOPT_INTERFACE, tr_address_to_string (addr));
if (((addr = tr_sessionGetPublicAddress (s, TR_AF_INET, &is_default_value))) && !is_default_value)
curl_easy_setopt (e, CURLOPT_INTERFACE, tr_address_to_string (addr));
else if (((addr = tr_sessionGetPublicAddress (s, TR_AF_INET6, &is_default_value))) && !is_default_value)
curl_easy_setopt (e, CURLOPT_INTERFACE, tr_address_to_string (addr));
if (task->cookies != NULL)
curl_easy_setopt (e, CURLOPT_COOKIE, task->cookies);
if (task->cookies != NULL)
curl_easy_setopt (e, CURLOPT_COOKIE, task->cookies);
if (task->range != NULL) {
curl_easy_setopt (e, CURLOPT_RANGE, task->range);
/* don't bother asking the server to compress webseed fragments */
curl_easy_setopt (e, CURLOPT_ENCODING, "identity");
if (task->range != NULL)
{
curl_easy_setopt (e, CURLOPT_RANGE, task->range);
/* don't bother asking the server to compress webseed fragments */
curl_easy_setopt (e, CURLOPT_ENCODING, "identity");
}
return e;
return e;
}
/***
@ -210,19 +214,19 @@ createEasy (tr_session * s, struct tr_web * web, struct tr_web_task * task)
static void
task_finish_func (void * vtask)
{
struct tr_web_task * task = vtask;
dbgmsg ("finished web task %p; got %ld", task, task->code);
struct tr_web_task * task = vtask;
dbgmsg ("finished web task %p; got %ld", task, task->code);
if (task->done_func != NULL)
task->done_func (task->session,
task->did_connect,
task->did_timeout,
task->code,
evbuffer_pullup (task->response, -1),
evbuffer_get_length (task->response),
task->done_func_user_data);
if (task->done_func != NULL)
task->done_func (task->session,
task->did_connect,
task->did_timeout,
task->code,
evbuffer_pullup (task->response, -1),
evbuffer_get_length (task->response),
task->done_func_user_data);
task_free (task);
task_free (task);
}
/****
@ -237,9 +241,9 @@ tr_webRun (tr_session * session,
tr_web_done_func done_func,
void * done_func_user_data)
{
return tr_webRunWithBuffer (session, url, range, cookies,
done_func, done_func_user_data,
NULL);
return tr_webRunWithBuffer (session, url, range, cookies,
done_func, done_func_user_data,
NULL);
}
struct tr_web_task *
@ -251,28 +255,29 @@ tr_webRunWithBuffer (tr_session * session,
void * done_func_user_data,
struct evbuffer * buffer)
{
struct tr_web * web = session->web;
struct tr_web * web = session->web;
if (web != NULL)
if (web != NULL)
{
struct tr_web_task * task = tr_new0 (struct tr_web_task, 1);
struct tr_web_task * task = tr_new0 (struct tr_web_task, 1);
task->session = session;
task->url = tr_strdup (url);
task->range = tr_strdup (range);
task->cookies = tr_strdup (cookies);
task->done_func = done_func;
task->done_func_user_data = done_func_user_data;
task->response = buffer ? buffer : evbuffer_new ();
task->freebuf = buffer ? NULL : task->response;
task->session = session;
task->url = tr_strdup (url);
task->range = tr_strdup (range);
task->cookies = tr_strdup (cookies);
task->done_func = done_func;
task->done_func_user_data = done_func_user_data;
task->response = buffer ? buffer : evbuffer_new ();
task->freebuf = buffer ? NULL : task->response;
tr_lockLock (web->taskLock);
task->next = web->tasks;
web->tasks = task;
tr_lockUnlock (web->taskLock);
return task;
tr_lockLock (web->taskLock);
task->next = web->tasks;
web->tasks = task;
tr_lockUnlock (web->taskLock);
return task;
}
return NULL;
return NULL;
}
/**
@ -289,183 +294,184 @@ tr_select (int nfds,
struct timeval * t)
{
#ifdef WIN32
if (!r_fd_set->fd_count && !w_fd_set->fd_count && !c_fd_set->fd_count)
if (!r_fd_set->fd_count && !w_fd_set->fd_count && !c_fd_set->fd_count)
{
const long int msec = t->tv_sec*1000 + t->tv_usec/1000;
tr_wait_msec (msec);
const long int msec = t->tv_sec*1000 + t->tv_usec/1000;
tr_wait_msec (msec);
}
else if (select (0, r_fd_set->fd_count ? r_fd_set : NULL,
w_fd_set->fd_count ? w_fd_set : NULL,
c_fd_set->fd_count ? c_fd_set : NULL, t) < 0)
else if (select (0, r_fd_set->fd_count ? r_fd_set : NULL,
w_fd_set->fd_count ? w_fd_set : NULL,
c_fd_set->fd_count ? c_fd_set : NULL, t) < 0)
{
char errstr[512];
const int e = EVUTIL_SOCKET_ERROR ();
tr_net_strerror (errstr, sizeof (errstr), e);
dbgmsg ("Error: select (%d) %s", e, errstr);
char errstr[512];
const int e = EVUTIL_SOCKET_ERROR ();
tr_net_strerror (errstr, sizeof (errstr), e);
dbgmsg ("Error: select (%d) %s", e, errstr);
}
#else
select (nfds, r_fd_set, w_fd_set, c_fd_set, t);
select (nfds, r_fd_set, w_fd_set, c_fd_set, t);
#endif
}
static void
tr_webThreadFunc (void * vsession)
{
CURLM * multi;
struct tr_web * web;
int taskCount = 0;
struct tr_web_task * task;
tr_session * session = vsession;
CURLM * multi;
struct tr_web * web;
int taskCount = 0;
struct tr_web_task * task;
tr_session * session = vsession;
/* try to enable ssl for https support; but if that fails,
* try a plain vanilla init */
if (curl_global_init (CURL_GLOBAL_SSL))
curl_global_init (0);
/* try to enable ssl for https support; but if that fails,
* try a plain vanilla init */
if (curl_global_init (CURL_GLOBAL_SSL))
curl_global_init (0);
web = tr_new0 (struct tr_web, 1);
web->close_mode = ~0;
web->taskLock = tr_lockNew ();
web->tasks = NULL;
web->curl_verbose = getenv ("TR_CURL_VERBOSE") != NULL;
web->curl_ssl_verify = getenv ("TR_CURL_SSL_VERIFY") != NULL;
web->curl_ca_bundle = getenv ("CURL_CA_BUNDLE");
if (web->curl_ssl_verify) {
tr_ninf ("web", "will verify tracker certs using envvar CURL_CA_BUNDLE: %s",
web->curl_ca_bundle == NULL ? "none" : web->curl_ca_bundle);
tr_ninf ("web", "NB: this only works if you built against libcurl with openssl or gnutls, NOT nss");
tr_ninf ("web", "NB: invalid certs will show up as 'Could not connect to tracker' like many other errors");
}
web->cookie_filename = tr_buildPath (session->configDir, "cookies.txt", NULL);
multi = curl_multi_init ();
session->web = web;
for (;;)
web = tr_new0 (struct tr_web, 1);
web->close_mode = ~0;
web->taskLock = tr_lockNew ();
web->tasks = NULL;
web->curl_verbose = getenv ("TR_CURL_VERBOSE") != NULL;
web->curl_ssl_verify = getenv ("TR_CURL_SSL_VERIFY") != NULL;
web->curl_ca_bundle = getenv ("CURL_CA_BUNDLE");
if (web->curl_ssl_verify)
{
long msec;
int unused;
CURLMsg * msg;
CURLMcode mcode;
tr_ninf ("web", "will verify tracker certs using envvar CURL_CA_BUNDLE: %s",
web->curl_ca_bundle == NULL ? "none" : web->curl_ca_bundle);
tr_ninf ("web", "NB: this only works if you built against libcurl with openssl or gnutls, NOT nss");
tr_ninf ("web", "NB: invalid certs will show up as 'Could not connect to tracker' like many other errors");
}
web->cookie_filename = tr_buildPath (session->configDir, "cookies.txt", NULL);
if (web->close_mode == TR_WEB_CLOSE_NOW)
break;
if ((web->close_mode == TR_WEB_CLOSE_WHEN_IDLE) && (web->tasks == NULL))
break;
multi = curl_multi_init ();
session->web = web;
/* add tasks from the queue */
tr_lockLock (web->taskLock);
while (web->tasks != NULL)
for (;;)
{
long msec;
int unused;
CURLMsg * msg;
CURLMcode mcode;
if (web->close_mode == TR_WEB_CLOSE_NOW)
break;
if ((web->close_mode == TR_WEB_CLOSE_WHEN_IDLE) && (web->tasks == NULL))
break;
/* add tasks from the queue */
tr_lockLock (web->taskLock);
while (web->tasks != NULL)
{
/* pop the task */
task = web->tasks;
web->tasks = task->next;
task->next = NULL;
/* pop the task */
task = web->tasks;
web->tasks = task->next;
task->next = NULL;
dbgmsg ("adding task to curl: [%s]", task->url);
curl_multi_add_handle (multi, createEasy (session, web, task));
/*fprintf (stderr, "adding a task.. taskCount is now %d\n", taskCount);*/
++taskCount;
dbgmsg ("adding task to curl: [%s]", task->url);
curl_multi_add_handle (multi, createEasy (session, web, task));
/*fprintf (stderr, "adding a task.. taskCount is now %d\n", taskCount);*/
++taskCount;
}
tr_lockUnlock (web->taskLock);
tr_lockUnlock (web->taskLock);
/* maybe wait a little while before calling curl_multi_perform () */
msec = 0;
curl_multi_timeout (multi, &msec);
if (msec < 0)
/* maybe wait a little while before calling curl_multi_perform () */
msec = 0;
curl_multi_timeout (multi, &msec);
if (msec < 0)
msec = THREADFUNC_MAX_SLEEP_MSEC;
if (session->isClosed)
msec = 100; /* on shutdown, call perform () more frequently */
if (msec > 0)
{
int usec;
int max_fd;
struct timeval t;
fd_set r_fd_set, w_fd_set, c_fd_set;
max_fd = 0;
FD_ZERO (&r_fd_set);
FD_ZERO (&w_fd_set);
FD_ZERO (&c_fd_set);
curl_multi_fdset (multi, &r_fd_set, &w_fd_set, &c_fd_set, &max_fd);
if (msec > THREADFUNC_MAX_SLEEP_MSEC)
msec = THREADFUNC_MAX_SLEEP_MSEC;
if (session->isClosed)
msec = 100; /* on shutdown, call perform () more frequently */
if (msec > 0)
{
int usec;
int max_fd;
struct timeval t;
fd_set r_fd_set, w_fd_set, c_fd_set;
max_fd = 0;
FD_ZERO (&r_fd_set);
FD_ZERO (&w_fd_set);
FD_ZERO (&c_fd_set);
curl_multi_fdset (multi, &r_fd_set, &w_fd_set, &c_fd_set, &max_fd);
if (msec > THREADFUNC_MAX_SLEEP_MSEC)
msec = THREADFUNC_MAX_SLEEP_MSEC;
usec = msec * 1000;
t.tv_sec = usec / 1000000;
t.tv_usec = usec % 1000000;
tr_select (max_fd+1, &r_fd_set, &w_fd_set, &c_fd_set, &t);
usec = msec * 1000;
t.tv_sec = usec / 1000000;
t.tv_usec = usec % 1000000;
tr_select (max_fd+1, &r_fd_set, &w_fd_set, &c_fd_set, &t);
}
/* call curl_multi_perform () */
do {
mcode = curl_multi_perform (multi, &unused);
} while (mcode == CURLM_CALL_MULTI_PERFORM);
/* call curl_multi_perform () */
do
mcode = curl_multi_perform (multi, &unused);
while (mcode == CURLM_CALL_MULTI_PERFORM);
/* pump completed tasks from the multi */
while ((msg = curl_multi_info_read (multi, &unused)))
/* pump completed tasks from the multi */
while ((msg = curl_multi_info_read (multi, &unused)))
{
if ((msg->msg == CURLMSG_DONE) && (msg->easy_handle != NULL))
if ((msg->msg == CURLMSG_DONE) && (msg->easy_handle != NULL))
{
double total_time;
struct tr_web_task * task;
long req_bytes_sent;
CURL * e = msg->easy_handle;
curl_easy_getinfo (e, CURLINFO_PRIVATE, (void*)&task);
curl_easy_getinfo (e, CURLINFO_RESPONSE_CODE, &task->code);
curl_easy_getinfo (e, CURLINFO_REQUEST_SIZE, &req_bytes_sent);
curl_easy_getinfo (e, CURLINFO_TOTAL_TIME, &total_time);
task->did_connect = task->code>0 || req_bytes_sent>0;
task->did_timeout = !task->code && (total_time >= task->timeout_secs);
curl_multi_remove_handle (multi, e);
curl_easy_cleanup (e);
/*fprintf (stderr, "removing a completed task.. taskCount is now %d (response code: %d, response len: %d)\n", taskCount, (int)task->code, (int)evbuffer_get_length (task->response));*/
tr_runInEventThread (task->session, task_finish_func, task);
--taskCount;
double total_time;
struct tr_web_task * task;
long req_bytes_sent;
CURL * e = msg->easy_handle;
curl_easy_getinfo (e, CURLINFO_PRIVATE, (void*)&task);
curl_easy_getinfo (e, CURLINFO_RESPONSE_CODE, &task->code);
curl_easy_getinfo (e, CURLINFO_REQUEST_SIZE, &req_bytes_sent);
curl_easy_getinfo (e, CURLINFO_TOTAL_TIME, &total_time);
task->did_connect = task->code>0 || req_bytes_sent>0;
task->did_timeout = !task->code && (total_time >= task->timeout_secs);
curl_multi_remove_handle (multi, e);
curl_easy_cleanup (e);
tr_runInEventThread (task->session, task_finish_func, task);
--taskCount;
}
}
}
/* Discard any remaining tasks.
* This is rare, but can happen on shutdown with unresponsive trackers. */
while (web->tasks != NULL) {
task = web->tasks;
web->tasks = task->next;
dbgmsg ("Discarding task \"%s\"", task->url);
task_free (task);
/* Discard any remaining tasks.
* This is rare, but can happen on shutdown with unresponsive trackers. */
while (web->tasks != NULL)
{
task = web->tasks;
web->tasks = task->next;
dbgmsg ("Discarding task \"%s\"", task->url);
task_free (task);
}
/* cleanup */
curl_multi_cleanup (multi);
tr_lockFree (web->taskLock);
tr_free (web->cookie_filename);
tr_free (web);
session->web = NULL;
/* cleanup */
curl_multi_cleanup (multi);
tr_lockFree (web->taskLock);
tr_free (web->cookie_filename);
tr_free (web);
session->web = NULL;
}
void
tr_webInit (tr_session * session)
{
tr_threadNew (tr_webThreadFunc, session);
tr_threadNew (tr_webThreadFunc, session);
}
void
tr_webClose (tr_session * session, tr_web_close_mode close_mode)
{
if (session->web != NULL)
if (session->web != NULL)
{
session->web->close_mode = close_mode;
session->web->close_mode = close_mode;
if (close_mode == TR_WEB_CLOSE_NOW)
while (session->web != NULL)
tr_wait_msec (100);
if (close_mode == TR_WEB_CLOSE_NOW)
while (session->web != NULL)
tr_wait_msec (100);
}
}
void
tr_webGetTaskInfo (struct tr_web_task * task, tr_web_task_info info, void * dst)
{
curl_easy_getinfo (task->curl_easy, (CURLINFO) info, dst);
curl_easy_getinfo (task->curl_easy, (CURLINFO) info, dst);
}
/*****
@ -476,108 +482,110 @@ tr_webGetTaskInfo (struct tr_web_task * task, tr_web_task_info info, void * dst)
const char *
tr_webGetResponseStr (long code)
{
switch (code)
switch (code)
{
case 0: return "No Response";
case 101: return "Switching Protocols";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-Authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
case 306: return " (Unused)";
case 307: return "Temporary Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Request Entity Too Large";
case 414: return "Request-URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
case 417: return "Expectation Failed";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "HTTP Version Not Supported";
default: return "Unknown Error";
case 0: return "No Response";
case 101: return "Switching Protocols";
case 200: return "OK";
case 201: return "Created";
case 202: return "Accepted";
case 203: return "Non-Authoritative Information";
case 204: return "No Content";
case 205: return "Reset Content";
case 206: return "Partial Content";
case 300: return "Multiple Choices";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
case 305: return "Use Proxy";
case 306: return " (Unused)";
case 307: return "Temporary Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
case 405: return "Method Not Allowed";
case 406: return "Not Acceptable";
case 407: return "Proxy Authentication Required";
case 408: return "Request Timeout";
case 409: return "Conflict";
case 410: return "Gone";
case 411: return "Length Required";
case 412: return "Precondition Failed";
case 413: return "Request Entity Too Large";
case 414: return "Request-URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Requested Range Not Satisfiable";
case 417: return "Expectation Failed";
case 500: return "Internal Server Error";
case 501: return "Not Implemented";
case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
case 504: return "Gateway Timeout";
case 505: return "HTTP Version Not Supported";
default: return "Unknown Error";
}
}
void
tr_http_escape (struct evbuffer * out,
const char * str, int len, bool escape_slashes)
const char * str,
int len,
bool escape_slashes)
{
const char * end;
const char * end;
if ((len < 0) && (str != NULL))
len = strlen (str);
if ((len < 0) && (str != NULL))
len = strlen (str);
for (end=str+len; str && str!=end; ++str) {
if ((*str == ',')
|| (*str == '-')
|| (*str == '.')
|| (('0' <= *str) && (*str <= '9'))
|| (('A' <= *str) && (*str <= 'Z'))
|| (('a' <= *str) && (*str <= 'z'))
|| ((*str == '/') && (!escape_slashes)))
evbuffer_add_printf (out, "%c", *str);
else
evbuffer_add_printf (out, "%%%02X", (unsigned)(*str&0xFF));
for (end=str+len; str && str!=end; ++str)
{
if ((*str == ',') || (*str == '-')
|| (*str == '.')
|| (('0' <= *str) && (*str <= '9'))
|| (('A' <= *str) && (*str <= 'Z'))
|| (('a' <= *str) && (*str <= 'z'))
|| ((*str == '/') && (!escape_slashes)))
evbuffer_add_printf (out, "%c", *str);
else
evbuffer_add_printf (out, "%%%02X", (unsigned)(*str&0xFF));
}
}
char *
tr_http_unescape (const char * str, int len)
{
char * tmp = curl_unescape (str, len);
char * ret = tr_strdup (tmp);
curl_free (tmp);
return ret;
char * tmp = curl_unescape (str, len);
char * ret = tr_strdup (tmp);
curl_free (tmp);
return ret;
}
static int
is_rfc2396_alnum (uint8_t ch)
{
return ('0' <= ch && ch <= '9')
|| ('A' <= ch && ch <= 'Z')
|| ('a' <= ch && ch <= 'z')
|| ch == '.'
|| ch == '-'
|| ch == '_'
|| ch == '~';
return ('0' <= ch && ch <= '9')
|| ('A' <= ch && ch <= 'Z')
|| ('a' <= ch && ch <= 'z')
|| ch == '.'
|| ch == '-'
|| ch == '_'
|| ch == '~';
}
void
tr_http_escape_sha1 (char * out, const uint8_t * sha1_digest)
{
const uint8_t * in = sha1_digest;
const uint8_t * end = in + SHA_DIGEST_LENGTH;
const uint8_t * in = sha1_digest;
const uint8_t * end = in + SHA_DIGEST_LENGTH;
while (in != end)
if (is_rfc2396_alnum (*in))
*out++ = (char) *in++;
else
out += tr_snprintf (out, 4, "%%%02x", (unsigned int)*in++);
while (in != end)
if (is_rfc2396_alnum (*in))
*out++ = (char) *in++;
else
out += tr_snprintf (out, 4, "%%%02x", (unsigned int)*in++);
*out = '\0';
*out = '\0';
}