1//
2// This file is part of the aMule Project.
3//
4// Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
5// Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
6//
7// Any parts of this program derived from the xMule, lMule or eMule project,
8// or contributed by third-party developers are copyrighted by their
9// respective authors.
10//
11// This program is free software; you can redistribute it and/or modify
12// it under the terms of the GNU General Public License as published by
13// the Free Software Foundation; either version 2 of the License, or
14// (at your option) any later version.
15//
16// This program is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19// GNU General Public License for more details.
20//
21// You should have received a copy of the GNU General Public License
22// along with this program; if not, write to the Free Software
23// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
24//
25
26#include "ListenSocket.h"	// Interface declarations
27
28#include <common/EventIDs.h>
29
30#include "ClientTCPSocket.h"	// Needed for CClientRequestSocket
31#include "Logger.h"			// Needed for AddLogLineM
32#include "Statistics.h"		// Needed for theStats
33#include "Preferences.h"	// Needed for CPreferences
34#include "amule.h"		// Needed for theApp
35#include "ServerConnect.h"	// Needed for CServerConnect
36
37//-----------------------------------------------------------------------------
38// CListenSocket
39//-----------------------------------------------------------------------------
40//
41// This is the socket that listens to incomming connections in aMule's TCP port
42// As soon as a connection is detected, it creates a new socket of type
43// CClientTCPSocket to handle (accept) the connection.
44//
45
46CListenSocket::CListenSocket(wxIPaddress &addr, const CProxyData *ProxyData)
47:
48// wxSOCKET_NOWAIT    - means non-blocking i/o
49// wxSOCKET_REUSEADDR - means we can reuse the socket imediately (wx-2.5.3)
50CSocketServerProxy(addr, wxSOCKET_NOWAIT|wxSOCKET_REUSEADDR, ProxyData)
51{
52	// 0.42e - vars not used by us
53	bListening = false;
54	shutdown = false;
55	m_OpenSocketsInterval = 0;
56	m_nPeningConnections = 0;
57	totalconnectionchecks = 0;
58	averageconnections = 0.0;
59	// Set the listen socket event handler -- The handler is written in amule.cpp
60	if (Ok()) {
61 		SetEventHandler(*theApp, ID_LISTENSOCKET_EVENT);
62 		SetNotify(wxSOCKET_CONNECTION_FLAG);
63 		Notify(true);
64
65		AddLogLineNS(_("ListenSocket: Ok."));
66	} else {
67		AddLogLineCS(_("ERROR: Could not listen to TCP port.") );
68	}
69}
70
71
72CListenSocket::~CListenSocket()
73{
74	shutdown = true;
75	Discard();
76	Close();
77
78#ifdef __DEBUG__
79	// No new sockets should have been opened by now
80	for (SocketSet::iterator it = socket_list.begin(); it != socket_list.end(); it++) {
81		wxASSERT((*it)->OnDestroy());
82	}
83#endif
84
85	KillAllSockets();
86}
87
88
89bool CListenSocket::StartListening()
90{
91	// 0.42e
92	bListening = true;
93
94	return true;
95}
96
97void CListenSocket::ReStartListening()
98{
99	// 0.42e
100	bListening = true;
101	if (m_nPeningConnections) {
102		m_nPeningConnections--;
103		OnAccept(0);
104	}
105}
106
107void CListenSocket::StopListening()
108{
109	// 0.42e
110	bListening = false;
111	theStats::AddMaxConnectionLimitReached();
112}
113
114void CListenSocket::OnAccept(int nErrorCode)
115{
116	// 0.42e
117	if (!nErrorCode) {
118		m_nPeningConnections++;
119		if (m_nPeningConnections < 1) {
120			wxFAIL;
121			m_nPeningConnections = 1;
122		}
123		if (TooManySockets(true) && !theApp->serverconnect->IsConnecting()) {
124			StopListening();
125			return;
126		} else if (bListening == false) {
127			// If the client is still at maxconnections,
128			// this will allow it to go above it ...
129			// But if you don't, you will get a lowID on all servers.
130			ReStartListening();
131		}
132		// Deal with the pending connections, there might be more than one, due to
133		// the StopListening() call above.
134		while (m_nPeningConnections) {
135			m_nPeningConnections--;
136			// Create a new socket to deal with the connection
137			CClientTCPSocket* newclient = new CClientTCPSocket();
138			// Accept the connection and give it to the newly created socket
139			if (!AcceptWith(*newclient, false)) {
140				newclient->Safe_Delete();
141			} else {
142				wxASSERT(theApp->IsRunning());
143				if (!newclient->InitNetworkData()) {
144					// IP or port were not returned correctly
145					// from the accepted address, or filtered.
146					newclient->Safe_Delete();
147				}
148			}
149		}
150	}
151}
152
153void CListenSocket::AddConnection()
154{
155	m_OpenSocketsInterval++;
156}
157
158void CListenSocket::Process()
159{
160	// 042e + Kry changes for Destroy
161	m_OpenSocketsInterval = 0;
162	SocketSet::iterator it = socket_list.begin();
163	while ( it != socket_list.end() ) {
164		CClientTCPSocket* cur_socket = *it++;
165		if (!cur_socket->OnDestroy()) {
166			if (cur_socket->ForDeletion()) {
167				cur_socket->Destroy();
168			} else {
169				cur_socket->CheckTimeOut();
170			}
171		}
172	}
173
174	if ((GetOpenSockets()+5 < thePrefs::GetMaxConnections() || theApp->serverconnect->IsConnecting()) && !bListening) {
175		ReStartListening();
176	}
177}
178
179void CListenSocket::RecalculateStats()
180{
181	// 0.42e
182	memset(m_ConnectionStates,0,6);
183	for (SocketSet::iterator it = socket_list.begin(); it != socket_list.end(); ) {
184		CClientTCPSocket* cur_socket = *it++;
185		switch (cur_socket->GetConState()) {
186			case ES_DISCONNECTED:
187				m_ConnectionStates[0]++;
188				break;
189			case ES_NOTCONNECTED:
190				m_ConnectionStates[1]++;
191				break;
192			case ES_CONNECTED:
193				m_ConnectionStates[2]++;
194				break;
195		}
196	}
197}
198
199void CListenSocket::AddSocket(CClientTCPSocket* toadd)
200{
201	wxASSERT(toadd);
202	socket_list.insert(toadd);
203	theStats::AddActiveConnection();
204}
205
206void CListenSocket::RemoveSocket(CClientTCPSocket* todel)
207{
208	wxASSERT(todel);
209	socket_list.erase(todel);
210	theStats::RemoveActiveConnection();
211}
212
213void CListenSocket::KillAllSockets()
214{
215	// 0.42e reviewed - they use delete, but our safer is Destroy...
216	// But I bet it would be better to call Safe_Delete on the socket.
217	// Update: no... Safe_Delete MARKS for deletion. We need to delete it.
218	for (SocketSet::iterator it = socket_list.begin(); it != socket_list.end(); ) {
219		CClientTCPSocket* cur_socket = *it++;
220		if (cur_socket->GetClient()) {
221			cur_socket->Safe_Delete_Client();
222		} else {
223			cur_socket->Safe_Delete();
224			cur_socket->Destroy();
225		}
226	}
227}
228
229bool CListenSocket::TooManySockets(bool bIgnoreInterval)
230{
231	if (GetOpenSockets() > thePrefs::GetMaxConnections() || (m_OpenSocketsInterval > (thePrefs::GetMaxConperFive()*GetMaxConperFiveModifier()) && !bIgnoreInterval)) {
232		return true;
233	} else {
234		return false;
235	}
236}
237
238bool CListenSocket::IsValidSocket(CClientTCPSocket* totest)
239{
240	// 0.42e
241	return socket_list.find(totest) != socket_list.end();
242}
243
244
245void CListenSocket::UpdateConnectionsStatus()
246{
247	// 0.42e xcept for the khaos stats
248	if( theApp->IsConnected() ) {
249		totalconnectionchecks++;
250		float percent;
251		percent = (float)(totalconnectionchecks-1)/(float)totalconnectionchecks;
252		if( percent > .99f ) {
253			percent = .99f;
254		}
255		averageconnections = (averageconnections*percent) + (float)GetOpenSockets()*(1.0f-percent);
256	}
257}
258
259
260float CListenSocket::GetMaxConperFiveModifier()
261{
262	float SpikeSize = GetOpenSockets() - averageconnections;
263	if ( SpikeSize < 1 ) {
264		return 1;
265	}
266
267	float SpikeTolerance = 2.5f*thePrefs::GetMaxConperFive();
268	if ( SpikeSize > SpikeTolerance ) {
269		return 0;
270	}
271
272	return 1.0f - (SpikeSize/SpikeTolerance);
273}
274// File_checked_for_headers
275