2
* Copyright (C) 2004-2011 See the AUTHORS file for details.
4
* This program is free software; you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 2 as published
6
* by the Free Software Foundation.
14
#include "FileUtils.h"
18
class CDCCBounce : public CSocket {
20
CDCCBounce(CBounceDCCMod* pMod, unsigned long uLongIP, unsigned short uPort,
21
const CString& sFileName, const CString& sRemoteNick,
22
const CString& sRemoteIP, bool bIsChat = false);
23
CDCCBounce(CBounceDCCMod* pMod, const CString& sHostname, unsigned short uPort,
24
const CString& sRemoteNick, const CString& sRemoteIP,
25
const CString& sFileName, int iTimeout = 60, bool bIsChat = false);
26
virtual ~CDCCBounce();
28
static unsigned short DCCRequest(const CString& sNick, unsigned long uLongIP, unsigned short uPort, const CString& sFileName, bool bIsChat, CBounceDCCMod* pMod, const CString& sRemoteIP);
30
void ReadLine(const CString& sData);
31
virtual void ReadData(const char* data, size_t len);
32
virtual void ReadPaused();
33
virtual void Timeout();
34
virtual void ConnectionRefused();
35
virtual void ReachedMaxBuffer();
36
virtual void SockError(int iErrno);
37
virtual void Connected();
38
virtual void Disconnected();
39
virtual Csock* GetSockObj(const CString& sHost, unsigned short uPort);
41
void PutServ(const CString& sLine);
42
void PutPeer(const CString& sLine);
43
bool IsPeerConnected() { return (m_pPeer) ? m_pPeer->IsConnected() : false; }
46
void SetPeer(CDCCBounce* p) { m_pPeer = p; }
47
void SetRemoteIP(const CString& s) { m_sRemoteIP = s; }
48
void SetRemoteNick(const CString& s) { m_sRemoteNick = s; }
49
void SetRemote(bool b) { m_bIsRemote = b; }
53
unsigned short GetUserPort() const { return m_uRemotePort; }
54
const CString& GetRemoteIP() const { return m_sRemoteIP; }
55
const CString& GetRemoteNick() const { return m_sRemoteNick; }
56
const CString& GetFileName() const { return m_sFileName; }
57
CDCCBounce* GetPeer() const { return m_pPeer; }
58
bool IsRemote() { return m_bIsRemote; }
59
bool IsChat() { return m_bIsChat; }
63
CString m_sRemoteNick;
68
CBounceDCCMod* m_pModule;
70
unsigned short m_uRemotePort;
74
static const unsigned int m_uiMaxDCCBuffer;
75
static const unsigned int m_uiMinDCCBuffer;
78
// If we buffer more than this in memory, we will throttle the receiving side
79
const unsigned int CDCCBounce::m_uiMaxDCCBuffer = 10 * 1024;
80
// If less than this is in the buffer, the receiving side continues
81
const unsigned int CDCCBounce::m_uiMinDCCBuffer = 2 * 1024;
83
class CBounceDCCMod : public CModule {
85
void ListDCCsCommand(const CString& sLine) {
87
Table.AddColumn("Type");
88
Table.AddColumn("State");
89
Table.AddColumn("Speed");
90
Table.AddColumn("Nick");
91
Table.AddColumn("IP");
92
Table.AddColumn("File");
94
set<CSocket*>::const_iterator it;
95
for (it = BeginSockets(); it != EndSockets(); ++it) {
96
CDCCBounce* pSock = (CDCCBounce*) *it;
97
CString sSockName = pSock->GetSockName();
99
if (!(pSock->IsRemote())) {
101
Table.SetCell("Nick", pSock->GetRemoteNick());
102
Table.SetCell("IP", pSock->GetRemoteIP());
104
if (pSock->IsChat()) {
105
Table.SetCell("Type", "Chat");
107
Table.SetCell("Type", "Xfer");
108
Table.SetCell("File", pSock->GetFileName());
111
CString sState = "Waiting";
112
if ((pSock->IsConnected()) || (pSock->IsPeerConnected())) {
114
if ((pSock->IsPeerConnected()) && (pSock->IsPeerConnected())) {
115
sState = "Connected";
118
Table.SetCell("State", sState);
122
if (PutModule(Table) == 0) {
123
PutModule("You have no active DCCs.");
127
void UseClientIPCommand(const CString& sLine) {
128
CString sValue = sLine.Token(1, true);
130
if (!sValue.empty()) {
131
SetNV("UseClientIP", sValue);
134
PutModule("UseClientIP: " + CString(GetNV("UseClientIP").ToBool()));
137
MODCONSTRUCTOR(CBounceDCCMod) {
139
AddCommand("ListDCCs", static_cast<CModCommand::ModCmdFunc>(&CBounceDCCMod::ListDCCsCommand),
140
"", "List all active DCCs");
141
AddCommand("UseClientIP", static_cast<CModCommand::ModCmdFunc>(&CBounceDCCMod::UseClientIPCommand),
145
virtual ~CBounceDCCMod() {}
147
CString GetLocalDCCIP() {
148
return m_pUser->GetLocalDCCIP();
152
return GetNV("UseClientIP").ToBool();
155
virtual EModRet OnUserCTCP(CString& sTarget, CString& sMessage) {
156
if (sMessage.Equals("DCC ", false, 4)) {
157
CString sType = sMessage.Token(1);
158
CString sFile = sMessage.Token(2);
159
unsigned long uLongIP = sMessage.Token(3).ToULong();
160
unsigned short uPort = sMessage.Token(4).ToUShort();
161
unsigned long uFileSize = sMessage.Token(5).ToULong();
162
CString sIP = GetLocalDCCIP();
164
if (!UseClientIP()) {
165
uLongIP = CUtils::GetLongIP(m_pClient->GetRemoteIP());
168
if (sType.Equals("CHAT")) {
169
unsigned short uBNCPort = CDCCBounce::DCCRequest(sTarget, uLongIP, uPort, "", true, this, "");
171
PutIRC("PRIVMSG " + sTarget + " :\001DCC CHAT chat " + CString(CUtils::GetLongIP(sIP)) + " " + CString(uBNCPort) + "\001");
173
} else if (sType.Equals("SEND")) {
174
// DCC SEND readme.txt 403120438 5550 1104
175
unsigned short uBNCPort = CDCCBounce::DCCRequest(sTarget, uLongIP, uPort, sFile, false, this, "");
177
PutIRC("PRIVMSG " + sTarget + " :\001DCC SEND " + sFile + " " + CString(CUtils::GetLongIP(sIP)) + " " + CString(uBNCPort) + " " + CString(uFileSize) + "\001");
179
} else if (sType.Equals("RESUME")) {
180
// PRIVMSG user :DCC RESUME "znc.o" 58810 151552
181
unsigned short uResumePort = sMessage.Token(3).ToUShort();
183
set<CSocket*>::const_iterator it;
184
for (it = BeginSockets(); it != EndSockets(); ++it) {
185
CDCCBounce* pSock = (CDCCBounce*) *it;
187
if (pSock->GetLocalPort() == uResumePort) {
188
PutIRC("PRIVMSG " + sTarget + " :\001DCC " + sType + " " + sFile + " " + CString(pSock->GetUserPort()) + " " + sMessage.Token(4) + "\001");
191
} else if (sType.Equals("ACCEPT")) {
192
// Need to lookup the connection by port, filter the port, and forward to the user
194
set<CSocket*>::const_iterator it;
195
for (it = BeginSockets(); it != EndSockets(); ++it) {
196
CDCCBounce* pSock = (CDCCBounce*) *it;
197
if (pSock->GetUserPort() == sMessage.Token(3).ToUShort()) {
198
PutIRC("PRIVMSG " + sTarget + " :\001DCC " + sType + " " + sFile + " " + CString(pSock->GetLocalPort()) + " " + sMessage.Token(4) + "\001");
209
virtual EModRet OnPrivCTCP(CNick& Nick, CString& sMessage) {
210
if (sMessage.Equals("DCC ", false, 4) && m_pUser->IsUserAttached()) {
211
// DCC CHAT chat 2453612361 44592
212
CString sType = sMessage.Token(1);
213
CString sFile = sMessage.Token(2);
214
unsigned long uLongIP = sMessage.Token(3).ToULong();
215
unsigned short uPort = sMessage.Token(4).ToUShort();
216
unsigned long uFileSize = sMessage.Token(5).ToULong();
218
if (sType.Equals("CHAT")) {
219
CNick FromNick(Nick.GetNickMask());
220
unsigned short uBNCPort = CDCCBounce::DCCRequest(FromNick.GetNick(), uLongIP, uPort, "", true, this, CUtils::GetIP(uLongIP));
222
CString sIP = GetLocalDCCIP();
223
m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + m_pUser->GetNick() + " :\001DCC CHAT chat " + CString(CUtils::GetLongIP(sIP)) + " " + CString(uBNCPort) + "\001");
225
} else if (sType.Equals("SEND")) {
226
// DCC SEND readme.txt 403120438 5550 1104
227
unsigned short uBNCPort = CDCCBounce::DCCRequest(Nick.GetNick(), uLongIP, uPort, sFile, false, this, CUtils::GetIP(uLongIP));
229
CString sIP = GetLocalDCCIP();
230
m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + m_pUser->GetNick() + " :\001DCC SEND " + sFile + " " + CString(CUtils::GetLongIP(sIP)) + " " + CString(uBNCPort) + " " + CString(uFileSize) + "\001");
232
} else if (sType.Equals("RESUME")) {
233
// Need to lookup the connection by port, filter the port, and forward to the user
234
unsigned short uResumePort = sMessage.Token(3).ToUShort();
236
set<CSocket*>::const_iterator it;
237
for (it = BeginSockets(); it != EndSockets(); ++it) {
238
CDCCBounce* pSock = (CDCCBounce*) *it;
240
if (pSock->GetLocalPort() == uResumePort) {
241
m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + m_pClient->GetNick() + " :\001DCC " + sType + " " + sFile + " " + CString(pSock->GetUserPort()) + " " + sMessage.Token(4) + "\001");
244
} else if (sType.Equals("ACCEPT")) {
245
// Need to lookup the connection by port, filter the port, and forward to the user
246
set<CSocket*>::const_iterator it;
247
for (it = BeginSockets(); it != EndSockets(); ++it) {
248
CDCCBounce* pSock = (CDCCBounce*) *it;
250
if (pSock->GetUserPort() == sMessage.Token(3).ToUShort()) {
251
m_pUser->PutUser(":" + Nick.GetNickMask() + " PRIVMSG " + m_pClient->GetNick() + " :\001DCC " + sType + " " + sFile + " " + CString(pSock->GetLocalPort()) + " " + sMessage.Token(4) + "\001");
263
CDCCBounce::CDCCBounce(CBounceDCCMod* pMod, unsigned long uLongIP, unsigned short uPort,
264
const CString& sFileName, const CString& sRemoteNick,
265
const CString& sRemoteIP, bool bIsChat) : CSocket(pMod) {
266
m_uRemotePort = uPort;
267
m_sConnectIP = CUtils::GetIP(uLongIP);
268
m_sRemoteIP = sRemoteIP;
269
m_sFileName = sFileName;
270
m_sRemoteNick = sRemoteNick;
273
m_sLocalIP = pMod->GetLocalDCCIP();
284
CDCCBounce::CDCCBounce(CBounceDCCMod* pMod, const CString& sHostname, unsigned short uPort,
285
const CString& sRemoteNick, const CString& sRemoteIP, const CString& sFileName,
286
int iTimeout, bool bIsChat) : CSocket(pMod, sHostname, uPort, iTimeout) {
291
m_sRemoteNick = sRemoteNick;
292
m_sFileName = sFileName;
293
m_sRemoteIP = sRemoteIP;
296
SetMaxBufferThreshold(10240);
304
CDCCBounce::~CDCCBounce() {
311
void CDCCBounce::ReadLine(const CString& sData) {
312
CString sLine = sData.TrimRight_n("\r\n");
314
DEBUG(GetSockName() << " <- [" << sLine << "]");
319
void CDCCBounce::ReachedMaxBuffer() {
320
DEBUG(GetSockName() << " == ReachedMaxBuffer()");
322
CString sType = (m_bIsChat) ? "Chat" : "Xfer";
324
m_pModule->PutModule("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Too long line received");
328
void CDCCBounce::ReadData(const char* data, size_t len) {
330
m_pPeer->Write(data, len);
332
size_t BufLen = m_pPeer->GetInternalWriteBuffer().length();
334
if (BufLen >= m_uiMaxDCCBuffer) {
335
DEBUG(GetSockName() << " The send buffer is over the "
336
"limit (" << BufLen <<"), throttling");
342
void CDCCBounce::ReadPaused() {
343
if (!m_pPeer || m_pPeer->GetInternalWriteBuffer().length() <= m_uiMinDCCBuffer)
347
void CDCCBounce::Timeout() {
348
DEBUG(GetSockName() << " == Timeout()");
349
CString sType = (m_bIsChat) ? "Chat" : "Xfer";
352
CString sHost = Csock::GetHostName();
353
if (!sHost.empty()) {
354
sHost = " to [" + sHost + " " + CString(Csock::GetPort()) + "]";
359
m_pModule->PutModule("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Timeout while connecting" + sHost);
361
m_pModule->PutModule("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Timeout waiting for incoming connection [" + Csock::GetLocalIP() + ":" + CString(Csock::GetLocalPort()) + "]");
365
void CDCCBounce::ConnectionRefused() {
366
DEBUG(GetSockName() << " == ConnectionRefused()");
368
CString sType = (m_bIsChat) ? "Chat" : "Xfer";
369
CString sHost = Csock::GetHostName();
370
if (!sHost.empty()) {
371
sHost = " to [" + sHost + " " + CString(Csock::GetPort()) + "]";
376
m_pModule->PutModule("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Connection Refused while connecting" + sHost);
379
void CDCCBounce::SockError(int iErrno) {
380
DEBUG(GetSockName() << " == SockError(" << iErrno << ")");
381
CString sType = (m_bIsChat) ? "Chat" : "Xfer";
384
CString sHost = Csock::GetHostName();
385
if (!sHost.empty()) {
386
sHost = "[" + sHost + " " + CString(Csock::GetPort()) + "]";
389
m_pModule->PutModule("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Socket error [" + CString(strerror(iErrno)) + "]" + sHost);
391
m_pModule->PutModule("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Socket error [" + CString(strerror(iErrno)) + "] [" + Csock::GetLocalIP() + ":" + CString(Csock::GetLocalPort()) + "]");
395
void CDCCBounce::Connected() {
397
DEBUG(GetSockName() << " == Connected()");
400
void CDCCBounce::Disconnected() {
401
DEBUG(GetSockName() << " == Disconnected()");
404
void CDCCBounce::Shutdown() {
406
DEBUG(GetSockName() << " == Close(); because my peer told me to");
410
Csock* CDCCBounce::GetSockObj(const CString& sHost, unsigned short uPort) {
413
if (m_sRemoteIP.empty()) {
417
CDCCBounce* pSock = new CDCCBounce(m_pModule, sHost, uPort, m_sRemoteNick, m_sRemoteIP, m_sFileName, m_bIsChat);
418
CDCCBounce* pRemoteSock = new CDCCBounce(m_pModule, sHost, uPort, m_sRemoteNick, m_sRemoteIP, m_sFileName, m_bIsChat);
419
pSock->SetPeer(pRemoteSock);
420
pRemoteSock->SetPeer(pSock);
421
pRemoteSock->SetRemote(true);
422
pSock->SetRemote(false);
424
if (!CZNC::Get().GetManager().Connect(m_sConnectIP, m_uRemotePort, "DCC::" + CString((m_bIsChat) ? "Chat" : "XFER") + "::Remote::" + m_sRemoteNick, 60, false, m_sLocalIP, pRemoteSock)) {
425
pRemoteSock->Close();
428
pSock->SetSockName(GetSockName());
432
void CDCCBounce::PutServ(const CString& sLine) {
433
DEBUG(GetSockName() << " -> [" << sLine << "]");
434
Write(sLine + "\r\n");
437
void CDCCBounce::PutPeer(const CString& sLine) {
439
m_pPeer->PutServ(sLine);
441
PutServ("*** Not connected yet ***");
445
unsigned short CDCCBounce::DCCRequest(const CString& sNick, unsigned long uLongIP, unsigned short uPort, const CString& sFileName, bool bIsChat, CBounceDCCMod* pMod, const CString& sRemoteIP) {
446
CDCCBounce* pDCCBounce = new CDCCBounce(pMod, uLongIP, uPort, sFileName, sNick, sRemoteIP, bIsChat);
447
unsigned short uListenPort = CZNC::Get().GetManager().ListenRand("DCC::" + CString((bIsChat) ? "Chat" : "Xfer") + "::Local::" + sNick,
448
pMod->GetLocalDCCIP(), false, SOMAXCONN, pDCCBounce, 120);
455
MODULEDEFS(CBounceDCCMod, "Bounce DCC module")