apps/tca_admin/TcaRegistry.cc
changeset 0 2b3e5ec03512
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/apps/tca_admin/TcaRegistry.cc	Thu Apr 21 14:57:45 2011 +0100
@@ -0,0 +1,262 @@
+/*
+ *    Copyright 2005-2006 University of Waterloo
+ * 
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <dtn-config.h>
+#endif
+
+#include "libs/gateway_prot.h"
+#include "libs/gateway_rpc.h"
+#include "libs/sha1.h"
+#include "TcaRegistry.h"
+
+static const char* APP_STRING = "tca";
+static const char* CLIB_STRING = "rpcgen";
+
+static const int DHT_KEYLEN = 20;           // number of uints in a key
+
+
+ // hash a key s, from original long-string form, down to 20-byte key
+ // usable in the dht
+ static void
+ hash(const std::string& s, uint8 digest[DHT_KEYLEN])
+ {
+     // Use sha1 hash of endpointid to get a (probably) unique 20-byte key
+     sha1_context ctx;
+     sha1_starts(&ctx);
+     sha1_update(&ctx, (unsigned char*)(s.c_str()), s.length());
+     sha1_finish(&ctx, digest);
+ }
+
+
+/*
+static void
+dump_digest(uint8 digest[DHT_KEYLEN])
+{
+    printf("digest=");
+    for (int i=0; i<DHT_KEYLEN; ++i) printf("%c", digest[i]);
+    printf("\n");
+}
+*/
+ 
+///////////////////////////////////////////////////////////////////////////////
+// class TcaRegistry
+
+
+bool
+TcaRegistry::init_nodes()
+{
+    // Construct list of available DHT nodes, hard coded at the moment.
+    // TODO: Do something smarter here, like go to OpenDHT site and read
+    // the current list of DHT nodes. Or read them from a local file that
+    // somebody actively maintains.
+
+    // To make this fast as possible for testing, cut this list down to just
+    // a few. For greater reliability and scalability, use more nodes.
+    dht_nodes_.push_back(std::string("cloudburst.uwaterloo.ca"));
+    dht_nodes_.push_back(std::string("blast.uwaterloo.ca"));
+
+    // Other known nodes:
+    /*
+    dht_nodes_.push_back(std::string("lefthand.eecs.harvard.edu"));
+    dht_nodes_.push_back(std::string("node2.lbnl.nodes.planet-lab.org"));
+    dht_nodes_.push_back(std::string("pl1.cs.utk.edu"));
+    dht_nodes_.push_back(std::string("pl1.ece.toronto.edu"));
+    dht_nodes_.push_back(std::string("planetlab2.cnds.jhu.edu"));
+    dht_nodes_.push_back(std::string("ricepl-3.cs.rice.edu"));
+    dht_nodes_.push_back(std::string("pli2-pa-3.hpl.hp.com"));
+    dht_nodes_.push_back(std::string("planetlab10.millennium.berkeley.edu"));
+    */
+
+    return true;
+}
+
+
+bool
+TcaRegistry::init_addrs()
+{
+    // First pass at "something smarter"... 
+    // Test each dht node and keep only the nodes that are awake.
+
+    // Usage Note: It would be good to call this function periodically
+    // to refresh the list of "good" nodes.
+
+    printf("Initializing TcaRegistry...\n");
+
+    last_node_ = 0;
+
+    sockaddr_in addr;
+    for (unsigned int i=0; i<dht_nodes_.size(); ++i)
+    {
+        if (test_node(dht_nodes_[i].c_str(), &addr))
+        {
+            // it's a keeper
+            dht_addrs_.push_back(addr);
+        }
+    }
+            
+    if (dht_addrs_.size() == 0) return false;
+
+    printf("...dht nodes available = %zu / %zu\n",
+           dht_addrs_.size(), dht_nodes_.size());
+    return true;
+}
+
+
+
+// write a registry record
+
+bool
+TcaRegistry::write(const RegRecord& rr, int ttl)
+{
+    CLIENT* p_node = get_node();
+    if (p_node == NULL) return false;
+
+    // printf("TcaRegistry::write: using node %d\n", int(p_node));
+
+    uint8 key[DHT_KEYLEN];
+    hash(rr.host_, key);
+    // dump_digest(key);
+
+    bamboo_put_args args;
+    memset(&args, 0, sizeof(args));
+
+    args.application = const_cast<char*>(APP_STRING);
+    args.client_library = const_cast<char*>(CLIB_STRING);
+    memcpy(args.key, key, DHT_KEYLEN);
+
+    args.value.bamboo_value_len = rr.link_addr_.length() + 1;
+    args.value.bamboo_value_val = const_cast<char*>(rr.link_addr_.c_str());
+
+    args.ttl_sec = ttl;
+
+    // TODO: Append other fields? Like timestamp of entry/refresh?
+    
+    bamboo_stat* res = bamboo_dht_proc_put_2(&args, p_node);
+    // printf("TcaRegistry::write: put return code = %d\n", int(*res));
+        
+    return (*res == BAMBOO_OK);
+}
+
+
+
+// read a registry record
+// rr.eid_ must be primed with the endpointid of the node to lookup
+
+bool
+TcaRegistry::read(RegRecord& rr)
+{
+    CLIENT* p_node = get_node();
+    if (p_node == NULL) return false;
+
+    // printf("TcaRegistry::read: using node %d\n", int(p_node));
+
+    uint8 key[DHT_KEYLEN];
+    hash(rr.host_, key);
+    // dump_digest(key);
+
+    bamboo_get_args args;
+    memset(&args, 0, sizeof(args));
+
+    args.application = const_cast<char*>(APP_STRING);
+    args.client_library = const_cast<char*>(CLIB_STRING);
+    memcpy(args.key, key, DHT_KEYLEN);
+
+    // Note: to here, this function is identical to write()
+
+    args.maxvals = 1;
+
+    bamboo_get_res* res = bamboo_dht_proc_get_2(&args, p_node);
+    if (res == NULL)
+    {
+        printf("TcaRegistry::read: get returned NULL\n");
+        return false;
+    }
+
+    int n_values = res->values.values_len;
+    
+    if (n_values != 1)
+    {
+        // printf("TcaRegistry::read: get returned %d values\n", n_values);
+        return false;
+    }
+
+    bamboo_value* p_val = &res->values.values_val[0];
+
+    rr.link_addr_ = p_val->bamboo_value_val;
+    printf("TcaRegistry::read: succeeded! value=%s\n", rr.link_addr_.c_str());
+
+    return true;
+}
+
+
+/*
+// This version gets nodes by dns name -- inneficient because of dns
+// lookup, and also bad because the node in question may not be alive.
+CLIENT*
+TcaRegistry::get_node()
+{
+    // Get next available node. We deliberately spread the load around
+    // among all availabe nodes, and also tolerate missing nodes.
+
+    CLIENT* p_node = NULL;
+
+    for (unsigned int i = last_node_ + 1; i != last_node_; ++i)
+    {
+        if (i == dht_nodes_.size()) i = 0;
+        p_node = get_connection(dht_nodes_[i].c_str());
+        if (p_node)
+        {
+            printf("TcaRegistry::get_node: using node %s\n",
+                    dht_nodes_[i].c_str());
+            last_node_ = i;
+            break;
+        }
+    }
+
+    return p_node;
+}
+*/
+
+
+CLIENT*
+TcaRegistry::get_node()
+{
+    // Get next available node. We deliberately spread the load around
+    // among all availabe nodes, and also tolerate missing nodes.
+    //
+    // This version uses the addrs list which saves a dns lookup.
+    // Also, the addrs list is only populated with the nodes that are alive
+    // at startup, so there's less chance of failed attempts.
+
+    CLIENT* p_node = NULL;
+
+    for (unsigned int i = last_node_ + 1; i != last_node_; ++i)
+    {
+        if (i == dht_addrs_.size()) i = 0;
+        p_node = get_connection(&dht_addrs_[i]);
+        if (p_node)
+        {
+            last_node_ = i;
+            break;
+        }
+    }
+
+    return p_node;
+}
+
+
+