(libT) copyediting: modify more files to the new indentation/whitespace formatting
This commit is contained in:
parent
1c86069e7b
commit
ad3407567b
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
@ -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';
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue