1 /*
2  * Copyright (C) 2001-2012 Jacek Sieka, arnetheduck on gmail point com
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #include "stdinc.h"
20 
21 #include "ConnectivityManager.h"
22 #include "SettingsManager.h"
23 #include "ClientManager.h"
24 #include "ConnectionManager.h"
25 #include "SearchManager.h"
26 #include "LogManager.h"
27 #include "UPnPManager.h"
28 #ifdef WITH_DHT
29 #include "dht/DHT.h"
30 #endif
31 
32 namespace dcpp {
33 
ConnectivityManager()34 ConnectivityManager::ConnectivityManager() :
35 autoDetected(false),
36 running(false)
37 {
38     updateLast();
39 }
40 
startSocket()41 void ConnectivityManager::startSocket() {
42    autoDetected = false;
43 
44    disconnect();
45 
46    if(ClientManager::getInstance()->isActive()) {
47        listen();
48 
49     // must be done after listen calls; otherwise ports won't be set
50     if(SETTING(INCOMING_CONNECTIONS) == SettingsManager::INCOMING_FIREWALL_UPNP)
51        UPnPManager::getInstance()->open();
52     }
53 
54     updateLast();
55 }
56 
detectConnection()57 void ConnectivityManager::detectConnection() {
58     if (running)
59         return;
60 
61     running = true;
62 
63     //fire(ConnectivityManagerListener::Started());
64 
65     // restore connectivity settings to their default value.
66     SettingsManager::getInstance()->unset(SettingsManager::TCP_PORT);
67     SettingsManager::getInstance()->unset(SettingsManager::UDP_PORT);
68     SettingsManager::getInstance()->unset(SettingsManager::TLS_PORT);
69     SettingsManager::getInstance()->unset(SettingsManager::EXTERNAL_IP);
70     SettingsManager::getInstance()->unset(SettingsManager::NO_IP_OVERRIDE);
71     //SettingsManager::getInstance()->unset(SettingsManager::MAPPER);
72     SettingsManager::getInstance()->unset(SettingsManager::BIND_ADDRESS);
73 
74    if (UPnPManager::getInstance()->getOpened()) {
75        UPnPManager::getInstance()->close();
76    }
77 
78    disconnect();
79 
80    log(_("Determining the best connectivity settings..."));
81    try {
82         listen();
83    } catch(const Exception& e) {
84         SettingsManager::getInstance()->set(SettingsManager::INCOMING_CONNECTIONS, SettingsManager::INCOMING_FIREWALL_PASSIVE);
85         log(str(F_("Unable to open %1% port(s); connectivity settings must be configured manually") % e.getError()));
86         fire(ConnectivityManagerListener::Finished());
87         running = false;
88         return;
89    }
90 
91    autoDetected = true;
92 
93    if (!Util::isPrivateIp(Util::getLocalIp(AF_INET))) {
94        SettingsManager::getInstance()->set(SettingsManager::INCOMING_CONNECTIONS, SettingsManager::INCOMING_DIRECT);
95        log(_("Public IP address detected, selecting active mode with direct connection"));
96        fire(ConnectivityManagerListener::Finished());
97         running = false;
98         return;
99    }
100 
101    SettingsManager::getInstance()->set(SettingsManager::INCOMING_CONNECTIONS, SettingsManager::INCOMING_FIREWALL_UPNP);
102    log(_("Local network with possible NAT detected, trying to map the ports using UPnP..."));
103 
104     if (!UPnPManager::getInstance()->open()) {
105         running = false;
106     }
107 }
108 
setup(bool settingsChanged)109 void ConnectivityManager::setup(bool settingsChanged) {
110    if(BOOLSETTING(AUTO_DETECT_CONNECTION)) {
111        if (!autoDetected) detectConnection();
112    } else {
113         if(autoDetected || (settingsChanged && (SearchManager::getInstance()->getPort() != SETTING(UDP_PORT) || ConnectionManager::getInstance()->getPort() != SETTING(TCP_PORT) || ConnectionManager::getInstance()->getSecurePort() != SETTING(TLS_PORT) || SETTING(BIND_ADDRESS) != lastBind))) {
114            if(settingsChanged || SETTING(INCOMING_CONNECTIONS) != SettingsManager::INCOMING_FIREWALL_UPNP) {
115                UPnPManager::getInstance()->close();
116            }
117            startSocket();
118        } else if(SETTING(INCOMING_CONNECTIONS) == SettingsManager::INCOMING_FIREWALL_UPNP && !UPnPManager::getInstance()->getOpened()) {
119            // previous UPnP mappings had failed; try again
120            UPnPManager::getInstance()->open();
121        }
122    }
123 }
124 
mappingFinished(bool success)125 void ConnectivityManager::mappingFinished(bool success) {
126     if(BOOLSETTING(AUTO_DETECT_CONNECTION)) {
127         if (!success) {
128             disconnect();
129             SettingsManager::getInstance()->set(SettingsManager::INCOMING_CONNECTIONS, SettingsManager::INCOMING_FIREWALL_PASSIVE);
130             log(_("Automatic setup of active mode has failed. You may want to set up your connection manually for better connectivity"));
131         }
132         fire(ConnectivityManagerListener::Finished());
133     }
134 
135     running = false;
136 }
137 
listen()138 void ConnectivityManager::listen() {
139     try {
140         ConnectionManager::getInstance()->listen();
141     } catch(const Exception&) {
142         throw Exception(_("Transfer (TCP)"));
143     }
144 
145     try {
146         SearchManager::getInstance()->listen();
147     } catch(const Exception&) {
148         throw Exception(_("Search (UDP)"));
149     }
150 #ifdef WITH_DHT
151     try {
152         dht::DHT::getInstance()->start();
153     } catch (const Exception&) {
154         throw Exception(_("DHT (UDP)"));
155     }
156 #endif
157 }
158 
disconnect()159 void ConnectivityManager::disconnect() {
160     SearchManager::getInstance()->disconnect();
161     ConnectionManager::getInstance()->disconnect();
162 #ifdef WITH_DHT
163     dht::DHT::getInstance()->stop();
164 #endif
165 }
166 
log(const string & message)167 void ConnectivityManager::log(const string& message) {
168    if(BOOLSETTING(AUTO_DETECT_CONNECTION)) {
169        LogManager::getInstance()->message(_("Connectivity: ") + message);
170        fire(ConnectivityManagerListener::Message(), message);
171    } else {
172        LogManager::getInstance()->message(message);
173    }
174 }
175 
updateLast()176 void ConnectivityManager::updateLast() {
177     lastTcp = (unsigned short)SETTING(TCP_PORT);
178     lastUdp = (unsigned short)SETTING(UDP_PORT);
179     lastTls = (unsigned short)(SETTING(TLS_PORT));
180     lastConn = SETTING(INCOMING_CONNECTIONS);
181     lastBind = SETTING(BIND_ADDRESS);
182     //lastMapper = SETTING(MAPPER);
183 }
184 
185 } // namespace dcpp
186