1 /* <!-- copyright */
2 /*
3  * aria2 - The high speed download utility
4  *
5  * Copyright (C) 2006 Tatsuhiro Tsujikawa
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  *
21  * In addition, as a special exception, the copyright holders give
22  * permission to link the code of portions of this program with the
23  * OpenSSL library under certain conditions as described in each
24  * individual source file, and distribute linked combinations
25  * including the two.
26  * You must obey the GNU General Public License in all respects
27  * for all of the code used other than OpenSSL.  If you modify
28  * file(s) with this exception, you may extend this exception to your
29  * version of the file(s), but you are not obligated to do so.  If you
30  * do not wish to do so, delete this exception statement from your
31  * version.  If you delete this exception statement from all source
32  * files in the program, then also delete it here.
33  */
34 /* copyright --> */
35 #include "InitiateConnectionCommand.h"
36 #include "Request.h"
37 #include "DownloadEngine.h"
38 #include "Option.h"
39 #include "Logger.h"
40 #include "LogFactory.h"
41 #include "message.h"
42 #include "prefs.h"
43 #include "NameResolver.h"
44 #include "SocketCore.h"
45 #include "FileEntry.h"
46 #include "RequestGroup.h"
47 #include "Segment.h"
48 #include "a2functional.h"
49 #include "InitiateConnectionCommandFactory.h"
50 #include "util.h"
51 #include "RecoverableException.h"
52 #include "fmt.h"
53 #include "SocketRecvBuffer.h"
54 #include "BackupIPv4ConnectCommand.h"
55 #include "ConnectCommand.h"
56 
57 namespace aria2 {
58 
InitiateConnectionCommand(cuid_t cuid,const std::shared_ptr<Request> & req,const std::shared_ptr<FileEntry> & fileEntry,RequestGroup * requestGroup,DownloadEngine * e)59 InitiateConnectionCommand::InitiateConnectionCommand(
60     cuid_t cuid, const std::shared_ptr<Request>& req,
61     const std::shared_ptr<FileEntry>& fileEntry, RequestGroup* requestGroup,
62     DownloadEngine* e)
63     : AbstractCommand(cuid, req, fileEntry, requestGroup, e)
64 {
65   setTimeout(std::chrono::seconds(getOption()->getAsInt(PREF_DNS_TIMEOUT)));
66   // give a chance to be executed in the next loop in DownloadEngine
67   setStatus(Command::STATUS_ONESHOT_REALTIME);
68   disableReadCheckSocket();
69   disableWriteCheckSocket();
70 }
71 
72 InitiateConnectionCommand::~InitiateConnectionCommand() = default;
73 
executeInternal()74 bool InitiateConnectionCommand::executeInternal()
75 {
76   std::string hostname;
77   uint16_t port;
78   std::shared_ptr<Request> proxyRequest = createProxyRequest();
79   if (!proxyRequest) {
80     hostname = getRequest()->getHost();
81     port = getRequest()->getPort();
82   }
83   else {
84     hostname = proxyRequest->getHost();
85     port = proxyRequest->getPort();
86   }
87   std::vector<std::string> addrs;
88   std::string ipaddr = resolveHostname(addrs, hostname, port);
89   if (ipaddr.empty()) {
90     addCommandSelf();
91     return false;
92   }
93   try {
94     auto c = createNextCommand(hostname, ipaddr, port, addrs, proxyRequest);
95     c->setStatus(Command::STATUS_ONESHOT_REALTIME);
96     getDownloadEngine()->setNoWait(true);
97     getDownloadEngine()->addCommand(std::move(c));
98     return true;
99   }
100   catch (RecoverableException& ex) {
101     // Catch exception and retry another address.
102     // See also AbstractCommand::checkIfConnectionEstablished
103 
104     // TODO ipaddr might not be used if pooled socket was found.
105     getDownloadEngine()->markBadIPAddress(hostname, ipaddr, port);
106     if (!getDownloadEngine()->findCachedIPAddress(hostname, port).empty()) {
107       A2_LOG_INFO_EX(EX_EXCEPTION_CAUGHT, ex);
108       A2_LOG_INFO(
109           fmt(MSG_CONNECT_FAILED_AND_RETRY, getCuid(), ipaddr.c_str(), port));
110       auto command =
111           InitiateConnectionCommandFactory::createInitiateConnectionCommand(
112               getCuid(), getRequest(), getFileEntry(), getRequestGroup(),
113               getDownloadEngine());
114       getDownloadEngine()->setNoWait(true);
115       getDownloadEngine()->addCommand(std::move(command));
116       return true;
117     }
118     getDownloadEngine()->removeCachedIPAddress(hostname, port);
119     throw;
120   }
121 }
122 
setConnectedAddrInfo(const std::shared_ptr<Request> & req,const std::string & hostname,const std::shared_ptr<SocketCore> & socket)123 void InitiateConnectionCommand::setConnectedAddrInfo(
124     const std::shared_ptr<Request>& req, const std::string& hostname,
125     const std::shared_ptr<SocketCore>& socket)
126 {
127   auto endpoint = socket->getPeerInfo();
128   req->setConnectedAddrInfo(hostname, endpoint.addr, endpoint.port);
129 }
130 
131 std::shared_ptr<BackupConnectInfo>
createBackupIPv4ConnectCommand(const std::string & hostname,const std::string & ipaddr,uint16_t port,Command * mainCommand)132 InitiateConnectionCommand::createBackupIPv4ConnectCommand(
133     const std::string& hostname, const std::string& ipaddr, uint16_t port,
134     Command* mainCommand)
135 {
136   // Prepare IPv4 backup connection attempt in "Happy Eyeballs"
137   // fashion.
138   std::shared_ptr<BackupConnectInfo> info;
139   char buf[sizeof(in6_addr)];
140   if (inetPton(AF_INET6, ipaddr.c_str(), &buf) == -1) {
141     return info;
142   }
143   A2_LOG_INFO("Searching IPv4 address for backup connection attempt");
144   std::vector<std::string> addrs;
145   getDownloadEngine()->findAllCachedIPAddresses(std::back_inserter(addrs),
146                                                 hostname, port);
147   for (std::vector<std::string>::const_iterator i = addrs.begin(),
148                                                 eoi = addrs.end();
149        i != eoi; ++i) {
150     if (inetPton(AF_INET, (*i).c_str(), &buf) == 0) {
151       info = std::make_shared<BackupConnectInfo>();
152       auto command = make_unique<BackupIPv4ConnectCommand>(
153           getDownloadEngine()->newCUID(), *i, port, info, mainCommand,
154           getRequestGroup(), getDownloadEngine());
155       A2_LOG_INFO(fmt("Issue backup connection command CUID#%" PRId64
156                       ", addr=%s",
157                       command->getCuid(), (*i).c_str()));
158       getDownloadEngine()->addCommand(std::move(command));
159       return info;
160     }
161   }
162   return info;
163 }
164 
setupBackupConnection(const std::string & hostname,const std::string & addr,uint16_t port,ConnectCommand * c)165 void InitiateConnectionCommand::setupBackupConnection(
166     const std::string& hostname, const std::string& addr, uint16_t port,
167     ConnectCommand* c)
168 {
169   std::shared_ptr<BackupConnectInfo> backupConnectInfo =
170       createBackupIPv4ConnectCommand(hostname, addr, port, c);
171   if (backupConnectInfo) {
172     c->setBackupConnectInfo(backupConnectInfo);
173   }
174 }
175 
176 } // namespace aria2
177