* ---------------------------------------------------
*/
-#include "configreader.h"
-#include "channels.h"
-#include "users.h"
#include "inspircd.h"
#include <stdarg.h>
#include "socketengine.h"
this->bound_fd = user->GetFd();
}
-void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached)
+void UserResolver::OnLookupComplete(const std::string &result, unsigned int ttl, bool cached, int resultnum)
{
+ /* We are only interested in the first matching result */
+ if (resultnum)
+ return;
+
if ((!this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user))
{
this->bound_user->stored_host = result;
bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, (!strncmp(ip, "0::ffff:", 8) ? DNS_QUERY_A : DNS_QUERY_AAAA), cached);
}
else
- {
/* IPV4 lookup (mixed protocol mode) */
- bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, DNS_QUERY_A, cached);
- }
-#else
+#endif
/* IPV4 lookup (ipv4 only mode) */
bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, DNS_QUERY_A, cached);
-#endif
this->ServerInstance->AddResolver(bound_user->res_forward, cached);
}
}
else if ((this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user))
{
/* Both lookups completed */
- std::string result2 = "0::ffff:";
+ std::string result2("0::ffff:");
result2.append(result);
if (this->bound_user->GetIPString() == result || this->bound_user->GetIPString() == result2)
{
{
/* Hostnames starting with : are not a good thing (tm) */
if (*(hostname.c_str()) == ':')
- hostname = "0" + hostname;
+ hostname.insert(0, "0");
this->bound_user->WriteServ("NOTICE Auth :*** Found your hostname (%s)%s", hostname.c_str(), (cached ? " -- cached" : ""));
this->bound_user->dns_done = true;
}
}
-userrec::userrec(InspIRCd* Instance) : ServerInstance(Instance)
+userrec::userrec(InspIRCd* Instance, const std::string &uid) : ServerInstance(Instance)
{
- // the PROPER way to do it, AVOID bzero at *ALL* costs
- *password = *nick = *ident = *host = *dhost = *fullname = *awaymsg = *oper = 0;
+ *password = *nick = *ident = *host = *dhost = *fullname = *awaymsg = *oper = *uuid = 0;
server = (char*)Instance->FindServerNamePtr(Instance->Config->ServerName);
reset_due = ServerInstance->Time();
age = ServerInstance->Time(true);
ChannelCount = timeout = flood = bytes_in = bytes_out = cmds_in = cmds_out = 0;
muted = exempt = haspassed = dns_done = false;
fd = -1;
- recvq = "";
- sendq = "";
- WriteError = "";
+ recvq.clear();
+ sendq.clear();
+ WriteError.clear();
res_forward = res_reverse = NULL;
Visibility = NULL;
ip = NULL;
memset(snomasks,0,sizeof(snomasks));
/* Invalidate cache */
operquit = cached_fullhost = cached_hostip = cached_makehost = cached_fullrealhost = NULL;
+
+ if (uid.empty())
+ strlcpy(uuid, Instance->GetUID().c_str(), UUID_LENGTH);
+ else
+ strlcpy(uuid, uid.c_str(), UUID_LENGTH);
+
+ ServerInstance->Log(DEBUG,"New UUID for user: %s (%s)", uuid, uid.empty() ? "allocated new" : "used remote");
+
+ user_hash::iterator finduuid = Instance->uuidlist->find(uuid);
+ if (finduuid == Instance->uuidlist->end())
+ (*Instance->uuidlist)[uuid] = this;
+ else
+ throw CoreException("Duplicate UUID "+std::string(uuid)+" in userrec constructor");
+}
+
+void userrec::RemoveCloneCounts()
+{
+ clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString());
+ if (x != ServerInstance->local_clones.end())
+ {
+ x->second--;
+ if (!x->second)
+ {
+ ServerInstance->local_clones.erase(x);
+ }
+ }
+
+ clonemap::iterator y = ServerInstance->global_clones.find(this->GetIPString());
+ if (y != ServerInstance->global_clones.end())
+ {
+ y->second--;
+ if (!y->second)
+ {
+ ServerInstance->global_clones.erase(y);
+ }
+ }
}
userrec::~userrec()
free(operquit);
if (ip)
{
- clonemap::iterator x = ServerInstance->local_clones.find(this->GetIPString());
- if (x != ServerInstance->local_clones.end())
- {
- x->second--;
- if (!x->second)
- {
- ServerInstance->local_clones.erase(x);
- }
- }
-
- clonemap::iterator y = ServerInstance->global_clones.find(this->GetIPString());
- if (y != ServerInstance->global_clones.end())
- {
- y->second--;
- if (!y->second)
- {
- ServerInstance->global_clones.erase(y);
- }
- }
+ this->RemoveCloneCounts();
if (this->GetProtocolFamily() == AF_INET)
{
}
#endif
}
+
+ ServerInstance->uuidlist->erase(uuid);
}
char* userrec::MakeHost()
void userrec::CloseSocket()
{
- shutdown(this->fd,2);
- close(this->fd);
+ ServerInstance->SE->Shutdown(this, 2);
+ ServerInstance->SE->Close(this);
}
char* userrec::GetFullHost()
void userrec::ClearBuffer()
{
- recvq = "";
+ recvq.clear();
}
std::string userrec::GetBuffer()
* Usually there are only one or two of these,
* so its is computationally cheap to do.
*/
- while ((*recvq.begin() == '\r') || (*recvq.begin() == '\n'))
- recvq.erase(recvq.begin());
+ std::string::iterator t = recvq.begin();
+ while (t != recvq.end() && (*t == '\r' || *t == '\n'))
+ {
+ recvq.erase(t);
+ t = recvq.begin();
+ }
for (std::string::iterator x = recvq.begin(); x != recvq.end(); x++)
{
{
if ((this->fd == FD_MAGIC_NUMBER) || (*this->GetWriteError()))
{
- sendq = "";
+ sendq.clear();
}
if ((sendq.length()) && (this->fd != FD_MAGIC_NUMBER))
{
int old_sendq_length = sendq.length();
-#ifndef WIN32
- int n_sent = write(this->fd, this->sendq.data(), this->sendq.length());
-#else
- int n_sent = send(this->fd, (const char*)this->sendq.data(), this->sendq.length(), 0);
-#endif
+ int n_sent = ServerInstance->SE->Send(this, this->sendq.data(), this->sendq.length(), 0);
+
if (n_sent == -1)
{
if (errno == EAGAIN)
{
/* Fatal error, set write error and bail
*/
- this->SetWriteError(strerror(errno));
+ this->SetWriteError(errno ? strerror(errno) : "EOF from client");
return;
}
}
// unset their oper type (what IS_OPER checks), and remove +o
*this->oper = 0;
this->modes[UM_OPERATOR] = 0;
-
- // remove them from the opers list.
- for (std::vector<userrec*>::iterator a = ServerInstance->all_opers.begin(); a < ServerInstance->all_opers.end(); a++)
- {
- if (*a == this)
- {
- ServerInstance->all_opers.erase(a);
- return;
- }
- }
+
+ // remove the user from the oper list. Will remove multiple entries as a safeguard against bug #404
+ ServerInstance->all_opers.remove(this);
}
}
/* add a client connection to the sockets list */
void userrec::AddClient(InspIRCd* Instance, int socket, int port, bool iscached, int socketfamily, sockaddr* ip)
{
- std::string tempnick = ConvToStr(socket) + "-unknown";
- user_hash::iterator iter = Instance->clientlist->find(tempnick);
+ /* NOTE: Calling this one parameter constructor for userrec automatically
+ * allocates a new UUID and places it in the hash_map.
+ */
+ userrec* New = NULL;
+ try
+ {
+ New = new userrec(Instance);
+ }
+ catch (...)
+ {
+ Instance->Log(DEFAULT,"*** WTF *** Duplicated UUID! -- Crack smoking monkies have been unleashed.");
+ Instance->WriteOpers("*** WARNING *** Duplicate UUID allocated!");
+ return;
+ }
+
+ int j = 0;
+
+ Instance->unregistered_count++;
+
char ipaddr[MAXBUF];
#ifdef IPV6
if (socketfamily == AF_INET6)
inet_ntop(AF_INET6, &((const sockaddr_in6*)ip)->sin6_addr, ipaddr, sizeof(ipaddr));
else
- inet_ntop(AF_INET, &((const sockaddr_in*)ip)->sin_addr, ipaddr, sizeof(ipaddr));
-#else
- inet_ntop(AF_INET, &((const sockaddr_in*)ip)->sin_addr, ipaddr, sizeof(ipaddr));
#endif
- userrec* New;
- int j = 0;
+ inet_ntop(AF_INET, &((const sockaddr_in*)ip)->sin_addr, ipaddr, sizeof(ipaddr));
- Instance->unregistered_count++;
+ (*(Instance->clientlist))[New->uuid] = New;
+ New->SetFd(socket);
- /*
- * fix by brain.
- * as these nicknames are 'RFC impossible', we can be sure nobody is going to be
- * using one as a registered connection. As they are per fd, we can also safely assume
- * that we wont have collisions. Therefore, if the nick exists in the list, its only
- * used by a dead socket, erase the iterator so that the new client may reclaim it.
- * this was probably the cause of 'server ignores me when i hammer it with reconnects'
- * issue in earlier alphas/betas
- */
- if (iter != Instance->clientlist->end())
- {
- userrec* goner = iter->second;
- DELETE(goner);
- Instance->clientlist->erase(iter);
- }
-
- New = new userrec(Instance);
- (*(Instance->clientlist))[tempnick] = New;
- New->fd = socket;
- strlcpy(New->nick,tempnick.c_str(),NICKMAX-1);
+ /* The users default nick is their UUID */
+ strlcpy(New->nick, New->uuid, NICKMAX - 1);
New->server = Instance->FindServerNamePtr(Instance->Config->ServerName);
/* We don't need range checking here, we KNOW 'unknown\0' will fit into the ident field. */
* See my note down there for why this is required. DO NOT REMOVE. :) -- w00t
*/
ConnectClass* i = New->GetClass();
- New->CheckClass();
- New->pingmax = i->GetPingTime();
- New->nping = Instance->Time() + i->GetPingTime() + Instance->Config->dns_timeout;
- New->timeout = Instance->Time() + i->GetRegTimeout();
- New->flood = i->GetFlood();
- New->threshold = i->GetThreshold();
- New->sendqmax = i->GetSendqMax();
- New->recvqmax = i->GetRecvqMax();
+ if (!i)
+ {
+ userrec::QuitUser(Instance, New, "Access denied by configuration");
+ return;
+ }
+
+ /*
+ * Check connect class settings and initialise settings into userrec.
+ * This will be done again after DNS resolution. -- w00t
+ */
+ New->CheckClass();
Instance->local_users.push_back(New);
if (r)
{
char reason[MAXBUF];
+ if (*Instance->Config->MoronBanner)
+ New->WriteServ("NOTICE %s :*** %s", New->nick, Instance->Config->MoronBanner);
snprintf(reason,MAXBUF,"Z-Lined: %s",r->reason);
userrec::QuitUser(Instance, New, reason);
return;
/*
* Check class restrictions
*/
-void userrec::CheckClass()
+void userrec::CheckClass(const std::string &explicit_class)
{
- ConnectClass* a = this->GetClass();
+ ConnectClass* a = this->GetClass(explicit_class);
if ((!a) || (a->GetType() == CC_DENY))
{
userrec::QuitUser(ServerInstance, this, "Unauthorised connection");
return;
}
-
- if ((!a->GetPass().empty()) && (!this->haspassed))
- {
- userrec::QuitUser(ServerInstance, this, "Invalid password");
- return;
- }
-
- if ((!a) || (a->GetType() == CC_DENY))
- {
- userrec::QuitUser(ServerInstance, this,"Unauthorised connection");
- return;
- }
-
- if ((a->GetMaxLocal()) && (this->LocalCloneCount() > a->GetMaxLocal()))
+ else if ((a->GetMaxLocal()) && (this->LocalCloneCount() > a->GetMaxLocal()))
{
userrec::QuitUser(ServerInstance, this, "No more connections allowed from your host via this connect class (local)");
ServerInstance->WriteOpers("*** WARNING: maximum LOCAL connections (%ld) exceeded for IP %s", a->GetMaxLocal(), this->GetIPString());
ServerInstance->WriteOpers("*** WARNING: maximum GLOBAL connections (%ld) exceeded for IP %s", a->GetMaxGlobal(), this->GetIPString());
return;
}
+
+ this->pingmax = a->GetPingTime();
+ this->nping = ServerInstance->Time() + a->GetPingTime() + ServerInstance->Config->dns_timeout;
+ this->timeout = ServerInstance->Time() + a->GetRegTimeout();
+ this->flood = a->GetFlood();
+ this->threshold = a->GetThreshold();
+ this->sendqmax = a->GetSendqMax();
+ this->recvqmax = a->GetRecvqMax();
+ this->MaxChans = a->GetMaxChans();
}
void userrec::FullConnect()
* Don't remove this! -- w00t
*/
this->CheckClass();
-
+
+ /* Check the password, if one is required by the user's connect class.
+ * This CANNOT be in CheckClass(), because that is called prior to PASS as well!
+ */
+ if ((!this->GetClass()->GetPass().empty()) && (!this->haspassed))
+ {
+ userrec::QuitUser(ServerInstance, this, "Invalid password");
+ return;
+ }
+
if (!this->exempt)
{
GLine* r = ServerInstance->XLines->matches_gline(this);
{
this->muted = true;
char reason[MAXBUF];
+ if (*ServerInstance->Config->MoronBanner)
+ this->WriteServ("NOTICE %s :*** %s", this->nick, ServerInstance->Config->MoronBanner);
snprintf(reason,MAXBUF,"G-Lined: %s",r->reason);
- ServerInstance->GlobalCulls.AddItem(this, reason);
+ userrec::QuitUser(ServerInstance, this, reason);
return;
}
{
this->muted = true;
char reason[MAXBUF];
+ if (*ServerInstance->Config->MoronBanner)
+ this->WriteServ("NOTICE %s :*** %s", this, ServerInstance->Config->MoronBanner);
snprintf(reason,MAXBUF,"K-Lined: %s",n->reason);
- ServerInstance->GlobalCulls.AddItem(this, reason);
+ userrec::QuitUser(ServerInstance, this, reason);
return;
}
-
}
this->WriteServ("NOTICE Auth :Welcome to \002%s\002!",ServerInstance->Config->Network);
ServerInstance->Config->Send005(this);
+ this->WriteServ("042 %s %s :your unique ID", this->nick, this->uuid);
+
+
this->ShowMOTD();
/* Now registered */
if (this->registered == REG_ALL)
{
- const char* pars[1];
- pars[0] = newnick;
- std::string cmd = "NICK";
- return (ServerInstance->Parser->CallHandler(cmd, pars, 1, this) == CMD_SUCCESS);
+ std::deque<classbase*> dummy;
+ command_t* nickhandler = ServerInstance->Parser->GetHandler("NICK");
+ if (nickhandler)
+ {
+ nickhandler->HandleInternal(1, dummy);
+ bool result = (ServerInstance->Parser->CallHandler("NICK", &newnick, 1, this) == CMD_SUCCESS);
+ nickhandler->HandleInternal(0, dummy);
+ return result;
+ }
}
return false;
}
*/
void userrec::Write(std::string text)
{
-#ifdef WINDOWS
- if ((this->fd < 0) || (this->m_internalFd > MAX_DESCRIPTORS))
-#else
- if ((this->fd < 0) || (this->fd > MAX_DESCRIPTORS))
-#endif
+ if (!ServerInstance->SE->BoundsCheckFd(this))
return;
try
{
- /* ServerInstance->Log(DEBUG,"C[%d] <- %s", this->GetFd(), text.c_str());
+ /* ServerInstance->Log(DEBUG,"C[%d] O %s", this->GetFd(), text.c_str());
* WARNING: The above debug line is VERY loud, do NOT
* enable it till we have a good way of filtering it
* out of the logs (e.g. 1.2 would be good).
{
try
{
+ /* XXX: The lack of buffering here is NOT a bug, modules implementing this interface have to
+ * implement their own buffering mechanisms
+ */
ServerInstance->Config->GetIOHook(this->GetPort())->OnRawSocketWrite(this->fd, text.data(), text.length());
}
catch (CoreException& modexcept)
if (!IS_OPER(this) && IS_LOCAL(this))
return;
- std::string wallop = "WALLOPS :" + text;
+ std::string wallop("WALLOPS :");
+ wallop.append(text);
for (std::vector<userrec*>::const_iterator i = ServerInstance->local_users.begin(); i != ServerInstance->local_users.end(); i++)
{
userrec* t = *i;
- if ((IS_LOCAL(t)) && (t->modes[UM_WALLOPS]))
+ if (t->IsModeSet('w'))
this->WriteTo(t,wallop);
}
}
* If the channel is NOT private/secret OR the user shares a common channel
* If the user is an oper, and the <options:operspywhois> option is set.
*/
- if ((source == this) || (IS_OPER(source) && ServerInstance->Config->OperSpyWhois) || (((!i->first->modes[CM_PRIVATE]) && (!i->first->modes[CM_SECRET])) || (i->first->HasUser(source))))
+ if ((source == this) || (IS_OPER(source) && ServerInstance->Config->OperSpyWhois) || (((!i->first->IsModeSet('p')) && (!i->first->IsModeSet('s'))) || (i->first->HasUser(source))))
{
list.append(i->first->GetPrefixChar(this)).append(i->first->name).append(" ");
}
}
}
+unsigned int userrec::GetMaxChans()
+{
+ return this->MaxChans;
+}
/* looks up a users password for their connection class (<ALLOW>/<DENY> tags)
* NOTE: If the <ALLOW> or <DENY> tag specifies an ip, and this user resolves,
* then their ip will be taken as 'priority' anyway, so for example,
* <connect allow="127.0.0.1"> will match joe!bloggs@localhost
*/
-ConnectClass* userrec::GetClass()
+ConnectClass* userrec::GetClass(const std::string &explicit_name)
{
- for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ if (!explicit_name.empty())
+ {
+ for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
+ {
+ if (explicit_name == i->GetName())
+ return &(*i);
+ }
+ }
+ else
{
- if (((match(this->GetIPString(),i->GetHost().c_str(),true)) || (match(this->host,i->GetHost().c_str()))))
+ for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
{
- if (i->GetPort())
+ if (((match(this->GetIPString(),i->GetHost().c_str(),true)) || (match(this->host,i->GetHost().c_str()))))
{
- if (this->GetPort() == i->GetPort())
- return &(*i);
+ if (i->GetPort())
+ {
+ if (this->GetPort() == i->GetPort())
+ return &(*i);
+ else
+ continue;
+ }
else
- continue;
+ return &(*i);
}
- else
- return &(*i);
}
}
return NULL;
{
if (!ServerInstance->Config->RULES.size())
{
- this->WriteServ("NOTICE %s :Rules file is missing.",this->nick);
+ this->WriteServ("434 %s :RULES File is missing",this->nick);
return;
}
- this->WriteServ("NOTICE %s :%s rules",this->nick,ServerInstance->Config->ServerName);
+
+ this->WriteServ("308 %s :- %s Server Rules -",this->nick,ServerInstance->Config->ServerName);
for (file_cache::iterator i = ServerInstance->Config->RULES.begin(); i != ServerInstance->Config->RULES.end(); i++)
- this->WriteServ("NOTICE %s :%s",this->nick,i->c_str());
+ this->WriteServ("232 %s :- %s",this->nick,i->c_str());
- this->WriteServ("NOTICE %s :End of %s rules.",this->nick,ServerInstance->Config->ServerName);
+ this->WriteServ("309 %s :End of RULES command.",this->nick);
}
void userrec::HandleEvent(EventType et, int errornum)