Imported Upstream version 0.3
[anytun.git] / src / resolver.cpp
1 /*
2  *  anytun
3  *
4  *  The secure anycast tunneling protocol (satp) defines a protocol used
5  *  for communication between any combination of unicast and anycast
6  *  tunnel endpoints.  It has less protocol overhead than IPSec in Tunnel
7  *  mode and allows tunneling of every ETHER TYPE protocol (e.g.
8  *  ethernet, ip, arp ...). satp directly includes cryptography and
9  *  message authentication based on the methodes used by SRTP.  It is
10  *  intended to deliver a generic, scaleable and secure solution for
11  *  tunneling and relaying of packets of any protocol.
12  *
13  *
14  *  Copyright (C) 2007-2008 Othmar Gsenger, Erwin Nindl, 
15  *                          Christian Pointner <satp@wirdorange.org>
16  *
17  *  This file is part of Anytun.
18  *
19  *  Anytun is free software: you can redistribute it and/or modify
20  *  it under the terms of the GNU General Public License version 3 as
21  *  published by the Free Software Foundation.
22  *
23  *  Anytun is distributed in the hope that it will be useful,
24  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
25  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  *  GNU General Public License for more details.
27  *
28  *  You should have received a copy of the GNU General Public License
29  *  along with anytun.  If not, see <http://www.gnu.org/licenses/>.
30  */
31
32 #include <boost/bind.hpp>
33 #include <boost/system/error_code.hpp>
34
35 #include "resolver.h"
36 #include "log.h"
37
38 using ::boost::asio::ip::udp;
39 using ::boost::asio::ip::tcp;
40
41 template<class Proto>
42 void waitAndEnqueue(u_int32_t s, const std::string& addr, const std::string& port, boost::function<void(boost::asio::ip::basic_endpoint<Proto>)> const& onResolve, ErrorCallback const& onError, ResolvAddrType r)
43 {
44   cLog.msg(Log::PRIO_ERROR) << "the resolver only supports udp and tcp";
45 }
46
47 template<>
48 void waitAndEnqueue(u_int32_t s, const std::string& addr, const std::string& port, boost::function<void(boost::asio::ip::basic_endpoint<udp>)> const& onResolve, ErrorCallback const& onError, ResolvAddrType r)
49 {
50   boost::this_thread::sleep(boost::posix_time::milliseconds(s * 1000));
51   gResolver.resolveUdp(addr, port, onResolve, onError, r);
52 }
53
54 template<>
55 void waitAndEnqueue(u_int32_t s, const std::string& addr, const std::string& port, boost::function<void(boost::asio::ip::basic_endpoint<tcp>)> const& onResolve, ErrorCallback const& onError, ResolvAddrType r)
56 {
57   boost::this_thread::sleep(boost::posix_time::milliseconds(s * 1000));
58   gResolver.resolveTcp(addr, port, onResolve, onError, r);
59 }
60
61
62 template<class Proto>
63 ResolveHandler<Proto>::ResolveHandler(const std::string& addr, const std::string& port, boost::function<void(boost::asio::ip::basic_endpoint<Proto>)> const& onResolve, ErrorCallback const& onError, ResolvAddrType r) : addr_(addr), port_(port), onResolve_(onResolve), onError_(onError), resolv_addr_type_(r)
64 {
65 }
66
67 template<class Proto>
68 void ResolveHandler<Proto>::operator()(const boost::system::error_code& e, const boost::asio::ip::basic_resolver_iterator<Proto> endpointIt)
69 {
70   if(boost::system::posix_error::success == e) {
71     try {
72       onResolve_(*endpointIt);
73     }
74     catch(const std::runtime_error& e)
75     {
76       onError_(e);
77     }
78   } else {
79     cLog.msg(Log::PRIO_ERROR) << "Error while resolving '" << addr_ << "' '" << port_ << "', retrying in 10 sec.";
80     boost::thread(boost::bind(waitAndEnqueue<Proto>, 10, addr_, port_, onResolve_, onError_, resolv_addr_type_));
81   }
82 }
83
84 Resolver* Resolver::inst = NULL;
85 Mutex Resolver::instMutex;
86 Resolver& gResolver = Resolver::instance();
87
88 Resolver& Resolver::instance()
89 {
90   Lock lock(instMutex);
91   static instanceCleaner c;
92   if(!inst)
93     inst = new Resolver();
94   
95   return *inst;
96 }
97
98 Resolver::Resolver() : udp_resolver_(io_service_), tcp_resolver_(io_service_), thread_(NULL)
99 {
100 }
101
102 Resolver::~Resolver()
103 {
104   if(thread_)
105     delete thread_;
106 }
107
108 void Resolver::init()
109 {
110   if(!thread_)
111           thread_ = new boost::thread(boost::bind(&Resolver::run, this));
112 }
113
114 void Resolver::run()
115 {
116   cLog.msg(Log::PRIO_DEBUG) << "Resolver Thread started";
117
118   while(1) {
119     try {
120       io_service_.run();
121       io_service_.reset();
122       boost::this_thread::sleep(boost::posix_time::milliseconds(250));
123     }
124     catch(const std::runtime_error& e)
125     {
126       cLog.msg(Log::PRIO_ERROR) << "resolver caught runtime error, restarting: " << e.what();
127     }
128     catch(const std::exception& e)
129     {
130       cLog.msg(Log::PRIO_ERROR) << "resolver caught exception, restarting: " << e.what();
131     }
132   }
133 }
134
135
136 void Resolver::resolveUdp(const std::string& addr, const std::string& port, UdpResolveCallback const& onResolve, ErrorCallback const& onError, ResolvAddrType r)
137 {
138   cLog.msg(Log::PRIO_DEBUG) << "trying to resolv UDP: '" << addr << "' '" << port << "'";
139
140   std::auto_ptr<udp::resolver::query> query;
141   if(addr != "") {
142     switch(r) {
143     case IPV4_ONLY: query = std::auto_ptr<udp::resolver::query>(new udp::resolver::query(udp::v4(), addr, port)); break;
144     case IPV6_ONLY: query = std::auto_ptr<udp::resolver::query>(new udp::resolver::query(udp::v6(), addr, port)); break;
145     default: query = std::auto_ptr<udp::resolver::query>(new udp::resolver::query(addr, port)); break;
146     }
147   }
148   else {
149     switch(r) {
150     case IPV4_ONLY: query = std::auto_ptr<udp::resolver::query>(new udp::resolver::query(udp::v4(), port)); break;
151     case IPV6_ONLY: query = std::auto_ptr<udp::resolver::query>(new udp::resolver::query(udp::v6(), port)); break;
152     default: query = std::auto_ptr<udp::resolver::query>(new udp::resolver::query(port)); break;
153     }
154   }
155   UdpResolveHandler handler(addr, port, onResolve, onError, r);
156   udp_resolver_.async_resolve(*query, handler);
157 }
158
159 void Resolver::resolveTcp(const std::string& addr, const std::string& port, TcpResolveCallback const& onResolve, ErrorCallback const& onError, ResolvAddrType r)
160 {
161   cLog.msg(Log::PRIO_DEBUG) << "trying to resolv TCP: '" << addr << "' '" << port << "'";
162
163   std::auto_ptr<tcp::resolver::query> query;
164   if(addr != "") {
165     switch(r) {
166     case IPV4_ONLY: query = std::auto_ptr<tcp::resolver::query>(new tcp::resolver::query(tcp::v4(), addr, port)); break;
167     case IPV6_ONLY: query = std::auto_ptr<tcp::resolver::query>(new tcp::resolver::query(tcp::v6(), addr, port)); break;
168     default: query = std::auto_ptr<tcp::resolver::query>(new tcp::resolver::query(addr, port)); break;
169     }
170   }
171   else {
172     switch(r) {
173     case IPV4_ONLY: query = std::auto_ptr<tcp::resolver::query>(new tcp::resolver::query(tcp::v4(), port)); break;
174     case IPV6_ONLY: query = std::auto_ptr<tcp::resolver::query>(new tcp::resolver::query(tcp::v6(), port)); break;
175     default: query = std::auto_ptr<tcp::resolver::query>(new tcp::resolver::query(port)); break;
176     }
177   }
178   TcpResolveHandler handler(addr, port, onResolve, onError, r);
179   tcp_resolver_.async_resolve(*query, handler); 
180 }