ccnx tunnel; in progress
diff --git a/include/ccnx-tunnel.h b/include/ccnx-tunnel.h
new file mode 100644
index 0000000..cddfcd5
--- /dev/null
+++ b/include/ccnx-tunnel.h
@@ -0,0 +1,84 @@
+#ifndef CCNX_TUNNEL_H
+#define CCNX_TUNNEL_H
+
+#include <sync/sync-ccnx-wrapper.h>
+#include <string>
+#include <boost/function.hpp>
+#include <boost/variant.hpp>
+#include <boost/thread/locks.hpp>
+#include <utility>
+#include <map>
+
+using namespace Sync;
+using namespace std;
+
+// Eventually, Sync::CcnxWrapper should be moved to this namespace. 
+// It has nothing to do with Sync.
+namespace Ccnx
+{
+
+class CcnxTunnel : public CcnxWrapper 
+{
+public: 
+  typedef boost::variant<StringDataCallback, RawDataCallback> DataCallback;
+  typedef multimap<string, DataCallback> PendingInterestTable;
+  typedef multimap<string, InterestCallback> RegisteredInterestTable;
+  typedef multimap<string, DataCallback>iterator PitIter;
+  typedef multimap<string, InterestCallback>iterator RitIter;
+  typedef boost::shared_mutex Lock;
+  typedef boost::unique_lock<Lock> WriteLock;
+  typedef boost::shared_lock<Lock> ReadLock;
+
+
+  CcnxTunnel(); 
+  virtual ~CcnxTunnel();
+
+  // name is topology-independent
+  virtual int
+  publishRawData(const string &name, const char *buf, size_t len, int freshness);
+
+  // prefix is topology-independent
+  virtual int
+  setInterestFilter(const string &prefix, const InterestCallback &interestCallback);
+
+  // prefix is topology-independent
+  // this clears all entries with key equal to prefix
+  virtual void
+  clearInterestFilter(const string &prefix);
+
+  // subclass should provide translation service from topology-independent name
+  // to routable name
+  virtual string
+  queryRoutableName(string &name) = 0;
+
+  // subclass should implement the function to store ContentObject with topoloy-independent
+  // name to the permanent storage; default does nothing
+  virtual void
+  storeContentObject(string &name, ContentObjectPtr co) {}
+
+  // should be called  when connect to a different network
+  void
+  refreshLocalPrefix();
+
+  static bool
+  isPrefix(string &prefix, string &name);
+
+protected:
+  void
+  handleTunneledInterest(string tunneldInterest);
+
+  virtual int
+  sendInterest (const std::string &strInterest, void *dataPass);
+
+protected:
+  // need a way to update local prefix, perhaps using macports trick, but eventually we need something more portable
+  string m_localPrefix;
+  PendingInterestTable m_pit;
+  RegisteredInterestTable m_rit;
+  Lock m_pitLock;
+  Lock m_ritLock;
+};
+
+};
+
+#endif
diff --git a/src/ccnx-tunnel.cpp b/src/ccnx-tunnel.cpp
new file mode 100644
index 0000000..2ac604a
--- /dev/null
+++ b/src/ccnx-tunnel.cpp
@@ -0,0 +1,94 @@
+#include "ccnx-tunnel.h"
+
+namespace Ccnx
+{
+
+CcnxTunnel::CcnxTunnel()
+                          : CcnxWrapper()
+                          , m_localPrefix("/")
+{
+  refreshLocalPrefix();
+}
+
+CcnxTunnel::~CcnxTunnel() 
+{
+}
+
+CcnxTunnel::refreshLocalPrefix() 
+{
+  string newPrefix = getLocalPrefix();
+  if (!newPrefix.empty() && m_localPrefix != newPrefix)
+  {
+    CcnxWrapper::clearInterestFilter(m_localPrefix);
+    CcnxWrapper::setInterestFilter(newPrefix, bind(&CcnxTunnel::handleTunneledInterest, this, _1));
+    m_localPrefix = newPrefix;
+  }
+}
+
+int
+CccnxTunnel::publishRawData(const string &name, const char *buf, size_t len, int freshness)
+{
+  ContentObjectPtr co = createContentObject(name, buf, len, freshness);
+  storeContentObject(name, co);
+  
+  string tunneledName = queryRoutableName(name);
+  ContentObjectPtr tunneledCo = createContentObject(tunneledName, co->m_data, co->m_len, freshness);
+
+  return putToCcnd(tunneledCo);
+}
+
+void
+CcnxTunnel::handleTunneledInterest(string tunneledInterest)
+{
+  // The interest must have m_localPrefix as a prefix (component-wise), otherwise ccnd would not deliver it to us
+  string interest = (m_localPrefix == "/") 
+                    ? tunneledInterest
+                    : tunneldInterest.substr(m_localPrefix.size());
+
+  ReadLock(m_ritLock); 
+  
+  // This is linear scan, but should be acceptable under the assumption that the caller wouldn't be listening to a lot prefixes (as of now, most app listen to one or two prefixes)
+  for (RitIter it = m_rit.begin(); it != m_rit.end(); it++)
+  {
+    // evoke callback for any prefix that is the prefix of the interest
+    if (isPrefix(it->first(), interest))
+    {
+      (it->second())(interest);
+    }
+  }
+}
+
+bool
+CcnxTunnel::isPrefix(string &prefix, string &name)
+{
+  // prefix is literally prefix of name 
+  if (name.find(prefix) == 0)
+  {
+    // name and prefix are exactly the same, or the next character in name
+    // is '/'; in both case, prefix is the ccnx prefix of name (component-wise)
+    if (name.size() == prefix.size() || name.at(prefix.size()) == '/')
+    {
+      return true;
+    }
+  }
+  return false;
+}
+
+int 
+CcnxTunnel::setInterestFilter(const string &prefix, const InterestCallback &interestCallback)
+{
+  WriteLock(m_ritLock);
+  m_rit.insert(make_pair(prefix, new InterestCallback(interestCallback)));
+  return 0;
+}
+
+void 
+CcnxTunnel::clearInterestFilter(const string &prefix)
+{
+  WriteLock(m_ritLock);
+  // remove all
+  m_rit.erase(prefix);
+}
+
+}
+