Imported Upstream version 0.3.4
[anytun.git] / src / anyrtpproxy / commandHandler.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-2009 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 as published by
21  *  the Free Software Foundation, either version 3 of the License, or
22  *  any later version.
23  *
24  *  Anytun is distributed in the hope that it will be useful,
25  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
26  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  *  GNU General Public License for more details.
28  *
29  *  You should have received a copy of the GNU General Public License
30  *  along with anytun.  If not, see <http://www.gnu.org/licenses/>.
31  */
32
33 #include <sstream>
34 #include <vector>
35
36 #include <iomanip>
37 #include <iostream>
38 #include <sstream>
39
40 #include <boost/bind.hpp>
41
42 #include "commandHandler.h"
43 #include "../buffer.h"
44 #include "../log.h"
45 #include "../syncQueue.h"
46 #include "syncRtpCommand.h"
47 #include "rtpSessionTable.h"
48 #include "callIdQueue.h"
49 #include "options.h"
50
51 #define MAX_COMMAND_LENGTH 1000
52
53 CommandHandler::CommandHandler(SyncQueue& q, std::string lp,PortWindow& pw) : thread_(boost::bind(run,this)),
54   queue_(q), running_(true), control_sock_(io_service_),
55   local_address_(""), local_port_(lp),port_window_(pw)
56 {
57   proto::resolver resolver(io_service_);
58   proto::resolver::query query(local_port_);
59   proto::endpoint e = *resolver.resolve(query);
60   control_sock_.open(e.protocol());
61   control_sock_.bind(e);
62 }
63
64 CommandHandler::CommandHandler(SyncQueue& q, string la, std::string lp, PortWindow& pw) : thread_(boost::bind(run,this)),
65   queue_(q), running_(true), control_sock_(io_service_),
66   local_address_(la), local_port_(lp),port_window_(pw)
67 {
68   proto::resolver resolver(io_service_);
69   proto::resolver::query query(local_address_, local_port_);
70   proto::endpoint e = *resolver.resolve(query);
71   control_sock_.open(e.protocol());
72   control_sock_.bind(e);
73 }
74
75 void CommandHandler::run(void* s)
76 {
77   CommandHandler* self = reinterpret_cast<CommandHandler*>(s);
78
79   Buffer buf(uint32_t(MAX_COMMAND_LENGTH));
80   try {
81     proto::endpoint remote_end;
82
83     int len;
84     while(1) {
85       buf.setLength(MAX_COMMAND_LENGTH);
86
87       len = self->control_sock_.receive_from(boost::asio::buffer(buf.getBuf(), buf.getLength()), remote_end);
88       buf.setLength(len);
89
90       std::string ret = self->handle(std::string(reinterpret_cast<char*>(buf.getBuf()), buf.getLength())); // TODO: reinterpret is ugly
91
92       cLog.msg(Log::PRIO_DEBUG) << "CommandHandler received Command from " << remote_end << ", ret='" << ret << "'";
93
94       self->control_sock_.send_to(boost::asio::buffer(ret.c_str(), ret.length()), remote_end);
95     }
96   } catch(std::exception& e) {
97     self->running_ = false;
98   }
99   self->running_ = false;
100 }
101
102 bool CommandHandler::isRunning()
103 {
104   return running_;
105 }
106
107
108
109 std::string CommandHandler::handle(std::string command)
110 {
111   istringstream iss(command);
112   ostringstream oss;
113   std::string cookie;
114   std::string cmd;
115
116   iss >> cookie;
117   oss << cookie << " ";
118
119   if(iss.bad() || iss.eof()) {
120     oss << RET_ERR_SYNTAX;
121     return oss.str();
122   }
123   iss >> cmd;
124
125   std::vector<std::string> params;
126   while(!iss.bad() && !iss.eof()) {
127     std::string tmp;
128     iss >> tmp;
129     params.push_back(tmp);
130   }
131
132   switch(std::toupper(cmd[0])) {
133   case CMD_REQUEST:
134     if(params.size() < 4) { oss << RET_ERR_SYNTAX; break; }
135     oss << handleRequest(cmd.erase(0,1), params[0], params[1], params[2], params[3], (params.size() < 5) ? "" : params[4]);
136     break;
137   case CMD_RESPONSE:
138     if(params.size() < 4) { oss << RET_ERR_SYNTAX; break; }
139     oss << handleResponse(cmd.erase(0,1), params[0], params[1], params[2], params[3], (params.size() < 5) ? "" : params[4]);
140     break;
141   case CMD_DELETE:
142     if(params.size() < 2) { oss << RET_ERR_SYNTAX; break; }
143     oss << handleDelete(params[0], params[1], (params.size() < 3) ? "" : params[2]);
144     break;
145   case CMD_VERSION:
146     if(cmd.length() > 1 && cmd[1] == 'F') {
147       if(params.size() < 1) { oss << RET_ERR_SYNTAX; break; }
148       oss << handleVersionF(params[0]);
149       break;
150     }
151     oss << handleVersion();
152     break;
153   case CMD_INFO:
154     oss << handleInfo();
155     break;
156   default:
157     oss << RET_ERR_SYNTAX;
158     break;
159   }
160
161   return oss.str();
162 }
163
164 string CommandHandler::handleRequest(string modifiers, string call_id, string addr, string port, string from_tag, string to_tag)
165 {
166   std::cout << "received request[" << modifiers << "] command ('" << call_id << "','" << addr  << "','" << port
167             << "','" << from_tag << "','" << to_tag << "')" << std::endl;
168
169   try {
170     RtpSession::proto::resolver resolver(io_service_);
171     bool is_new;
172     RtpSession& session = gRtpSessionTable.getOrNewSession(call_id, is_new);
173     if(is_new) {
174       uint16_t port1 = port_window_.newPort(); // TODO: get next available port
175       uint16_t port2 = port_window_.newPort(); // TODO: get next available port
176       if(!port1 || !port2) {
177         if(port1) { port_window_.freePort(port1); }
178         if(port2) { port_window_.freePort(port2); }
179         throw std::runtime_error("no free port found");
180       }
181       std::stringstream ps1, ps2;
182       ps1 << port1;
183       ps2 << port2;
184
185       RtpSession::proto::endpoint e1, e2;
186       if(gOpt.getLocalAddr() == "") {
187         RtpSession::proto::resolver::query query1(ps1.str());
188         e1 = *resolver.resolve(query1);
189         RtpSession::proto::resolver::query query2(ps2.str());
190         e2 = *resolver.resolve(query2);
191       } else {
192         RtpSession::proto::resolver::query query1(gOpt.getLocalAddr(),ps1.str());
193         e1 = *resolver.resolve(query1);
194         RtpSession::proto::resolver::query query2(gOpt.getLocalAddr(),ps2.str());
195         e2 = *resolver.resolve(query2);
196       }
197
198       session.setLocalEnd1(e1);
199       session.setLocalEnd2(e2);
200     }
201     RtpSession::proto::resolver::query query(addr,port);
202     session.setRemoteEnd1(*resolver.resolve(query));
203
204     ostringstream oss;
205     oss << session.getLocalEnd2().port();
206     return oss.str();
207   } catch(std::exception& e) {
208     return RET_ERR_UNKNOWN; // TODO: change to corret error value
209   }
210 }
211
212 string CommandHandler::handleResponse(string modifiers, string call_id, string addr, string port, string from_tag, string to_tag)
213 {
214   std::cout << "received response[" << modifiers << "] command ('" << call_id << "','" << addr  << "','" << port
215             << "','" << from_tag << "','" << to_tag << "')" << std::endl;
216
217   try {
218     RtpSession& session = gRtpSessionTable.getSession(call_id);
219     RtpSession::proto::resolver resolver(io_service_);
220     RtpSession::proto::resolver::query query(addr,port);
221     session.setRemoteEnd2(*resolver.resolve(query));
222     session.isComplete(true);
223     SyncRtpCommand sc(call_id);
224     queue_.push(sc);
225
226     ostringstream oss;
227     oss << session.getLocalEnd1().port();
228     return oss.str();
229   } catch(std::exception& e) {
230     return RET_ERR_UNKNOWN; // TODO: change to corret error value
231   }
232 }
233
234 string CommandHandler::handleDelete(string call_id, string from_tag, string to_tag)
235 {
236   std::cout << "received delete command ('" << call_id << "','" << from_tag << "','" << to_tag << "')" << std::endl;
237
238   try {
239     RtpSession& session = gRtpSessionTable.getSession(call_id);
240     session.isDead(true);
241     SyncRtpCommand sc(call_id);
242     queue_.push(sc);
243
244     return RET_OK;
245   } catch(std::exception& e) {
246     return RET_ERR_UNKNOWN; // TODO: change to corret error value
247   }
248 }
249
250 string CommandHandler::handleVersion()
251 {
252   std::cout << "received version command" << std::endl;
253   return BASE_VERSION;
254 }
255
256 string CommandHandler::handleVersionF(string date_code)
257 {
258   std::cout << "received version[F] command ('" << date_code << "')" << std::endl;
259   if(!date_code.compare(SUP_VERSION)) {
260     return "1";
261   }
262
263   return "0";
264 }
265
266 string CommandHandler::handleInfo()
267 {
268   std::cout << "received info command, ignoring" << std::endl;
269   return RET_OK;
270 }
271