blob: 52521e7672372ba788244cff2d5546503737744e [file] [log] [blame]
Alexander Afanasyev2a001942016-12-14 18:18:41 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Junxiao Shif748a4e2017-07-05 23:41:48 +00002/*
Davide Pesaventoe422f9e2022-06-03 01:30:23 -04003 * Copyright (c) 2014-2022, 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"
Davide Pesaventoa997d292017-08-24 20:16:59 -040027
Alexander Afanasyev2a001942016-12-14 18:18:41 -080028#include <boost/algorithm/string.hpp>
Davide Pesavento45898d22018-04-17 21:59:54 -040029#include <boost/asio/ip/tcp.hpp>
Davide Pesaventoa997d292017-08-24 20:16:59 -040030
Davide Pesaventoe4b22382018-06-10 14:37:24 -040031#include <regex>
Davide Pesaventoa997d292017-08-24 20:16:59 -040032#include <sstream>
Alexander Afanasyev2a001942016-12-14 18:18:41 -080033
Davide Pesaventoe422f9e2022-06-03 01:30:23 -040034namespace ndn::autoconfig {
Alexander Afanasyev2a001942016-12-14 18:18:41 -080035
36/**
37 * A partial and specialized copy of ndn::FaceUri implementation
38 *
39 * Consider removing in favor of a library-provided URL parsing, if project
40 * includes such a library.
41 */
42class Url
43{
44public:
45 Url(const std::string& url)
46 : m_isValid(false)
47 {
Davide Pesaventoe4b22382018-06-10 14:37:24 -040048 static const std::regex protocolExp("(\\w+\\d?(\\+\\w+)?)://([^/]*)(\\/[^?]*)?");
49 std::smatch protocolMatch;
50 if (!std::regex_match(url, protocolMatch, protocolExp)) {
Alexander Afanasyev2a001942016-12-14 18:18:41 -080051 return;
52 }
53 m_scheme = protocolMatch[1];
Davide Pesaventoe4b22382018-06-10 14:37:24 -040054 std::string authority = protocolMatch[3];
Alexander Afanasyev2a001942016-12-14 18:18:41 -080055 m_path = protocolMatch[4];
56
57 // pattern for IPv6 address enclosed in [ ], with optional port number
Davide Pesaventoe4b22382018-06-10 14:37:24 -040058 static const std::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
Alexander Afanasyev2a001942016-12-14 18:18:41 -080059 // pattern for IPv4-mapped IPv6 address, with optional port number
Davide Pesaventoe4b22382018-06-10 14:37:24 -040060 static const std::regex v4MappedV6Exp("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
Alexander Afanasyev2a001942016-12-14 18:18:41 -080061 // pattern for IPv4/hostname/fd/ifname, with optional port number
Davide Pesaventoe4b22382018-06-10 14:37:24 -040062 static const std::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$");
Alexander Afanasyev2a001942016-12-14 18:18:41 -080063
64 if (authority.empty()) {
65 // UNIX, internal
66 }
67 else {
Davide Pesaventoe4b22382018-06-10 14:37:24 -040068 std::smatch match;
69 bool isV6 = std::regex_match(authority, match, v6Exp);
Alexander Afanasyev2a001942016-12-14 18:18:41 -080070 if (isV6 ||
Davide Pesaventoe4b22382018-06-10 14:37:24 -040071 std::regex_match(authority, match, v4MappedV6Exp) ||
72 std::regex_match(authority, match, v4HostExp)) {
Alexander Afanasyev2a001942016-12-14 18:18:41 -080073 m_host = match[1];
74 m_port = match[2];
75 }
76 else {
77 return;
78 }
79 }
80 if (m_port.empty()) {
81 m_port = "80";
82 }
83 if (m_path.empty()) {
84 m_path = "/";
85 }
86 m_isValid = true;
87 }
88
89 bool
90 isValid() const
91 {
92 return m_isValid;
93 }
94
95 const std::string&
96 getScheme() const
97 {
98 return m_scheme;
99 }
100
101 const std::string&
102 getHost() const
103 {
104 return m_host;
105 }
106
107 const std::string&
108 getPort() const
109 {
110 return m_port;
111 }
112
113 const std::string&
114 getPath() const
115 {
116 return m_path;
117 }
118
119private:
120 bool m_isValid;
121 std::string m_scheme;
122 std::string m_host;
123 std::string m_port;
124 std::string m_path;
125};
126
127class HttpException : public std::runtime_error
128{
129public:
Davide Pesaventoaa9e3b22022-10-21 17:00:07 -0400130 using std::runtime_error::runtime_error;
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800131};
132
Junxiao Shicb766862017-07-07 22:21:04 +0000133NdnFchDiscovery::NdnFchDiscovery(const std::string& url)
134 : m_url(url)
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800135{
136}
137
138void
Junxiao Shicb766862017-07-07 22:21:04 +0000139NdnFchDiscovery::doStart()
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800140{
141 try {
Davide Pesavento45898d22018-04-17 21:59:54 -0400142 boost::asio::ip::tcp::iostream requestStream;
143#if BOOST_VERSION >= 106700
144 requestStream.expires_after(std::chrono::seconds(3));
145#else
146 requestStream.expires_from_now(boost::posix_time::seconds(3));
147#endif // BOOST_VERSION >= 106700
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800148
149 Url url(m_url);
150 if (!url.isValid()) {
Davide Pesavento19779d82019-02-14 13:40:04 -0500151 NDN_THROW(HttpException("Invalid NDN-FCH URL: " + m_url));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800152 }
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800153 if (!boost::iequals(url.getScheme(), "http")) {
Davide Pesavento19779d82019-02-14 13:40:04 -0500154 NDN_THROW(HttpException("Only http:// NDN-FCH URLs are supported"));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800155 }
156
157 requestStream.connect(url.getHost(), url.getPort());
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800158 if (!requestStream) {
Davide Pesavento19779d82019-02-14 13:40:04 -0500159 NDN_THROW(HttpException("HTTP connection error to " + m_url));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800160 }
161
162 requestStream << "GET " << url.getPath() << " HTTP/1.0\r\n";
163 requestStream << "Host: " << url.getHost() << ":" << url.getPort() << "\r\n";
164 requestStream << "Accept: */*\r\n";
165 requestStream << "Cache-Control: no-cache\r\n";
166 requestStream << "Connection: close\r\n\r\n";
167 requestStream.flush();
168
169 std::string statusLine;
170 std::getline(requestStream, statusLine);
171 if (!requestStream) {
Davide Pesavento19779d82019-02-14 13:40:04 -0500172 NDN_THROW(HttpException("HTTP communication error"));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800173 }
174
175 std::stringstream responseStream(statusLine);
176 std::string httpVersion;
177 responseStream >> httpVersion;
178 unsigned int statusCode;
179 responseStream >> statusCode;
180 std::string statusMessage;
181
182 std::getline(responseStream, statusMessage);
183 if (!static_cast<bool>(requestStream) || httpVersion.substr(0, 5) != "HTTP/") {
Davide Pesavento19779d82019-02-14 13:40:04 -0500184 NDN_THROW(HttpException("HTTP communication error"));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800185 }
186 if (statusCode != 200) {
187 boost::trim(statusMessage);
Davide Pesavento19779d82019-02-14 13:40:04 -0500188 NDN_THROW(HttpException("HTTP request failed: " + to_string(statusCode) + " " + statusMessage));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800189 }
190 std::string header;
191 while (std::getline(requestStream, header) && header != "\r")
192 ;
193
194 std::string hubHost;
195 requestStream >> hubHost;
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800196 if (hubHost.empty()) {
Davide Pesavento19779d82019-02-14 13:40:04 -0500197 NDN_THROW(HttpException("NDN-FCH did not return hub host"));
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800198 }
199
Junxiao Shicb766862017-07-07 22:21:04 +0000200 this->provideHubFaceUri("udp://" + hubHost);
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800201 }
202 catch (const std::runtime_error& e) {
Junxiao Shicb766862017-07-07 22:21:04 +0000203 this->fail(e.what());
Alexander Afanasyev2a001942016-12-14 18:18:41 -0800204 }
205}
206
Davide Pesaventoe422f9e2022-06-03 01:30:23 -0400207} // namespace ndn::autoconfig