blob: 13dd3abcad8234239079927555d33fa8269aa192 [file] [log] [blame]
Alexander Afanasyev2a001942016-12-14 18:18:41 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Qi Zhao9ae88572017-05-23 10:54:01 -07003 * Copyright (c) 2014-2017, Regents of the University of California,
Alexander Afanasyev2a001942016-12-14 18:18:41 -08004 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include "ndn-fch-discovery.hpp"
27#include <boost/regex.hpp>
28#include <boost/algorithm/string.hpp>
29
30namespace ndn {
31namespace tools {
32namespace autoconfig {
33
34/**
35 * A partial and specialized copy of ndn::FaceUri implementation
36 *
37 * Consider removing in favor of a library-provided URL parsing, if project
38 * includes such a library.
39 */
40class Url
41{
42public:
43 Url(const std::string& url)
44 : m_isValid(false)
45 {
46 static const boost::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
47 boost::smatch protocolMatch;
48 if (!boost::regex_match(url, protocolMatch, protocolExp)) {
49 return;
50 }
51 m_scheme = protocolMatch[1];
52 const std::string& authority = protocolMatch[3];
53 m_path = protocolMatch[4];
54
55 // pattern for IPv6 address enclosed in [ ], with optional port number
56 static const boost::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
57 // pattern for IPv4-mapped IPv6 address, with optional port number
58 static const boost::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
59 // pattern for IPv4/hostname/fd/ifname, with optional port number
60 static const boost::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
61
62 if (authority.empty()) {
63 // UNIX, internal
64 }
65 else {
66 boost::smatch match;
67 bool isV6 = boost::regex_match(authority, match, v6Exp);
68 if (isV6 ||
69 boost::regex_match(authority, match, v4MappedV6Exp) ||
70 boost::regex_match(authority, match, v4HostExp)) {
71 m_host = match[1];
72 m_port = match[2];
73 }
74 else {
75 return;
76 }
77 }
78 if (m_port.empty()) {
79 m_port = "80";
80 }
81 if (m_path.empty()) {
82 m_path = "/";
83 }
84 m_isValid = true;
85 }
86
87 bool
88 isValid() const
89 {
90 return m_isValid;
91 }
92
93 const std::string&
94 getScheme() const
95 {
96 return m_scheme;
97 }
98
99 const std::string&
100 getHost() const
101 {
102 return m_host;
103 }
104
105 const std::string&
106 getPort() const
107 {
108 return m_port;
109 }
110
111 const std::string&
112 getPath() const
113 {
114 return m_path;
115 }
116
117private:
118 bool m_isValid;
119 std::string m_scheme;
120 std::string m_host;
121 std::string m_port;
122 std::string m_path;
123};
124
125class HttpException : public std::runtime_error
126{
127public:
128 explicit
129 HttpException(const std::string& what)
130 : std::runtime_error(what)
131 {
132 }
133};
134
135NdnFchDiscovery::NdnFchDiscovery(Face& face, KeyChain& keyChain,
136 const std::string& url,
137 const NextStageCallback& nextStageOnFailure)
138 : Base(face, keyChain, nextStageOnFailure)
139 , m_url(url)
140{
141}
142
143void
144NdnFchDiscovery::start()
145{
Qi Zhao9ae88572017-05-23 10:54:01 -0700146 std::cerr << "Trying NDN-FCH service..." << std::endl;
147
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800148 try {
149 using namespace boost::asio::ip;
150 tcp::iostream requestStream;
151
152 requestStream.expires_from_now(boost::posix_time::milliseconds(3000));
153
154 Url url(m_url);
155 if (!url.isValid()) {
156 BOOST_THROW_EXCEPTION(HttpException("Invalid NDN-FCH URL: " + m_url));
157 }
158
159 if (!boost::iequals(url.getScheme(), "http")) {
160 BOOST_THROW_EXCEPTION(HttpException("Only http:// NDN-FCH URLs are supported"));
161 }
162
163 requestStream.connect(url.getHost(), url.getPort());
164
165 if (!requestStream) {
166 BOOST_THROW_EXCEPTION(HttpException("HTTP connection error to " + m_url));
167 }
168
169 requestStream << "GET " << url.getPath() << " HTTP/1.0\r\n";
170 requestStream << "Host: " << url.getHost() << ":" << url.getPort() << "\r\n";
171 requestStream << "Accept: */*\r\n";
172 requestStream << "Cache-Control: no-cache\r\n";
173 requestStream << "Connection: close\r\n\r\n";
174 requestStream.flush();
175
176 std::string statusLine;
177 std::getline(requestStream, statusLine);
178 if (!requestStream) {
179 BOOST_THROW_EXCEPTION(HttpException("HTTP communication error"));
180 }
181
182 std::stringstream responseStream(statusLine);
183 std::string httpVersion;
184 responseStream >> httpVersion;
185 unsigned int statusCode;
186 responseStream >> statusCode;
187 std::string statusMessage;
188
189 std::getline(responseStream, statusMessage);
190 if (!static_cast<bool>(requestStream) || httpVersion.substr(0, 5) != "HTTP/") {
Qi Zhao9ae88572017-05-23 10:54:01 -0700191 BOOST_THROW_EXCEPTION(HttpException("HTTP communication error"));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800192 }
193 if (statusCode != 200) {
194 boost::trim(statusMessage);
Qi Zhao9ae88572017-05-23 10:54:01 -0700195 BOOST_THROW_EXCEPTION(HttpException("HTTP request failed: " + std::to_string(statusCode) + " " + statusMessage));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800196 }
197 std::string header;
198 while (std::getline(requestStream, header) && header != "\r")
199 ;
200
201 std::string hubHost;
202 requestStream >> hubHost;
203
204 if (hubHost.empty()) {
Qi Zhao9ae88572017-05-23 10:54:01 -0700205 BOOST_THROW_EXCEPTION(HttpException("NDN-FCH did not return hub host"));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800206 }
207
208 this->connectToHub("udp://" + hubHost);
209 }
210 catch (const std::runtime_error& e) {
211 m_nextStageOnFailure(std::string("Failed to find NDN router using NDN-FCH service (") + e.what() + ")");
212 }
213}
214
215} // namespace autoconfig
216} // namespace tools
217} // namespace ndn