* ---------------------------------------------------
*/
+/* $Core: libIRCDusers */
+
#include "inspircd.h"
#include <stdarg.h>
#include "socketengine.h"
/* XXX: Used for speeding up WriteCommon operations */
unsigned long uniq_id = 0;
-bool InitTypes(ServerConfig* conf, const char* tag)
-{
- if (conf->opertypes.size())
- {
- for (opertype_t::iterator n = conf->opertypes.begin(); n != conf->opertypes.end(); n++)
- {
- if (n->second)
- delete[] n->second;
- }
- }
-
- conf->opertypes.clear();
- return true;
-}
-
-bool InitClasses(ServerConfig* conf, const char* tag)
-{
- if (conf->operclass.size())
- {
- for (operclass_t::iterator n = conf->operclass.begin(); n != conf->operclass.end(); n++)
- {
- if (n->second)
- delete[] n->second;
- }
- }
-
- conf->operclass.clear();
- return true;
-}
-
-bool DoType(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
-{
- const char* TypeName = values[0].GetString();
- const char* Classes = values[1].GetString();
-
- conf->opertypes[TypeName] = strnewdup(Classes);
- return true;
-}
-
-bool DoClass(ServerConfig* conf, const char* tag, char** entries, ValueList &values, int* types)
-{
- const char* ClassName = values[0].GetString();
- const char* CommandList = values[1].GetString();
-
- conf->operclass[ClassName] = strnewdup(CommandList);
- return true;
-}
-
-bool DoneClassesAndTypes(ServerConfig* conf, const char* tag)
-{
- return true;
-}
-
std::string User::ProcessNoticeMasks(const char *sm)
{
bool adding = true, oldadding = false;
}
}
-UserResolver::UserResolver(InspIRCd* Instance, User* user, std::string to_resolve, QueryType qt, bool &cache) :
- Resolver(Instance, to_resolve, qt, cache), bound_user(user)
-{
- this->fwd = (qt == DNS_QUERY_A || qt == DNS_QUERY_AAAA);
- this->bound_fd = user->GetFd();
-}
-
-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;
- try
- {
- /* Check we didnt time out */
- if (this->bound_user->registered != REG_ALL)
- {
- bool cached;
-#ifdef IPV6
- if (this->bound_user->GetProtocolFamily() == AF_INET6)
- {
- /* IPV6 forward lookup (with possibility of 4in6) */
- const char* ip = this->bound_user->GetIPString();
- 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) */
-#endif
- /* IPV4 lookup (ipv4 only mode) */
- bound_user->res_forward = new UserResolver(this->ServerInstance, this->bound_user, result, DNS_QUERY_A, cached);
- this->ServerInstance->AddResolver(bound_user->res_forward, cached);
- }
- }
- catch (CoreException& e)
- {
- ServerInstance->Log(DEBUG,"Error in resolver: %s",e.GetReason());
- }
- }
- else if ((this->fwd) && (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user))
- {
- /* Both lookups completed */
- std::string result2("0::ffff:");
- result2.append(result);
- if (this->bound_user->GetIPString() == result || this->bound_user->GetIPString() == result2)
- {
- std::string hostname = this->bound_user->stored_host;
- if (hostname.length() < 65)
- {
- /* Check we didnt time out */
- if ((this->bound_user->registered != REG_ALL) && (!this->bound_user->dns_done))
- {
- /* Hostnames starting with : are not a good thing (tm) */
- if (*(hostname.c_str()) == ':')
- 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;
- strlcpy(this->bound_user->dhost, hostname.c_str(),64);
- strlcpy(this->bound_user->host, hostname.c_str(),64);
- /* Invalidate cache */
- this->bound_user->InvalidateCache();
- }
- }
- else
- {
- if (!this->bound_user->dns_done)
- {
- this->bound_user->WriteServ("NOTICE Auth :*** Your hostname is longer than the maximum of 64 characters, using your IP address (%s) instead.", this->bound_user->GetIPString());
- this->bound_user->dns_done = true;
- }
- }
- }
- else
- {
- if (!this->bound_user->dns_done)
- {
- this->bound_user->WriteServ("NOTICE Auth :*** Your hostname does not match up with your IP address. Sorry, using your IP address (%s) instead.", this->bound_user->GetIPString());
- this->bound_user->dns_done = true;
- }
- }
- }
-}
-
-void UserResolver::OnError(ResolverError e, const std::string &errormessage)
-{
- if (ServerInstance->SE->GetRef(this->bound_fd) == this->bound_user)
- {
- /* Since dns timeout is implemented outside of the resolver, this was a race condition that could result in this message being sent *after*
- * the user was fully connected. This check fixes that issue - Special */
- if (!this->bound_user->dns_done)
- {
- /* Error message here */
- this->bound_user->WriteServ("NOTICE Auth :*** Could not resolve your hostname: %s; using your IP address (%s) instead.", errormessage.c_str(), this->bound_user->GetIPString());
- this->bound_user->dns_done = true;
- }
- }
-}
-
-
bool User::IsNoticeMaskSet(unsigned char sm)
{
return (snomasks[sm-65]);
server = (char*)Instance->FindServerNamePtr(Instance->Config->ServerName);
reset_due = ServerInstance->Time();
age = ServerInstance->Time(true);
+ Penalty = 0;
lines_in = lastping = signon = idle_lastmsg = nping = registered = 0;
- ChannelCount = timeout = flood = bytes_in = bytes_out = cmds_in = cmds_out = 0;
- muted = exempt = haspassed = dns_done = false;
+ ChannelCount = timeout = bytes_in = bytes_out = cmds_in = cmds_out = 0;
+ OverPenalty = ExemptFromPenalty = muted = exempt = haspassed = dns_done = false;
fd = -1;
recvq.clear();
sendq.clear();
res_forward = res_reverse = NULL;
Visibility = NULL;
ip = NULL;
+ MyClass = NULL;
chans.clear();
invites.clear();
memset(modes,0,sizeof(modes));
User::~User()
{
+ /* NULL for remote users :) */
+ if (this->MyClass)
+ {
+ this->MyClass->RefCount--;
+ ServerInstance->Log(DEBUG, "User destructor -- connect refcount now: %u", this->MyClass->RefCount);
+ }
+
this->InvalidateCache();
this->DecrementModes();
if (operquit)
return true;
// are they even an oper at all?
- if (IS_OPER(this))
+ if (!IS_OPER(this))
+ {
+ return false;
+ }
+
+ // check their opertype exists (!). This won't affect local users, of course.
+ opertype_t::iterator iter_opertype = ServerInstance->Config->opertypes.find(this->oper);
+ if (iter_opertype == ServerInstance->Config->opertypes.end())
{
- opertype_t::iterator iter_opertype = ServerInstance->Config->opertypes.find(this->oper);
- if (iter_opertype != ServerInstance->Config->opertypes.end())
+ return false;
+ }
+
+ /* XXX all this strtok/strdup stuff is a bit ick and horrid -- w00t */
+ char* Classes = strdup(iter_opertype->second);
+ char* myclass = strtok_r(Classes," ",&savept);
+ while (myclass)
+ {
+ operclass_t::iterator iter_operclass = ServerInstance->Config->operclass.find(myclass);
+ if (iter_operclass != ServerInstance->Config->operclass.end())
{
- char* Classes = strdup(iter_opertype->second);
- char* myclass = strtok_r(Classes," ",&savept);
- while (myclass)
+ char* CommandList = strdup(iter_operclass->second);
+ mycmd = strtok_r(CommandList," ",&savept2);
+ while (mycmd)
{
- operclass_t::iterator iter_operclass = ServerInstance->Config->operclass.find(myclass);
- if (iter_operclass != ServerInstance->Config->operclass.end())
+ if ((!strcasecmp(mycmd,command.c_str())) || (*mycmd == '*'))
{
- char* CommandList = strdup(iter_operclass->second);
- mycmd = strtok_r(CommandList," ",&savept2);
- while (mycmd)
- {
- if ((!strcasecmp(mycmd,command.c_str())) || (*mycmd == '*'))
- {
- free(Classes);
- free(CommandList);
- return true;
- }
- mycmd = strtok_r(NULL," ",&savept2);
- }
+ free(Classes);
free(CommandList);
+ return true;
}
- myclass = strtok_r(NULL," ",&savept);
+ mycmd = strtok_r(NULL," ",&savept2);
}
- free(Classes);
+ free(CommandList);
}
+ myclass = strtok_r(NULL," ",&savept);
}
+ free(Classes);
+
return false;
}
if (a.length())
recvq.append(a);
- if (recvq.length() > (unsigned)this->recvqmax)
+ if (this->MyClass && (recvq.length() > this->MyClass->GetRecvqMax()))
{
this->SetWriteError("RecvQ exceeded");
- ServerInstance->WriteOpers("*** User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->recvqmax);
+ ServerInstance->WriteOpers("*** User %s RecvQ of %d exceeds connect class maximum of %d",this->nick,recvq.length(),this->MyClass->GetRecvqMax());
return false;
}
{
try
{
- if (!recvq.length())
+ if (recvq.empty())
return "";
/* Strip any leading \r or \n off the string.
if (*this->GetWriteError())
return;
- if (sendq.length() + data.length() > (unsigned)this->sendqmax)
+ if (this->MyClass && (sendq.length() + data.length() > this->MyClass->GetSendqMax()))
{
/*
* Fix by brain - Set the error text BEFORE calling writeopers, because
* to repeatedly add the text to the sendq!
*/
this->SetWriteError("SendQ exceeded");
- ServerInstance->WriteOpers("*** User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->sendqmax);
+ ServerInstance->WriteOpers("*** User %s SendQ of %d exceeds connect class maximum of %d",this->nick,sendq.length() + data.length(),this->MyClass->GetSendqMax());
return;
}
void User::QuitUser(InspIRCd* Instance, User *user, const std::string &quitreason, const char* operreason)
{
- user->Write("ERROR :Closing link (%s@%s) [%s]", user->ident, user->host, operreason);
+ Instance->Log(DEBUG,"QuitUser: %s '%s'", user->nick, quitreason.c_str());
+ user->Write("ERROR :Closing link (%s@%s) [%s]", user->ident, user->host, *operreason ? operreason : quitreason.c_str());
user->muted = true;
Instance->GlobalCulls.AddItem(user, quitreason.c_str(), operreason);
}
return;
}
+ Instance->Log(DEBUG,"New user fd: %d", socket);
+
int j = 0;
Instance->unregistered_count++;
* First class check. We do this again in FullConnect after DNS is done, and NICK/USER is recieved.
* See my note down there for why this is required. DO NOT REMOVE. :) -- w00t
*/
- ConnectClass* i = New->GetClass();
+ ConnectClass* i = New->SetClass();
if (!i)
{
}
#endif
- New->exempt = (Instance->XLines->matches_exception(New) != NULL);
+ New->exempt = (Instance->XLines->MatchesLine("E",New) != NULL);
if (!New->exempt)
{
- ZLine* r = Instance->XLines->matches_zline(ipaddr);
+ XLine* r = Instance->XLines->MatchesLine("Z",New);
+
if (r)
{
char reason[MAXBUF];
{
if (!Instance->SE->AddFd(New))
{
+ Instance->Log(DEBUG,"Internal error on new connection");
User::QuitUser(Instance, New, "Internal error handling connection");
}
}
* BOPM and other stuff requires it.
*/
New->WriteServ("NOTICE Auth :*** Looking up your hostname...");
+
+ if (Instance->Config->NoUserDns)
+ {
+ New->dns_done = true;
+ }
+ else
+ {
+ New->StartDNSLookup();
+ }
}
unsigned long User::GlobalCloneCount()
/*
* Check class restrictions
*/
-void User::CheckClass(const std::string &explicit_class)
+void User::CheckClass()
{
- ConnectClass* a = this->GetClass(explicit_class);
+ ConnectClass* a = this->MyClass;
if ((!a) || (a->GetType() == CC_DENY))
{
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();
}
* may put the user into a totally seperate class with different restrictions! so we *must* check again.
* Don't remove this! -- w00t
*/
- this->CheckClass();
+ this->SetClass();
/* 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))
+ if (this->MyClass && !this->MyClass->GetPass().empty() && !this->haspassed)
{
User::QuitUser(ServerInstance, this, "Invalid password");
return;
}
-
+
if (!this->exempt)
{
- GLine* r = ServerInstance->XLines->matches_gline(this);
+ XLine* r = ServerInstance->XLines->MatchesLine("G",this);
if (r)
{
return;
}
- KLine* n = ServerInstance->XLines->matches_kline(this);
+ XLine* n = ServerInstance->XLines->MatchesLine("K",this);
if (n)
{
ServerInstance->CallCommandHandler("LUSERS", NULL, 0, this);
/*
- * fix 3 by brain, move registered = 7 below these so that spurious modes and host
- * changes dont go out onto the network and produce 'fake direction'.
+ * We don't set REG_ALL until triggering OnUserConnect, so some module events don't spew out stuff
+ * for a user that doesn't exist yet.
*/
FOREACH_MOD(I_OnUserConnect,OnUserConnect(this));
return false;
}
- if (ServerInstance->XLines->matches_qline(newnick))
+ if (ServerInstance->XLines->MatchesLine("Q",newnick))
{
ServerInstance->stats->statsCollisions++;
return false;
}
break;
default:
- ServerInstance->Log(DEBUG,"Ut oh, I dont know protocol %d to be set on '%s'!", protocol_family, this->nick);
+ ServerInstance->Log(DEBUG,"Uh oh, I dont know protocol %d to be set on '%s'!", protocol_family, this->nick);
break;
}
}
return sin->sin_family;
}
+/*
+ * XXX the duplication here is horrid..
+ * do we really need two methods doing essentially the same thing?
+ */
const char* User::GetIPString()
{
static char buf[1024];
return "";
}
-const char* User::GetIPString(char* buf)
-{
- if (this->ip == NULL)
- {
- *buf = 0;
- return buf;
- }
-
- switch (this->GetProtocolFamily())
- {
-#ifdef SUPPORT_IP6LINKS
- case AF_INET6:
- {
- static char temp[1024];
-
- sockaddr_in6* sin = (sockaddr_in6*)this->ip;
- inet_ntop(sin->sin6_family, &sin->sin6_addr, buf, sizeof(buf));
- /* IP addresses starting with a : on irc are a Bad Thing (tm) */
- if (*buf == ':')
- {
- strlcpy(&temp[1], buf, sizeof(temp) - 1);
- *temp = '0';
- strlcpy(buf, temp, sizeof(temp));
- }
- return buf;
- }
- break;
-#endif
- case AF_INET:
- {
- sockaddr_in* sin = (sockaddr_in*)this->ip;
- inet_ntop(sin->sin_family, &sin->sin_addr, buf, sizeof(buf));
- return buf;
- }
- break;
-
- default:
- break;
- }
- return "";
-}
-
/** NOTE: We cannot pass a const reference to this method.
* The string is changed by the workings of the method,
* so that if we pass const ref, we end up copying it to
return this->MaxChans;
}
-/* looks up a users password for their connection class (<ALLOW>/<DENY> tags)
+
+/*
+ * Sets a user's connection class.
+ * If the class name is provided, it will be used. Otherwise, the class will be guessed using host/ip/ident/etc.
* 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* User::GetClass(const std::string &explicit_name)
+ConnectClass* User::SetClass(const std::string &explicit_name)
{
+ ConnectClass *found = NULL;
+
+ if (!IS_LOCAL(this))
+ return NULL;
+
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);
+ ConnectClass* c = *i;
+
+ if (explicit_name == c->GetName() && !c->GetDisabled())
+ {
+ found = c;
+ }
}
}
else
{
for (ClassVector::iterator i = ServerInstance->Config->Classes.begin(); i != ServerInstance->Config->Classes.end(); i++)
{
- if (((match(this->GetIPString(),i->GetHost().c_str(),true)) || (match(this->host,i->GetHost().c_str()))))
+ ConnectClass* c = *i;
+
+ if (((match(this->GetIPString(),c->GetHost().c_str(),true)) || (match(this->host,c->GetHost().c_str()))))
{
- if (i->GetPort())
+ if (c->GetPort())
{
- if (this->GetPort() == i->GetPort())
- return &(*i);
+ if (this->GetPort() == c->GetPort() && !c->GetDisabled())
+ {
+ found = c;
+ }
else
continue;
}
else
- return &(*i);
+ {
+ if (!c->GetDisabled())
+ found = c;
+ }
}
}
}
- return NULL;
+
+ /* ensure we don't fuck things up refcount wise, only remove them from a class if we find a new one :P */
+ if (found)
+ {
+ /* deny change if change will take class over the limit */
+ if (found->limit && (found->RefCount + 1 >= found->limit))
+ {
+ ServerInstance->Log(DEBUG, "OOPS: Connect class limit (%u) hit, denying", found->limit);
+ return this->MyClass;
+ }
+
+ /* should always be valid, but just in case .. */
+ if (this->MyClass)
+ {
+ if (found == this->MyClass) // no point changing this shit :P
+ return this->MyClass;
+ this->MyClass->RefCount--;
+ ServerInstance->Log(DEBUG, "Untying user from connect class -- refcount: %u", this->MyClass->RefCount);
+ }
+
+ this->MyClass = found;
+ this->MyClass->RefCount++;
+ ServerInstance->Log(DEBUG, "User tied to new class -- connect refcount now: %u", this->MyClass->RefCount);
+ }
+
+ return this->MyClass;
+}
+
+/* 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* User::GetClass()
+{
+ return this->MyClass;
}
void User::PurgeEmptyChannels()
return operquit ? operquit : "";
}
+void User::IncreasePenalty(int increase)
+{
+ this->Penalty += increase;
+}
+
+void User::DecreasePenalty(int decrease)
+{
+ this->Penalty -= decrease;
+}
+
VisData::VisData()
{
}