Files
znc/DCCBounce.cpp
psychon ade1ee5418 Add a new DCCVHost config option
If this option is set to an ip address, this one is used as the local address
for DCC connections. This can e.g. be used to "fix" DCC bouncing with ipv6
connections. Without it, this just more or less fails badly.


git-svn-id: https://znc.svn.sourceforge.net/svnroot/znc/trunk@1647 726aef4b-f618-498e-8847-2d620e286838
2009-10-10 12:45:25 +00:00

216 lines
6.1 KiB
C++

/*
* Copyright (C) 2004-2009 See the AUTHORS file for details.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include "DCCBounce.h"
#include "User.h"
#include "znc.h"
// If we buffer more than this in memory, we will throttle the receiving side
const unsigned int CDCCBounce::m_uiMaxDCCBuffer = 10 * 1024;
// If less than this is in the buffer, the receiving side continues
const unsigned int CDCCBounce::m_uiMinDCCBuffer = 2 * 1024;
CDCCBounce::CDCCBounce(CUser* pUser, unsigned long uLongIP, unsigned short uPort,
const CString& sFileName, const CString& sRemoteNick,
const CString& sRemoteIP, bool bIsChat) : CZNCSock() {
m_uRemotePort = uPort;
m_sConnectIP = CUtils::GetIP(uLongIP);
m_sRemoteIP = sRemoteIP;
m_sFileName = sFileName;
m_sRemoteNick = sRemoteNick;
m_pUser = pUser;
m_bIsChat = bIsChat;
m_sLocalIP = pUser->GetLocalDCCIP();
m_pPeer = NULL;
m_bIsRemote = false;
if (bIsChat) {
EnableReadLine();
}
m_pUser->AddDCCBounce(this);
}
CDCCBounce::CDCCBounce(const CString& sHostname, unsigned short uPort, CUser* pUser,
const CString& sRemoteNick, const CString& sRemoteIP, const CString& sFileName,
int iTimeout, bool bIsChat) : CZNCSock(sHostname, uPort, iTimeout) {
m_uRemotePort = 0;
m_bIsChat = bIsChat;
m_pUser = pUser;
m_pPeer = NULL;
m_sRemoteNick = sRemoteNick;
m_sFileName = sFileName;
m_sRemoteIP = sRemoteIP;
m_bIsRemote = false;
SetMaxBufferThreshold(10240);
if (bIsChat) {
EnableReadLine();
}
m_pUser->AddDCCBounce(this);
}
CDCCBounce::~CDCCBounce() {
if (m_pPeer) {
m_pPeer->Shutdown();
m_pPeer = NULL;
}
m_pUser->AddBytesRead(GetBytesRead());
m_pUser->AddBytesWritten(GetBytesWritten());
m_pUser->DelDCCBounce(this);
}
void CDCCBounce::ReadLine(const CString& sData) {
CString sLine = sData.TrimRight_n("\r\n");
DEBUG(GetSockName() << " <- [" << sLine << "]");
PutPeer(sLine);
}
void CDCCBounce::ReachedMaxBuffer() {
DEBUG(GetSockName() << " == ReachedMaxBuffer()");
CString sType = (m_bIsChat) ? "Chat" : "Xfer";
m_pUser->PutStatus("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Too long line received");
Close();
}
void CDCCBounce::ReadData(const char* data, int len) {
size_t BufLen;
if (m_pPeer) {
m_pPeer->Write(data, len);
BufLen = m_pPeer->GetInternalWriteBuffer().length();
if (BufLen >= m_uiMaxDCCBuffer) {
DEBUG(GetSockName() << " The send buffer is over the "
"limit (" << BufLen <<"), throttling");
PauseRead();
}
}
}
void CDCCBounce::ReadPaused() {
if (!m_pPeer || m_pPeer->GetInternalWriteBuffer().length() <= m_uiMinDCCBuffer)
UnPauseRead();
}
void CDCCBounce::Timeout() {
DEBUG(GetSockName() << " == Timeout()");
CString sType = (m_bIsChat) ? "Chat" : "Xfer";
if (IsRemote()) {
CString sHost = Csock::GetHostName();
if (!sHost.empty()) {
sHost = " to [" + sHost + ":" + CString(Csock::GetPort()) + "]";
} else {
sHost = ".";
}
m_pUser->PutStatus("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Timeout while connecting" + sHost);
} else {
m_pUser->PutStatus("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Timeout waiting for incoming connection [" + Csock::GetLocalIP() + ":" + CString(Csock::GetLocalPort()) + "]");
}
}
void CDCCBounce::ConnectionRefused() {
DEBUG(GetSockName() << " == ConnectionRefused()");
CString sType = (m_bIsChat) ? "Chat" : "Xfer";
CString sHost = Csock::GetHostName();
if (!sHost.empty()) {
sHost = " to [" + sHost + ":" + CString(Csock::GetPort()) + "]";
} else {
sHost = ".";
}
m_pUser->PutStatus("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Connection Refused while connecting" + sHost);
}
void CDCCBounce::SockError(int iErrno) {
DEBUG(GetSockName() << " == SockError(" << iErrno << ")");
CString sType = (m_bIsChat) ? "Chat" : "Xfer";
if (IsRemote()) {
CString sHost = Csock::GetHostName();
if (!sHost.empty()) {
sHost = "[" + sHost + ":" + CString(Csock::GetPort()) + "]";
}
m_pUser->PutStatus("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Socket error [" + CString(strerror(iErrno)) + "]" + sHost);
} else {
m_pUser->PutStatus("DCC " + sType + " Bounce (" + m_sRemoteNick + "): Socket error [" + CString(strerror(iErrno)) + "] [" + Csock::GetLocalIP() + ":" + CString(Csock::GetLocalPort()) + "]");
}
}
void CDCCBounce::Connected() {
DEBUG(GetSockName() << " == Connected()");
SetTimeout(0);
}
void CDCCBounce::Disconnected() {
DEBUG(GetSockName() << " == Disconnected()");
}
void CDCCBounce::Shutdown() {
m_pPeer = NULL;
DEBUG(GetSockName() << " == Close(); because my peer told me to");
Close();
}
Csock* CDCCBounce::GetSockObj(const CString& sHost, unsigned short uPort) {
Close();
if (m_sRemoteIP.empty()) {
m_sRemoteIP = sHost;
}
CDCCBounce* pSock = new CDCCBounce(sHost, uPort, m_pUser, m_sRemoteNick, m_sRemoteIP, m_sFileName, m_bIsChat);
CDCCBounce* pRemoteSock = new CDCCBounce(sHost, uPort, m_pUser, m_sRemoteNick, m_sRemoteIP, m_sFileName, m_bIsChat);
pSock->SetPeer(pRemoteSock);
pRemoteSock->SetPeer(pSock);
pRemoteSock->SetRemote(true);
pSock->SetRemote(false);
if (!CZNC::Get().GetManager().Connect(m_sConnectIP, m_uRemotePort, "DCC::" + CString((m_bIsChat) ? "Chat" : "XFER") + "::Remote::" + m_sRemoteNick, 60, false, m_sLocalIP, pRemoteSock)) {
pRemoteSock->Close();
}
pSock->SetSockName(GetSockName());
pSock->SetTimeout(0);
return pSock;
}
void CDCCBounce::PutServ(const CString& sLine) {
DEBUG(GetSockName() << " -> [" << sLine << "]");
Write(sLine + "\r\n");
}
void CDCCBounce::PutPeer(const CString& sLine) {
if (m_pPeer) {
m_pPeer->PutServ(sLine);
} else {
PutServ("*** Not connected yet ***");
}
}
unsigned short CDCCBounce::DCCRequest(const CString& sNick, unsigned long uLongIP, unsigned short uPort, const CString& sFileName, bool bIsChat, CUser* pUser, const CString& sRemoteIP) {
CDCCBounce* pDCCBounce = new CDCCBounce(pUser, uLongIP, uPort, sFileName, sNick, sRemoteIP, bIsChat);
unsigned short uListenPort = CZNC::Get().GetManager().ListenRand("DCC::" + CString((bIsChat) ? "Chat" : "Xfer") + "::Local::" + sNick,
pUser->GetLocalDCCIP(), false, SOMAXCONN, pDCCBounce, 120);
return uListenPort;
}