Git Product home page Git Product logo

dht's Introduction

The files dht.c and dht.h implement the variant of the Kademlia Distributed
Hash Table (DHT) used in the Bittorrent network (``mainline'' variant).

The file dht-example.c is a stand-alone program that participates in the
DHT.  Another example is a patch against Transmission, which you might or
might not be able to find somewhere.

The code is designed to work well in both event-driven and threaded code.
The caller, which is either an event-loop or a dedicated thread, must
periodically call the function dht_periodic.  In addition, it must call
dht_periodic whenever any data has arrived from the network.

All functions return -1 in case of failure, in which case errno is set, or
a positive value in case of success.

Initialisation
**************

* dht_init

This must be called before using the library.  You pass it an integer
identifying a bound IPv4 datagram socket in non-blocking mode, an integer
identifying a bound IPv6 datagram socket in non-blocking mode, and your
node id, a 20-octet array that should be globally unique.

The integers that identify the two sockets should usually be file
descriptors; however, as the library does not directly perform any socket-
or file-related operations on them, they can be arbitrary integers, for
example indices in a table of structures that represent sockets in your
code.

If you're on a multi-homed host, you should bind your sockets to one of
your addresses.  This is especially relevant for IPv6.

Node ids must be well distributed, so you cannot just use your Bittorrent
id; you should either generate a truly random value (using plenty of
entropy), or at least take the SHA-1 of something.  However, it is a good
idea to keep the id stable, so you may want to store it in stable storage
at client shutdown.

 
* dht_uninit

This may be called at the end of the session.

Bootstrapping
*************

The DHT needs to be taught a small number of contacts to begin functioning.
You can hard-wire a small number of stable nodes in your application, but
this obviously fails to scale.  You may save the list of known good nodes
at shutdown, and restore it at restart.  You may also grab nodes from
torrent files (the nodes field), and you may exchange contacts with other
Bittorrent peers using the PORT extension.

* dht_ping_node

This is the main bootstrapping primitive.  You pass it an address at which
you believe that a DHT node may be living, and a query will be sent.  If
a node replies, and if there is space in the routing table, it will be
inserted.

* dht_insert_node

This is a softer bootstrapping method, which doesn't actually send
a query -- it only stores the node in the routing table for later use.

Note that dht_insert_node requires that you supply a node id.  If the id
turns out to be wrong, the DHT will eventually recover; still, inserting
massive amounts of incorrect information into your routing table is not
a good idea.

An additionaly difficulty with dht_insert_node is that a Kademlia routing
table cannot absorb nodes faster than a certain rate.  A freshly initialised
routing table is able to absorb 128 nodes of each address family without
dropping any.  The tolerable rate after that is difficult to estimate: it is
probably on the order of one node every few seconds per node already in
the table divided by 8, for some suitable value of 8.

Doing some work
***************

* dht_periodic

This function should be called by your main loop periodically, and also
whenever data is available on the socket.  The time after which
dht_periodic should be called if no data is available is returned in the
parameter tosleep.  (You do not need to be particularly accurate; actually,
it is a good idea to be late by a random value.)

The parameters buf, buflen, from and fromlen optionally carry a received
message.  If buflen is 0, then no message was received.

Dht_periodic also takes a callback, which will be called whenever something
interesting happens (see below).

* dht_search

This schedules a search for information about the info-hash specified in
id; it returns 1 if this is a new search, and 0 if it merely reset the
timeouts for a search in progress.  If port is not 0, it specifies the TCP
port on which the current peer is listening; in that case, when the search
is complete it will be announced to the network.  The port is in host
order, beware if you got it from a struct sockaddr_in.

In either case, data is passed to the callback function as soon as it is
available, possibly in multiple pieces.  The callback function will also
be called when the search is complete.

Up to DHT_MAX_SEARCHES (1024) searches can be in progress at a given time;
any more, and dht_search will return -1.  If you specify a new search for
the same info hash as a search still in progress, the previous search is
combined with the new one -- you will only receive a completion indication
once.

Information queries
*******************

* dht_nodes

This returns the number of known good, dubious and cached nodes in our
routing table.  This can be used to decide whether it's reasonable to start
a search; a search is likely to be successful as long as we have a few good
nodes; however, in order to avoid overloading your bootstrap nodes, you may
want to wait until good is at least 4 and good + doubtful is at least 30 or
so.

It also includes the number of nodes that recently sent us an unsolicited
request; this can be used to determine if the UDP port used for the DHT is
firewalled.

If you want to display a single figure to the user, you should display
good + doubtful, which is the total number of nodes in your routing table.
Some clients try to estimate the total number of nodes, but this doesn't
make much sense -- since the result is exponential in the number of nodes
in the routing table, small variations in the latter cause huge jumps in
the former.

* dht_get_nodes

This retrieves the list of known good nodes, starting with the nodes in our
own bucket.  It is a good idea to save the list of known good nodes at
shutdown, and ping them at startup.

* dht_dump_tables
* dht_debug

These are debugging aids.

Functions provided by you
*************************

* The callback function

The callback function is called with 5 arguments.  Closure is simply the
value that you passed to dht_periodic.  Event is one of DHT_EVENT_VALUES or
DHT_EVENT_VALUES6, which indicates that we have new values, or
DHT_EVENT_SEARCH_DONE or DHT_EVENT_SEARCH_DONE6, which indicates that
a search has completed.  In either case, info_hash is set to the info-hash
of the search.

In the case of DHT_EVENT_VALUES, data is a list of nodes in ``compact''
format -- 6 or 18 bytes per node.  Its length in bytes is in data_len.

* dht_sendto

This will be called whenever the library needs to send a datagram.  If the
integers passed to dht_init are file descriptors, this can simply be an
alias for the sendto system call.

* dht_blacklisted

This is a function that takes an IP address and returns true if this
address should be silently ignored.  Do not use this feature unless you
really must -- Kademlia supposes transitive reachability.

* dht_hash

This should compute a reasonably strong cryptographic hash of the passed
values.  SHA-1 should be good enough.

* dht_random_bytes

This should fill the supplied buffer with cryptographically strong random
bytes.  It's called every 30 minutes on average, so it doesn't need to be
fast.

Final notes
***********

* NAT

Nothing works well across NATs, but Kademlia is somewhat less impacted than
many other protocols.  The implementation takes care to distinguish between
unidirectional and bidirectional reachability, and NATed nodes will
eventually fall out from other nodes' routing tables.

While there is no periodic pinging in this implementation, maintaining
a full routing table requires slightly more than one packet exchange per
minute, even in a completely idle network; this should be sufficient to
make most full cone NATs happy.


                                        Juliusz Chroboczek
                                        <[email protected]>

dht's People

Contributors

coeur avatar erkan-yilmaz avatar falsecam avatar fonic avatar gamepad64 avatar ghazel avatar jech avatar joel-su avatar mikedld avatar raspopov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dht's Issues

find_closest

regards code of buffer_closest_nodes, a trivial way is to scan the bucket and its adjacent buckets and find the 8 closest node , what's your consideration

Storing values

I didn't see a method for storing/publishing values. Is this correct?

How difficult would it be to implement such a function. I am willing to do it if it's not to complicated.

Bug Report

Bug: struct bucket.time is defined as int - should be time_t

free(buckets6)

dht.c 1668

if(s >= 0) {
    buckets = calloc(sizeof(struct bucket), 1);
    if(buckets == NULL)
        return -1;
    buckets->af = AF_INET;

    rc = set_nonblocking(s, 1);
    if(rc < 0)
        goto fail;
}

if(s6 >= 0) {
    buckets6 = calloc(sizeof(struct bucket), 1);
    if(buckets6 == NULL)
        return -1;
    buckets6->af = AF_INET6;

    rc = set_nonblocking(s6, 1);
    if(rc < 0)
        goto fail;
}

...

 fail:
    free(buckets);
    buckets = NULL;
    free(buckets6);
    buckets6 = NULL;
    return -1;

what happens if _set_nonblocking(s, 1)_ returned an error
before we might _buckets6 = calloc_ (which comes after)
_fail:_ free(buckets6) ?

Or maybe the opposite
no v4 socket at all and _set_nonblocking(s6, 1)_ returns error
_fail:_ free(buckets) ?

maybe I'm not seeing this right...

BEP-44 support?

Would be nice if the library have support to store arbitrary values,
so other decentralized application could integrate with the Bittorrent DHT more easily.

Have found this was asked before (in issue #9 ).
But, now there is a BEP related (http://bittorrent.org/beps/bep_0044.html).

I think other libraries and programs (like libtorrent) has implemented it.
But i have not found a DHT specific library (in c/c++) with it.

Thanks

Send a version string even if the client does not provide one

BEP 5 has been updated to document the long standing de-facto standard of including a version string in RPC messages. This is an important feature for identifying implementations which may be lacking features or misbehaving. The relevant section of BEP5:

A key v should be included in every message with a client version string. The string should be a two character client identifier registered in BEP 20 followed by a two character version identifier.

By convention the version string identifies the DHT implementation rather than the client application. Thus this library should have it's own client identifier which it uses to populate the version string. Ideally it would always use this string regardless of what the client application's identifier is.

making libdht

I came across this working on the Debian integration of transmission. There's the option of using a system DHT library instead of bundling it, but if I see correctly, dht can't build as a library yet. I don't know why not, given that dht is so useful.

I could create a PR that add that, if you'd like.

Is this project dead ?

I've been monitoring activity on this project for a while and many pull requests and issues have been left unanswered for a while.

Include client version string in messages

Messages should include a "v" key with a 4-byte identifier value that contains the DHT node's 2-byte client identifier and 2-byte version identifier. This helps immensely in tracking down bugs in other DHT implementations, as you can get an idea of which implementation you are or aren't looking for.

See BEP 5 for details about the client version string.

"implied_port" is improperly handled

If a message includes the "implied_port" argument, the message parser finds the "port" portion of the "implied_port" argument key and uses its value as the port, which is always 0 or 1.

See BEP 5 for proper handling of the "implied_port" argument.

API improvements around DHT_EVENT_SEARCH_DONE notification

Tracking when a search completes is somewhat difficult at the moment. If the user allocates memory for a search, calls dht_search(), and then deallocates when DHT_EVENT_SEARCH_DONE arrives, memory can leak. The user can try to keep track of in-progress searches, and also its own timeout values, but this is is extra effort and slightly inaccurate.

There are a few small things I think could be improved about the API which should be backwards compatible:

  • if dht_search finds an existing struct search, it could return a different value (2?) so the caller knows not to expect an additional DHT_EVENT_SEARCH_DONE event
  • when a struct search expires, callback with DHT_EVENT_SEARCH_DONE(6) before freeing

I'd be happy to submit a pull request if these are welcome changes.

Redundant check introduced in 71d4372

The if(oldest->done) check that was added in recent commit 71d4372 appears to be redundant, as oldest->done is necessarily set. The loop at the beginning of the function ensures it:

    sr = searches;
    while(sr) {
        if(sr->done &&
           (oldest == NULL || oldest->step_time > sr->step_time))
            oldest = sr;
        sr = sr->next;
    }

However, this changes does make the point that it's fine for the new_search function to return a "done" slot. But isn't it supposed to be taken care of earlier in the function by the following lines ?

    /* The oldest slot is expired. */
    if(oldest && oldest->step_time < now.tv_sec - DHT_SEARCH_EXPIRE_TIME)
        return oldest;

Or maybe I'm wrong because we consider it's fine to return a "done" search that's not yet expired ? If that's the case, it seems that the observation I was making in PR #45 was incorrect and, we could revert the code back to before this change.

Also, you seem to have forgotten the possibility that oldest is NULL at that point in the function. It would be the case if no search is currently marked as "done".

It this a complete implementation for Kademlia DHT?

It's a really good implementation for DHT.

But I am a little confused with the algorithm for finding nodes. I think the most wonderful part of
Kademlia DHT is that we can do a recursive lookup, and each step we can make it half-more-closer to the target(aha, I have named it half-way-iteration-efficent.). And the most important data struct is how it deals with buckets, or route table.

As the described in Ethereum P2P Node Discovery.

Simple to say, It make use of an N size buckets with the i bucket storing the nodes have i common prefix as MyId.

But, as codes below, It uses a bucket link and iterate the 3 nearby buckets and pick the k nearest nodes. I have not figured out how It can make a half-way-iteration-efficent? Or if there something wrong understand to me, please sort me out, will be thankful.

b = find_bucket(id, AF_INET);
if(b) {
    numnodes = buffer_closest_nodes(nodes, numnodes, id, b);
     if(b->next)
         numnodes = buffer_closest_nodes(nodes, numnodes, id, b->next);
     b = previous_bucket(b);
     if(b)
        numnodes = buffer_closest_nodes(nodes, numnodes, id, b);
}

Enhance bootstrapping mechanism

From other implementations (libbtdht, KTorrent), I understand bootstrapping is usually done like this:

  • add bootstrap nodes with fake ids as far away as possible from one's own id
  • send find_node for slightly modified own id to bootstrap nodes
  • bootstrap is considered complete when a certain routing table depth (*) is reached

Here's how libbtdht does it:
https://github.com/bittorrent/libbtdht/blob/200b8adf83edeb84f1c81fb82de7e5b8624a5fa4/src/DhtImpl.cpp#L2673
https://github.com/bittorrent/libbtdht/blob/200b8adf83edeb84f1c81fb82de7e5b8624a5fa4/src/DhtImpl.cpp#L1263

(*) libbtdht refers to this as 'span', but I currently fail to understand how it is being calculated.

Would you care to add a similar mechanism?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.