typedef nspace::hash_map<std::string, userrec*, nspace::hash<string>, StrHashComp> user_hash;
typedef nspace::hash_map<std::string, chanrec*, nspace::hash<string>, StrHashComp> chan_hash;
typedef nspace::hash_map<in_addr,string*, nspace::hash<in_addr>, InAddr_HashComp> address_cache;
+typedef nspace::hash_map<std::string, WhoWasUser*, nspace::hash<string>, StrHashComp> whowas_hash;
typedef std::deque<command_t> command_table;
// This table references users by file descriptor.
user_hash clientlist;
chan_hash chanlist;
-user_hash whowas;
+whowas_hash whowas;
command_table cmdlist;
file_cache MOTD;
file_cache RULES;
void ReadConfig(bool bail, userrec* user)
{
char dbg[MAXBUF],pauseval[MAXBUF],Value[MAXBUF],timeout[MAXBUF],NB[MAXBUF],flood[MAXBUF],MW[MAXBUF];
- char AH[MAXBUF],AP[MAXBUF],AF[MAXBUF],DNT[MAXBUF],pfreq[MAXBUF],thold[MAXBUF];
+ char AH[MAXBUF],AP[MAXBUF],AF[MAXBUF],DNT[MAXBUF],pfreq[MAXBUF],thold[MAXBUF],sqmax[MAXBUF],rqmax[MAXBUF];
ConnectClass c;
std::stringstream errstr;
ConfValue("connect","flood",i,flood,&config_f);
ConfValue("connect","pingfreq",i,pfreq,&config_f);
ConfValue("connect","threshold",i,thold,&config_f);
+ ConfValue("connect","sendq",i,sqmax,&config_f);
+ ConfValue("connect","recvq",i,rqmax,&config_f);
if (Value[0])
{
strlcpy(c.host,Value,MAXBUF);
c.pingtime = 120;
c.flood = atoi(flood);
c.threshold = 5;
+ c.sendqmax = 262144; // 256k
+ c.recvqmax = 4096; // 4k
if (atoi(thold)>0)
{
c.threshold = atoi(thold);
}
+ if (atoi(sqmax)>0)
+ {
+ c.sendqmax = atoi(sqmax);
+ }
+ if (atoi(rqmax)>0)
+ {
+ c.recvqmax = atoi(rqmax);
+ }
if (atoi(timeout)>0)
{
c.registration_timeout = atoi(timeout);
}
}
-/* write formatted text to a socket, in same format as printf */
+/* write formatted text to a socket, in same format as printf
+ * New in 1.0 Beta 5 - Nothing is written directly to a users fd any more.
+ * Instead, data builds up in the users sendq and each time around the mainloop
+ * this data is flushed to the user's socket (see userrec::FlushWriteBuf).
+ */
void Write(int sock,char *text, ...)
{
- if (sock == FD_MAGIC_NUMBER)
+ if (sock < 0)
return;
if (!text)
{
char textbuffer[MAXBUF];
va_list argsPtr;
char tb[MAXBUF];
+ int res;
va_start (argsPtr, text);
vsnprintf(textbuffer, MAXBUF, text, argsPtr);
va_end(argsPtr);
int bytes = snprintf(tb,MAXBUF,"%s\r\n",textbuffer);
chop(tb);
- if ((sock != -1) && (sock != FD_MAGIC_NUMBER))
+ if (fd_ref_table[sock])
{
int MOD_RESULT = 0;
- FOREACH_RESULT(OnRawSocketWrite(sock,tb,bytes > 512 ? 512 : bytes));
- if (!MOD_RESULT)
- write(sock,tb,bytes > 512 ? 512 : bytes);
- if (fd_ref_table[sock])
- {
- fd_ref_table[sock]->bytes_out += (bytes > 512 ? 512 : bytes);
- fd_ref_table[sock]->cmds_out++;
- }
- statsSent += (bytes > 512 ? 512 : bytes);
+ FOREACH_RESULT(OnRawSocketWrite(sock,tb,bytes));
+ fd_ref_table[sock]->AddWriteBuf(tb);
+ statsSent += bytes;
}
+ else log(DEFAULT,"ERROR! attempted write to a user with no fd_ref_table entry!!!");
}
/* write a server formatted numeric response to a single socket */
void WriteServ(int sock, char* text, ...)
{
- if (sock == FD_MAGIC_NUMBER)
+ if (sock < 0)
return;
if (!text)
{
return;
}
char textbuffer[MAXBUF],tb[MAXBUF];
+ int res;
va_list argsPtr;
va_start (argsPtr, text);
va_end(argsPtr);
int bytes = snprintf(tb,MAXBUF,":%s %s\r\n",ServerName,textbuffer);
chop(tb);
- if ((sock != -1) && (sock != FD_MAGIC_NUMBER))
- {
+ if (fd_ref_table[sock])
+ {
int MOD_RESULT = 0;
- FOREACH_RESULT(OnRawSocketWrite(sock,tb,bytes > 512 ? 512 : bytes));
- if (!MOD_RESULT)
- write(sock,tb,bytes > 512 ? 512 : bytes);
- if (fd_ref_table[sock])
- {
- fd_ref_table[sock]->bytes_out += (bytes > 512 ? 512 : bytes);
- fd_ref_table[sock]->cmds_out++;
- }
- statsSent += (bytes > 512 ? 512 : bytes);
- }
+ FOREACH_RESULT(OnRawSocketWrite(sock,tb,bytes));
+ fd_ref_table[sock]->AddWriteBuf(tb);
+ statsSent += bytes;
+ }
+ else log(DEFAULT,"ERROR! attempted write to a user with no fd_ref_table entry!!!");
}
/* write text from an originating user to originating user */
void WriteFrom(int sock, userrec *user,char* text, ...)
{
- if (sock == FD_MAGIC_NUMBER)
+ if (sock < 0)
return;
if ((!text) || (!user))
{
}
char textbuffer[MAXBUF],tb[MAXBUF];
va_list argsPtr;
+ int res;
va_start (argsPtr, text);
vsnprintf(textbuffer, MAXBUF, text, argsPtr);
va_end(argsPtr);
int bytes = snprintf(tb,MAXBUF,":%s!%s@%s %s\r\n",user->nick,user->ident,user->dhost,textbuffer);
chop(tb);
- if ((sock != -1) && (sock != FD_MAGIC_NUMBER))
- {
+ if (fd_ref_table[sock])
+ {
int MOD_RESULT = 0;
- FOREACH_RESULT(OnRawSocketWrite(sock,tb,bytes > 512 ? 512 : bytes));
- if (!MOD_RESULT)
- write(sock,tb,bytes > 512 ? 512 : bytes);
- if (fd_ref_table[sock])
- {
- fd_ref_table[sock]->bytes_out += (bytes > 512 ? 512 : bytes);
- fd_ref_table[sock]->cmds_out++;
- }
- statsSent += (bytes > 512 ? 512 : bytes);
- }
+ FOREACH_RESULT(OnRawSocketWrite(sock,tb,bytes));
+ fd_ref_table[sock]->AddWriteBuf(tb);
+ statsSent += bytes;
+ }
+ else log(DEFAULT,"ERROR! attempted write to a user with no fd_ref_table entry!!!");
}
/* write text to an destination user from a source user (e.g. user privmsg) */
void NetSendToCommon(userrec* u, char* s)
{
char buffer[MAXBUF];
- snprintf(buffer,MAXBUF,"%s",s);
-
+ snprintf(buffer,MAXBUF,"%s %s",CreateSum().c_str(),s);
+
log(DEBUG,"NetSendToCommon: '%s' '%s'",u->nick,s);
std::string msg = buffer;
void NetSendToAll(char* s)
{
char buffer[MAXBUF];
- snprintf(buffer,MAXBUF,"%s",s);
+ snprintf(buffer,MAXBUF,"%s %s",CreateSum().c_str(),s);
log(DEBUG,"NetSendToAll: '%s'",s);
}
}
+void NetSendToAll_WithSum(char* s,char* u)
+{
+ char buffer[MAXBUF];
+ snprintf(buffer,MAXBUF,":%s %s",u,s);
+
+ log(DEBUG,"NetSendToAll: '%s'",s);
+
+ std::string msg = buffer;
+ FOREACH_MOD OnPacketTransmit(msg,s);
+ strlcpy(buffer,msg.c_str(),MAXBUF);
+
+ for (int j = 0; j < 32; j++)
+ {
+ if (me[j] != NULL)
+ {
+ for (int k = 0; k < me[j]->connectors.size(); k++)
+ {
+ me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
+ }
+ }
+ }
+}
+
void NetSendToAllAlive(char* s)
{
char buffer[MAXBUF];
- snprintf(buffer,MAXBUF,"%s",s);
+ snprintf(buffer,MAXBUF,"%s %s",CreateSum().c_str(),s);
log(DEBUG,"NetSendToAllAlive: '%s'",s);
void NetSendToOne(char* target,char* s)
{
char buffer[MAXBUF];
- snprintf(buffer,MAXBUF,"%s",s);
+ snprintf(buffer,MAXBUF,"%s %s",CreateSum().c_str(),s);
log(DEBUG,"NetSendToOne: '%s' '%s'",target,s);
void NetSendToAllExcept(const char* target,char* s)
{
char buffer[MAXBUF];
- snprintf(buffer,MAXBUF,"%s",s);
+ snprintf(buffer,MAXBUF,"%s %s",CreateSum().c_str(),s);
log(DEBUG,"NetSendToAllExcept: '%s' '%s'",target,s);
}
}
+void NetSendToAllExcept_WithSum(const char* target,char* s,char* u)
+{
+ char buffer[MAXBUF];
+ snprintf(buffer,MAXBUF,":%s %s",u,s);
+
+ log(DEBUG,"NetSendToAllExcept: '%s' '%s'",target,s);
+
+ std::string msg = buffer;
+ FOREACH_MOD OnPacketTransmit(msg,s);
+ strlcpy(buffer,msg.c_str(),MAXBUF);
+
+ for (int j = 0; j < 32; j++)
+ {
+ if (me[j] != NULL)
+ {
+ for (int k = 0; k < me[j]->connectors.size(); k++)
+ {
+ if (strcasecmp(me[j]->connectors[k].GetServerName().c_str(),target))
+ {
+ me[j]->SendPacket(buffer,me[j]->connectors[k].GetServerName().c_str());
+ }
+ }
+ }
+ }
+}
+
void WriteMode(const char* modes, int flags, const char* text, ...)
{
strcpy(scratch,"");
strcpy(sparam,"");
- if (chan->noexternal)
+ if (chan->binarymodes & CM_NOEXTERNAL)
{
strlcat(scratch,"n",MAXMODES);
}
- if (chan->topiclock)
+ if (chan->binarymodes & CM_TOPICLOCK)
{
strlcat(scratch,"t",MAXMODES);
}
{
strlcat(scratch,"l",MAXMODES);
}
- if (chan->inviteonly)
+ if (chan->binarymodes & CM_INVITEONLY)
{
strlcat(scratch,"i",MAXMODES);
}
- if (chan->moderated)
+ if (chan->binarymodes & CM_MODERATED)
{
strlcat(scratch,"m",MAXMODES);
}
- if (chan->secret)
+ if (chan->binarymodes & CM_SECRET)
{
strlcat(scratch,"s",MAXMODES);
}
- if (chan->c_private)
+ if (chan->binarymodes & CM_PRIVATE)
{
strlcat(scratch,"p",MAXMODES);
}
chanlist[cname] = new chanrec();
strlcpy(chanlist[cname]->name, cname,CHANMAX);
- chanlist[cname]->topiclock = 1;
- chanlist[cname]->noexternal = 1;
+ chanlist[cname]->binarymodes = CM_TOPICLOCK | CM_NOEXTERNAL;
chanlist[cname]->created = TIME;
strcpy(chanlist[cname]->topic, "");
strncpy(chanlist[cname]->setby, user->nick,NICKMAX);
FOREACH_RESULT(OnCheckInvite(user, Ptr));
if (MOD_RESULT == 0)
{
- if (Ptr->inviteonly)
+ if (Ptr->binarymodes & CM_INVITEONLY)
{
log(DEBUG,"add_channel: channel is +i");
if (user->IsInvited(Ptr->name))
user->chans[index].uc_modes = 0;
}
user->chans[index].channel = Ptr;
- Ptr->IncUserCounter();
Ptr->AddUser((char*)user);
WriteChannel(Ptr,user,"JOIN :%s",Ptr->name);
log(DEBUG,"deleting user hash value %lu",(unsigned long)user);
if (user->fd > -1)
fd_ref_table[user->fd] = NULL;
- delete user;
clientlist.erase(iter);
}
+ delete user;
}
void kill_link_silent(userrec *user,const char* r)
log(DEBUG,"deleting user hash value %lu",(unsigned long)user);
if (user->fd > -1)
fd_ref_table[user->fd] = NULL;
- delete user;
clientlist.erase(iter);
}
+ delete user;
}
log(DEBUG,"ReHashNick: Found hashed nick %s",Old);
- clientlist[New] = new userrec();
- clientlist[New] = oldnick->second;
+ userrec* olduser = oldnick->second;
+ clientlist[New] = olduser;
clientlist.erase(oldnick);
log(DEBUG,"ReHashNick: Nick rehashed as %s",New);
/* adds or updates an entry in the whowas list */
void AddWhoWas(userrec* u)
{
- user_hash::iterator iter = whowas.find(u->nick);
- userrec *a = new userrec();
+ whowas_hash::iterator iter = whowas.find(u->nick);
+ WhoWasUser *a = new WhoWasUser();
strlcpy(a->nick,u->nick,NICKMAX);
- strlcpy(a->ident,u->ident,64);
- strlcpy(a->dhost,u->dhost,256);
- strlcpy(a->host,u->host,256);
+ strlcpy(a->ident,u->ident,15);
+ strlcpy(a->dhost,u->dhost,160);
+ strlcpy(a->host,u->host,160);
strlcpy(a->fullname,u->fullname,128);
strlcpy(a->server,u->server,256);
a->signon = u->signon;
if (iter == whowas.end())
{
- if (whowas.size() == WHOWAS_MAX)
+ if (whowas.size() >= WHOWAS_MAX)
{
- for (user_hash::iterator i = whowas.begin(); i != whowas.end(); i++)
+ for (whowas_hash::iterator i = whowas.begin(); i != whowas.end(); i++)
{
// 3600 seconds in an hour ;)
if ((i->second->signon)<(TIME-(WHOWAS_STALE*3600)))
{
+ // delete the old one
if (i->second) delete i->second;
+ // replace with new one
i->second = a;
log(DEBUG,"added WHOWAS entry, purged an old record");
return;
}
}
+ // no space left and user doesnt exist. Don't leave ram in use!
+ log(DEBUG,"Not able to update whowas (list at WHOWAS_MAX entries and trying to add new?), freeing excess ram");
+ delete a;
}
else
{
// issue in earlier alphas/betas
if (iter != clientlist.end())
{
+ userrec* goner = iter->second;
+ delete goner;
clientlist.erase(iter);
}
strncpy(clientlist[tempnick]->host, host,160);
strncpy(clientlist[tempnick]->dhost, host,160);
strncpy(clientlist[tempnick]->server, ServerName,256);
- strncpy(clientlist[tempnick]->ident, "unknown",12);
+ strncpy(clientlist[tempnick]->ident, "unknown",15);
clientlist[tempnick]->registered = 0;
clientlist[tempnick]->signon = TIME+dns_timeout;
clientlist[tempnick]->lastping = 1;
clientlist[tempnick]->port = port;
- strncpy(clientlist[tempnick]->ip,ip,32);
+ strncpy(clientlist[tempnick]->ip,ip,16);
// set the registration timeout for this user
unsigned long class_regtimeout = 90;
int class_flood = 0;
long class_threshold = 5;
+ long class_sqmax = 262144; // 256kb
+ long class_rqmax = 4096; // 4k
for (ClassVector::iterator i = Classes.begin(); i != Classes.end(); i++)
{
class_flood = i->flood;
clientlist[tempnick]->pingmax = i->pingtime;
class_threshold = i->threshold;
+ class_sqmax = i->sendqmax;
+ class_rqmax = i->recvqmax;
break;
}
}
clientlist[tempnick]->timeout = TIME+class_regtimeout;
clientlist[tempnick]->flood = class_flood;
clientlist[tempnick]->threshold = class_threshold;
+ clientlist[tempnick]->sendqmax = class_sqmax;
+ clientlist[tempnick]->recvqmax = class_rqmax;
for (int i = 0; i < MAXCHANS; i++)
{
// irc server at once (or the irc server otherwise initiating this many connections, files etc)
// which for the time being is a physical impossibility (even the largest networks dont have more
// than about 10,000 users on ONE server!)
- if (socket > 65535)
+ if (socket > 65534)
{
kill_link(clientlist[tempnick],"Server is full");
return;
snprintf(buf,65535,":%s 376 %s :End of message of the day.\r\n", ServerName, user->nick);
WholeMOTD = WholeMOTD + buf;
// only one write operation
- send(user->fd,WholeMOTD.c_str(),WholeMOTD.length(),0);
+ user->AddWriteBuf(WholeMOTD);
statsSent += WholeMOTD.length();
}
// send start of sync marker: Y <timestamp>
// at this point the ircd receiving it starts broadcasting this netburst to all ircds
// except the ones its receiving it from.
- snprintf(data,MAXBUF,"Y %lu",(unsigned long)TIME);
+ snprintf(data,MAXBUF,"%s Y %lu",CreateSum().c_str(),(unsigned long)TIME);
serv->SendPacket(data,tcp_host);
// send users and channels
{
if (is_uline(me[j]->connectors[k].GetServerName().c_str()))
{
- snprintf(data,MAXBUF,"H %s",me[j]->connectors[k].GetServerName().c_str());
+ snprintf(data,MAXBUF,"%s H %s",CreateSum().c_str(),me[j]->connectors[k].GetServerName().c_str());
serv->SendPacket(data,tcp_host);
}
}
}
// send our version for the remote side to cache
- snprintf(data,MAXBUF,"v %s %s",ServerName,GetVersionString().c_str());
+ snprintf(data,MAXBUF,"%s v %s %s",CreateSum().c_str(),ServerName,GetVersionString().c_str());
serv->SendPacket(data,tcp_host);
// sync the users and channels, give the modules a look-in.
for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++)
{
- snprintf(data,MAXBUF,"N %lu %s %s %s %s +%s %s %s :%s",(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->modes,u->second->ip,u->second->server,u->second->fullname);
+ snprintf(data,MAXBUF,"%s N %lu %s %s %s %s +%s %s %s :%s",CreateSum().c_str(),(unsigned long)u->second->age,u->second->nick,u->second->host,u->second->dhost,u->second->ident,u->second->modes,u->second->ip,u->second->server,u->second->fullname);
serv->SendPacket(data,tcp_host);
if (strchr(u->second->modes,'o'))
{
- snprintf(data,MAXBUF,"| %s %s",u->second->nick,u->second->oper);
+ snprintf(data,MAXBUF,"%s | %s %s",CreateSum().c_str(),u->second->nick,u->second->oper);
serv->SendPacket(data,tcp_host);
}
for (int i = 0; i <= MODCOUNT; i++)
string_list l = modules[i]->OnUserSync(u->second);
for (int j = 0; j < l.size(); j++)
{
- strlcpy(data,l[j].c_str(),MAXBUF);
+ snprintf(data,MAXBUF,"%s %s",CreateSum().c_str(),l[j].c_str());
serv->SendPacket(data,tcp_host);
}
}
char* chl = chlist(u->second,u->second);
if (strcmp(chl,""))
{
- snprintf(data,MAXBUF,"J %s %s",u->second->nick,chl);
+ snprintf(data,MAXBUF,"%s J %s %s",CreateSum().c_str(),u->second->nick,chl);
serv->SendPacket(data,tcp_host);
}
}
string_list l = modules[i]->OnChannelSync(c->second);
for (int j = 0; j < l.size(); j++)
{
- strlcpy(data,l[j].c_str(),MAXBUF);
+ snprintf(data,MAXBUF,"%s %s",CreateSum().c_str(),l[j].c_str());
serv->SendPacket(data,tcp_host);
}
}
if (c->second->topic[0])
{
- snprintf(data,MAXBUF,"T %lu %s %s :%s",(unsigned long)c->second->topicset,c->second->setby,c->second->name,c->second->topic);
+ snprintf(data,MAXBUF,"%s T %lu %s %s :%s",CreateSum().c_str(),(unsigned long)c->second->topicset,c->second->setby,c->second->name,c->second->topic);
serv->SendPacket(data,tcp_host);
}
// send current banlist
for (BanList::iterator b = c->second->bans.begin(); b != c->second->bans.end(); b++)
{
- snprintf(data,MAXBUF,"M %s +b %s",c->second->name,b->data);
+ snprintf(data,MAXBUF,"%s M %s +b %s",CreateSum().c_str(),c->second->name,b->data);
serv->SendPacket(data,tcp_host);
}
}
// sync global zlines, glines, etc
sync_xlines(serv,tcp_host);
- snprintf(data,MAXBUF,"F %lu",(unsigned long)TIME);
+ snprintf(data,MAXBUF,"%s F %lu",CreateSum().c_str(),(unsigned long)TIME);
serv->SendPacket(data,tcp_host);
log(DEBUG,"Sent sync");
// ircd sends its serverlist after the end of sync here
WritePID(PID);
length = sizeof (client);
- char tcp_msg[MAXBUF],tcp_host[MAXBUF];
+ char tcp_msg[MAXBUF],tcp_host[MAXBUF],tcp_sum[MAXBUF];
#ifdef USE_KQUEUE
struct kevent ke;
for (int x = 0; x < SERVERportCount; x++)
{
std::deque<std::string> msgs;
+ std::deque<std::string> sums;
msgs.clear();
- if ((me[x]) && (me[x]->RecvPacket(msgs, tcp_host)))
+ sums.clear();
+ if ((me[x]) && (me[x]->RecvPacket(msgs, tcp_host, sums)))
{
for (int ctr = 0; ctr < msgs.size(); ctr++)
{
strlcpy(tcp_msg,msgs[ctr].c_str(),MAXBUF);
+ strlcpy(tcp_sum,msgs[ctr].c_str(),MAXBUF);
log(DEBUG,"Processing: %s",tcp_msg);
if (!tcp_msg[0])
{
{
if ((tcp_msg[0] != 'Y') && (tcp_msg[0] != 'X') && (tcp_msg[0] != 'F'))
{
- NetSendToAllExcept(tcp_host,tcp_msg);
+ NetSendToAllExcept_WithSum(tcp_host,tcp_msg,tcp_sum);
}
}
else
- NetSendToAllExcept(tcp_host,tcp_msg);
+ NetSendToAllExcept_WithSum(tcp_host,tcp_msg,tcp_sum);
}
std::string msg = tcp_msg;
FOREACH_MOD OnPacketReceive(msg,tcp_host);
strlcpy(tcp_msg,msg.c_str(),MAXBUF);
- handle_link_packet(tcp_msg, tcp_host, me[x]);
+ handle_link_packet(tcp_msg, tcp_host, me[x], tcp_sum);
}
goto label;
}
// we don't check the state of remote users.
if ((curr->fd != -1) && (curr->fd != FD_MAGIC_NUMBER))
{
+ curr->FlushWriteBuf();
+ if (curr->GetWriteError() != "")
+ {
+ log(DEBUG,"InspIRCd: write error: %s",curr->GetWriteError().c_str());
+ kill_link(curr,curr->GetWriteError().c_str());
+ goto label;
+ }
+
FD_SET (curr->fd, &sfd);
// registration timeout -- didnt send USER/NICK/HOST in the time specified in
// we don't check the state of remote users.
if ((curr->fd != -1) && (curr->fd != FD_MAGIC_NUMBER))
{
+
+ curr->FlushWriteBuf();
+ if (curr->GetWriteError() != "")
+ {
+ log(DEBUG,"InspIRCd: write error: %s",curr->GetWriteError().c_str());
+ kill_link(curr,curr->GetWriteError().c_str());
+ goto label;
+ }
+
// registration timeout -- didnt send USER/NICK/HOST in the time specified in
// their connection class.
if ((TIME > curr->timeout) && (curr->registered != 7))