NATEngineImpl.cpp revision 1a0e2f8a916539973741eb7b5225f74eb723275d
/* $Id$ */
/** @file
* Implementation of INATEngine in VBoxSVC.
*/
/*
* Copyright (C) 2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include "NATEngineImpl.h"
#include "AutoCaller.h"
#include "Logging.h"
#include "MachineImpl.h"
#include "GuestOSTypeImpl.h"
#include <iprt/string.h>
#include <iprt/cpp/utils.h>
#include <VBox/err.h>
#include <VBox/settings.h>
// constructor / destructor
////////////////////////////////////////////////////////////////////////////////
NATEngine::NATEngine():mParent(NULL), mAdapter(NULL){}
NATEngine::~NATEngine(){}
HRESULT NATEngine::FinalConstruct()
{
return S_OK;
}
HRESULT NATEngine::init(Machine *aParent, INetworkAdapter *aAdapter)
{
AutoInitSpan autoInitSpan(this);
AssertReturn(autoInitSpan.isOk(), E_FAIL);
autoInitSpan.setSucceeded();
m_fModified = false;
mData.allocate();
mData->mNetwork.setNull();
mData->mBindIP.setNull();
unconst(mParent) = aParent;
unconst(mAdapter) = aAdapter;
return S_OK;
}
HRESULT NATEngine::init(Machine *aParent, INetworkAdapter *aAdapter, NATEngine *aThat)
{
AutoInitSpan autoInitSpan(this);
AssertReturn(autoInitSpan.isOk(), E_FAIL);
Log(("init that:%p this:%p\n", aThat, this));
AutoCaller thatCaller (aThat);
AssertComRCReturnRC(thatCaller.rc());
AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
mData.share(aThat->mData);
NATRuleMap::iterator it;
mNATRules.clear();
for (it = aThat->mNATRules.begin(); it != aThat->mNATRules.end(); ++it)
{
mNATRules.insert(std::make_pair(it->first, it->second));
}
unconst(mParent) = aParent;
unconst(mAdapter) = aAdapter;
unconst(mPeer) = aThat;
autoInitSpan.setSucceeded();
return S_OK;
}
HRESULT NATEngine::initCopy (Machine *aParent, INetworkAdapter *aAdapter, NATEngine *aThat)
{
AutoInitSpan autoInitSpan(this);
AssertReturn(autoInitSpan.isOk(), E_FAIL);
Log(("initCopy that:%p this:%p\n", aThat, this));
AutoCaller thatCaller (aThat);
AssertComRCReturnRC(thatCaller.rc());
AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
mData.attachCopy(aThat->mData);
NATRuleMap::iterator it;
mNATRules.clear();
for (it = aThat->mNATRules.begin(); it != aThat->mNATRules.end(); ++it)
{
mNATRules.insert(std::make_pair(it->first, it->second));
}
unconst(mAdapter) = aAdapter;
unconst(mParent) = aParent;
autoInitSpan.setSucceeded();
return S_OK;
}
void NATEngine::FinalRelease()
{
uninit();
}
void NATEngine::uninit()
{
AutoUninitSpan autoUninitSpan(this);
if (autoUninitSpan.uninitDone())
return;
mNATRules.clear();
mData.free();
unconst(mPeer) = NULL;
unconst(mParent) = NULL;
}
bool NATEngine::isModified()
{
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
bool fModified = m_fModified;
return fModified;
}
bool NATEngine::rollback()
{
AutoCaller autoCaller(this);
AssertComRCReturn (autoCaller.rc(), false);
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
bool fChanged = m_fModified;
if (m_fModified)
{
/* we need to check all data to see whether anything will be changed
* after rollback */
mData.rollback();
}
m_fModified = false;
return fChanged;
}
void NATEngine::commit()
{
AutoCaller autoCaller(this);
AssertComRCReturnVoid (autoCaller.rc());
/* sanity too */
AutoCaller peerCaller (mPeer);
AssertComRCReturnVoid (peerCaller.rc());
/* lock both for writing since we modify both (mPeer is "master" so locked
* first) */
AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
if (m_fModified)
{
mData.commit();
if (mPeer)
{
mPeer->mData.attach (mData);
mPeer->mNATRules.clear();
NATRuleMap::iterator it;
for (it = mNATRules.begin(); it != mNATRules.end(); ++it)
{
mPeer->mNATRules.insert(std::make_pair(it->first, it->second));
}
}
}
m_fModified = false;
}
STDMETHODIMP
NATEngine::GetNetworkSettings(ULONG *aMtu, ULONG *aSockSnd, ULONG *aSockRcv, ULONG *aTcpWndSnd, ULONG *aTcpWndRcv)
{
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
if (aMtu)
*aMtu = mData->mMtu;
if (aSockSnd)
*aSockSnd = mData->mSockSnd;
if (aSockRcv)
*aSockRcv = mData->mSockRcv;
if (aTcpWndSnd)
*aTcpWndSnd = mData->mTcpSnd;
if (aTcpWndRcv)
*aTcpWndRcv = mData->mTcpRcv;
return S_OK;
}
STDMETHODIMP
NATEngine::SetNetworkSettings(ULONG aMtu, ULONG aSockSnd, ULONG aSockRcv, ULONG aTcpWndSnd, ULONG aTcpWndRcv)
{
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if ( aMtu || aSockSnd || aSockRcv
|| aTcpWndSnd || aTcpWndRcv)
{
mData.backup();
m_fModified = true;
}
if (aMtu)
mData->mMtu = aMtu;
if (aSockSnd)
mData->mSockSnd = aSockSnd;
if (aSockRcv)
mData->mSockRcv = aSockSnd;
if (aTcpWndSnd)
mData->mTcpSnd = aTcpWndSnd;
if (aTcpWndRcv)
mData->mTcpRcv = aTcpWndRcv;
if (m_fModified)
mParent->setModified(Machine::IsModified_NetworkAdapters);
return S_OK;
}
STDMETHODIMP
NATEngine::COMGETTER(Redirects)(ComSafeArrayOut(BSTR , aNatRules))
{
CheckComArgOutSafeArrayPointerValid(aNatRules);
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
SafeArray<BSTR> sf(mNATRules.size());
size_t i = 0;
NATRuleMap::const_iterator it;
for (it = mNATRules.begin();
it != mNATRules.end(); ++it, ++i)
{
settings::NATRule r = it->second;
BstrFmt bstr("%s,%d,%s,%d,%s,%d",
r.strName.c_str(),
r.proto,
r.strHostIP.c_str(),
r.u16HostPort,
r.strGuestIP.c_str(),
r.u16GuestPort);
bstr.detachTo(&sf[i]);
}
sf.detachTo(ComSafeArrayOutArg(aNatRules));
return S_OK;
}
STDMETHODIMP
NATEngine::AddRedirect(IN_BSTR aName, NATProtocol_T aProto, IN_BSTR aBindIp, USHORT aHostPort, IN_BSTR aGuestIP, USHORT aGuestPort)
{
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
Utf8Str name = aName;
settings::NATRule r;
const char *proto;
switch (aProto)
{
case NATProtocol_TCP:
proto = "tcp";
break;
case NATProtocol_UDP:
proto = "udp";
break;
default:
return E_INVALIDARG;
}
if (name.isEmpty())
name = Utf8StrFmt("%s_%d_%d", proto, aHostPort, aGuestPort);
NATRuleMap::iterator it;
for (it = mNATRules.begin(); it != mNATRules.end(); ++it)
{
r = it->second;
if (it->first == name)
return setError(E_INVALIDARG,
tr("A NAT rule of this name already exists"));
if ( r.strHostIP == Utf8Str(aBindIp)
&& r.u16HostPort == aHostPort)
return setError(E_INVALIDARG,
tr("A NAT rule for this host port and this host IP already exists"));
}
r.strName = name.c_str();
r.proto = aProto;
r.strHostIP = aBindIp;
r.u16HostPort = aHostPort;
r.strGuestIP = aGuestIP;
r.u16GuestPort = aGuestPort;
mNATRules.insert(std::make_pair(name, r));
mParent->setModified(Machine::IsModified_NetworkAdapters);
m_fModified = true;
ULONG ulSlot;
mAdapter->COMGETTER(Slot)(&ulSlot);
alock.release();
mParent->onNATRedirectRuleChange(ulSlot, FALSE, Bstr(name).raw(), aProto, Bstr(r.strHostIP).raw(), r.u16HostPort, Bstr(r.strGuestIP).raw(), r.u16GuestPort);
return S_OK;
}
STDMETHODIMP
NATEngine::RemoveRedirect(IN_BSTR aName)
{
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
NATRuleMap::iterator it = mNATRules.find(aName);
if (it == mNATRules.end())
return E_INVALIDARG;
mData.backup();
settings::NATRule r = it->second;
Utf8Str strHostIP = r.strHostIP;
Utf8Str strGuestIP = r.strGuestIP;
NATProtocol_T proto = r.proto;
uint16_t u16HostPort = r.u16HostPort;
uint16_t u16GuestPort = r.u16GuestPort;
ULONG ulSlot;
mAdapter->COMGETTER(Slot)(&ulSlot);
mNATRules.erase(it);
mParent->setModified(Machine::IsModified_NetworkAdapters);
m_fModified = true;
alock.release();
mParent->onNATRedirectRuleChange(ulSlot, TRUE, aName, proto, Bstr(strHostIP).raw(), u16HostPort, Bstr(strGuestIP).raw(), u16GuestPort);
return S_OK;
}
HRESULT NATEngine::loadSettings(const settings::NAT &data)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
mData->mNetwork = data.strNetwork;
mData->mBindIP = data.strBindIP;
mData->mMtu = data.u32Mtu;
mData->mSockSnd = data.u32SockSnd;
mData->mTcpRcv = data.u32TcpRcv;
mData->mTcpSnd = data.u32TcpSnd;
/* TFTP */
mData->mTftpPrefix = data.strTftpPrefix;
mData->mTftpBootFile = data.strTftpBootFile;
mData->mTftpNextServer = data.strTftpNextServer;
/* DNS */
mData->mDnsPassDomain = data.fDnsPassDomain;
mData->mDnsProxy = data.fDnsProxy;
mData->mDnsUseHostResolver = data.fDnsUseHostResolver;
/* Alias */
mData->mAliasMode = (data.fAliasUseSamePorts ? NATAliasMode_AliasUseSamePorts : 0);
mData->mAliasMode |= (data.fAliasLog ? NATAliasMode_AliasLog : 0);
mData->mAliasMode |= (data.fAliasProxyOnly ? NATAliasMode_AliasProxyOnly : 0);
/* port forwarding */
mNATRules.clear();
for (settings::NATRuleList::const_iterator it = data.llRules.begin();
it != data.llRules.end(); ++it)
{
mNATRules.insert(std::make_pair(it->strName, *it));
}
m_fModified = false;
return rc;
}
HRESULT NATEngine::saveSettings(settings::NAT &data)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = S_OK;
data.strNetwork = mData->mNetwork;
data.strBindIP = mData->mBindIP;
data.u32Mtu = mData->mMtu;
data.u32SockRcv = mData->mSockRcv;
data.u32SockSnd = mData->mSockSnd;
data.u32TcpRcv = mData->mTcpRcv;
data.u32TcpSnd = mData->mTcpSnd;
/* TFTP */
data.strTftpPrefix = mData->mTftpPrefix;
data.strTftpBootFile = mData->mTftpBootFile;
data.strTftpNextServer = mData->mTftpNextServer;
/* DNS */
data.fDnsPassDomain = !!mData->mDnsPassDomain;
data.fDnsProxy = !!mData->mDnsProxy;
data.fDnsUseHostResolver = !!mData->mDnsUseHostResolver;
/* Alias */
data.fAliasLog = !!(mData->mAliasMode & NATAliasMode_AliasLog);
data.fAliasProxyOnly = !!(mData->mAliasMode & NATAliasMode_AliasProxyOnly);
data.fAliasUseSamePorts = !!(mData->mAliasMode & NATAliasMode_AliasUseSamePorts);
for (NATRuleMap::iterator it = mNATRules.begin();
it != mNATRules.end(); ++it)
data.llRules.push_back(it->second);
m_fModified = false;
return rc;
}
STDMETHODIMP
NATEngine::COMSETTER(Network)(IN_BSTR aNetwork)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC (autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (Bstr(mData->mNetwork) != aNetwork)
{
mData.backup();
mData->mNetwork = aNetwork;
mParent->setModified(Machine::IsModified_NetworkAdapters);
m_fModified = true;
}
return S_OK;
}
STDMETHODIMP
NATEngine::COMGETTER(Network)(BSTR *aNetwork)
{
CheckComArgNotNull(aNetwork);
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
if (!mData->mNetwork.isEmpty())
{
mData->mNetwork.cloneTo(aNetwork);
Log(("Getter (this:%p) Network: %s\n", this, mData->mNetwork.c_str()));
}
return S_OK;
}
STDMETHODIMP
NATEngine::COMSETTER(HostIP) (IN_BSTR aBindIP)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC (autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (Bstr(mData->mBindIP) != aBindIP)
{
mData.backup();
mData->mBindIP = aBindIP;
mParent->setModified(Machine::IsModified_NetworkAdapters);
m_fModified = true;
}
return S_OK;
}
STDMETHODIMP NATEngine::COMGETTER(HostIP) (BSTR *aBindIP)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
if (!mData->mBindIP.isEmpty())
mData->mBindIP.cloneTo(aBindIP);
return S_OK;
}
STDMETHODIMP
NATEngine::COMSETTER(TftpPrefix)(IN_BSTR aTftpPrefix)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC (autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (Bstr(mData->mTftpPrefix) != aTftpPrefix)
{
mData.backup();
mData->mTftpPrefix = aTftpPrefix;
mParent->setModified(Machine::IsModified_NetworkAdapters);
m_fModified = true;
}
return S_OK;
}
STDMETHODIMP
NATEngine::COMGETTER(TftpPrefix)(BSTR *aTftpPrefix)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
if (!mData->mTftpPrefix.isEmpty())
{
mData->mTftpPrefix.cloneTo(aTftpPrefix);
Log(("Getter (this:%p) TftpPrefix: %s\n", this, mData->mTftpPrefix.c_str()));
}
return S_OK;
}
STDMETHODIMP
NATEngine::COMSETTER(TftpBootFile)(IN_BSTR aTftpBootFile)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC (autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (Bstr(mData->mTftpBootFile) != aTftpBootFile)
{
mData.backup();
mData->mTftpBootFile = aTftpBootFile;
mParent->setModified(Machine::IsModified_NetworkAdapters);
m_fModified = true;
}
return S_OK;
}
STDMETHODIMP
NATEngine::COMGETTER(TftpBootFile)(BSTR *aTftpBootFile)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
if (!mData->mTftpBootFile.isEmpty())
{
mData->mTftpBootFile.cloneTo(aTftpBootFile);
Log(("Getter (this:%p) BootFile: %s\n", this, mData->mTftpBootFile.c_str()));
}
return S_OK;
}
STDMETHODIMP
NATEngine::COMSETTER(TftpNextServer)(IN_BSTR aTftpNextServer)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC (autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (Bstr(mData->mTftpNextServer) != aTftpNextServer)
{
mData.backup();
mData->mTftpNextServer = aTftpNextServer;
mParent->setModified(Machine::IsModified_NetworkAdapters);
m_fModified = true;
}
return S_OK;
}
STDMETHODIMP
NATEngine::COMGETTER(TftpNextServer)(BSTR *aTftpNextServer)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
if (!mData->mTftpNextServer.isEmpty())
{
mData->mTftpNextServer.cloneTo(aTftpNextServer);
Log(("Getter (this:%p) NextServer: %s\n", this, mData->mTftpNextServer.c_str()));
}
return S_OK;
}
/* DNS */
STDMETHODIMP
NATEngine::COMSETTER(DnsPassDomain) (BOOL aDnsPassDomain)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC (autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (mData->mDnsPassDomain != aDnsPassDomain)
{
mData.backup();
mData->mDnsPassDomain = aDnsPassDomain;
mParent->setModified(Machine::IsModified_NetworkAdapters);
m_fModified = true;
}
return S_OK;
}
STDMETHODIMP
NATEngine::COMGETTER(DnsPassDomain)(BOOL *aDnsPassDomain)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
*aDnsPassDomain = mData->mDnsPassDomain;
return S_OK;
}
STDMETHODIMP
NATEngine::COMSETTER(DnsProxy)(BOOL aDnsProxy)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC (autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (mData->mDnsProxy != aDnsProxy)
{
mData.backup();
mData->mDnsProxy = aDnsProxy;
mParent->setModified(Machine::IsModified_NetworkAdapters);
m_fModified = true;
}
return S_OK;
}
STDMETHODIMP
NATEngine::COMGETTER(DnsProxy)(BOOL *aDnsProxy)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
*aDnsProxy = mData->mDnsProxy;
return S_OK;
}
STDMETHODIMP
NATEngine::COMGETTER(DnsUseHostResolver)(BOOL *aDnsUseHostResolver)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC (autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
*aDnsUseHostResolver = mData->mDnsUseHostResolver;
return S_OK;
}
STDMETHODIMP
NATEngine::COMSETTER(DnsUseHostResolver)(BOOL aDnsUseHostResolver)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
if (mData->mDnsUseHostResolver != aDnsUseHostResolver)
{
mData.backup();
mData->mDnsUseHostResolver = aDnsUseHostResolver;
mParent->setModified(Machine::IsModified_NetworkAdapters);
m_fModified = true;
}
return S_OK;
}
STDMETHODIMP NATEngine::COMSETTER(AliasMode) (ULONG aAliasMode)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC(autoCaller.rc());
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
if (mData->mAliasMode != aAliasMode)
{
mData.backup();
mData->mAliasMode = aAliasMode;
mParent->setModified(Machine::IsModified_NetworkAdapters);
m_fModified = true;
}
return S_OK;
}
STDMETHODIMP NATEngine::COMGETTER(AliasMode) (ULONG *aAliasMode)
{
AutoCaller autoCaller(this);
AssertComRCReturnRC (autoCaller.rc());
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
*aAliasMode = mData->mAliasMode;
return S_OK;
}