2024-09-24 14:54:57 +02:00

304 lines
5.9 KiB
C++

#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include <algorithm>
#include <stdio.h>
#include "adminList.h"
#include "global.h"
#include "stl/stringUtils.h"
#include "macros.h"
#include "webNet/socketOps.h"
#include "file/fileUtils.h"
#include "services/stdServiceImpl.h"
#include <assert.h>
using namespace std;
using namespace uniString;
using namespace stringUtil;
#define LOGNAME "[ADMINCGI] "
#ifdef _WIN32
typedef unsigned long in_addr_t;
#endif
adminList g_adminList;
class adminList::impl
{
private:
struct adminEntry: public admin_t
{
in_addr_t m_ip; // ip as binary type. Old style, but that's how the old sc_serv did it and we'll
// continue to do it that way until we're ready to break the old software
bool validIP() throw()
{
return ((m_ip != INADDR_NONE) && (m_ip != 0));
}
static in_addr_t stringToIP(const utf8 &sIP, utf8 &hostIP)
{
// default is to assume a raw IP address in the list
in_addr_t ip = inet_addr((const char *)sIP.c_str());
if (ip == INADDR_NONE)
{
// though if that fails then attempt to
// get an IP address from a hostname...
string sHost;
try
{
sHost = socketOps::hostNameToAddress(sIP.hideAsString());
}
catch(...)
{
}
if (!sHost.empty())
{
ip = inet_addr((const char *)sHost.c_str());
if (ip != INADDR_NONE)
{
hostIP = sHost;
}
}
}
return ip;
}
explicit adminEntry(const utf8 &numericIP) throw() : admin_t(numericIP), m_ip(stringToIP(numericIP, m_hostIP)) {}
adminEntry() throw() : m_ip(0) {}
};
AOL_namespace::mutex m_lock;
list<adminEntry> m_list;
public:
bool load(const uniFile::filenameType &fn) throw(exception)
{
if (fn.empty())
{
throwEx<tagged_error>(LOGNAME "No admin access file");
}
else if (gOptions.microServerDebug())
{
DLOG(LOGNAME "Attempting to read admin access file: " + fileUtil::getFullFilePath(fn));
}
stackLock sml(m_lock);
FILE *f = uniFile::fopen(fn,"rb");
if (!f) return false;
bool updating = (!m_list.empty());
m_list.clear();
try
{
int l = 0;
int count = 0;
while (true)
{
char buffer[4096] = {0};
if (!fgets(buffer, sizeof(buffer), f)) break; // get a line
++l; // increment line counter
utf8 s;
// skip utf-8 BOM
if ((strlen(buffer) > 2) &&
(((unsigned char*)buffer)[0] == 0xef) &&
(((unsigned char*)buffer)[1] == 0xbb) &&
(((unsigned char*)buffer)[2] == 0xbf))
{
s = &(buffer[3]);
}
else
{
s = buffer;
}
adminEntry e(stripWhitespace(s));
if (!e.validIP())
{
WLOG(LOGNAME "Line " + tos(l) + " of admin access list has been ignored (bad IP)");
}
else
{
if (this->find(e.m_numericIP,false) < 1)
{
m_list.push_back(e);
++count;
}
}
}
if (!updating)
{
if (count > 0)
{
ILOG(LOGNAME "Enabled " + tos(count) + " IP" +
(count != 1 ? "'s" : "") + " from admin access file");
}
else if (gOptions.microServerDebug())
{
DLOG(LOGNAME "No IPs read from admin access file");
}
}
else
{
ILOG(LOGNAME "Reloaded " + tos(count) + " IP" +
(count != 1 ? "'s" : "") + " from admin access file");
}
}
catch(...)
{
if (f) ::fclose(f);
throw;
}
if (f) ::fclose(f);
return true;
}
bool add(const utf8 &ipAddr, const bool soft) throw(exception)
{
// skip loopback addresses as we treat them specially anyway
if ((ipAddr.find(utf8("127.")) == utf8::npos))
{
adminEntry e(ipAddr);
if (!e.validIP())
{
if (!soft)
{
throwEx<runtime_error>(LOGNAME "Invalid IP specified - `" + ipAddr + "'");
}
else
{
return false;
}
}
stackLock sml(m_lock);
m_list.push_back(e);
return true;
}
return false;
}
// true if removed
bool remove(const utf8 &ipAddr, const bool allStream, const bool fallback = false, const bool use_lock = true)
{
if (use_lock)
{
stackLock sml(m_lock);
}
for ( list<adminEntry>::iterator i = m_list.begin(); i != m_list.end(); ++i )
{
if ( ( allStream || ( ( !allStream && ( ( *i ).m_numericIP == ipAddr ) ) || ( ( *i ).m_hostIP == ipAddr ) ) ) )
{
m_list.erase( i );
return true;
}
}
// attempt to see if we've got a hostname which has not been detected from the loading mapping
if (!fallback)
{
string sHost;
try
{
sHost = socketOps::hostNameToAddress(ipAddr.hideAsString());
}
catch(...)
{
}
if (!sHost.empty())
{
return remove(sHost, allStream, true, false);
}
}
return false;
}
// 1 if found, 0 if not, -1 if empty (assume allowed)
int find(const utf8 &ipAddr, const bool use_lock=true) throw()
{
if (use_lock)
{
stackLock sml(m_lock);
}
if (!m_list.empty())
{
for (list<adminEntry>::const_iterator i = m_list.begin(); i != m_list.end(); ++i)
{
if ((ipAddr == (*i).m_numericIP) || (ipAddr == (*i).m_hostIP))
{
return 1;
}
}
return 0;
}
return -1;
}
};
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
adminList::adminList():m_impl(0)
{
m_impl = new adminList::impl;
}
adminList::~adminList() throw()
{
forget(m_impl);
}
bool adminList::load(const uniFile::filenameType &fn) throw()
{
assert(m_impl);
bool result(false);
try
{
result = m_impl->load(fn);
}
catch(const exception &ex)
{
ELOG(ex.what());
}
return result;
}
// throws if parameters are invalid
bool adminList::add(const utf8 &ipAddr, const bool soft) throw(exception)
{
assert(m_impl);
return m_impl->add(ipAddr, soft);
}
// true if removed
bool adminList::remove(const utf8 &ipAddr, const bool allStream) throw()
{
assert(m_impl);
return m_impl->remove(ipAddr, allStream);
}
// 1 if found, 0 if not, -1 if empty (assume allowed)
int adminList::find(const utf8 &ipAddr) throw()
{
assert(m_impl);
return m_impl->find(ipAddr);
}