-/*\r
- * ircd-ratbox: A slightly useful ircd.\r
- * packet.c: Packet handlers.\r
- *\r
- * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center\r
- * Copyright (C) 1996-2002 Hybrid Development Team\r
- * Copyright (C) 2002-2005 ircd-ratbox development team\r
- *\r
- * This program is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * This program is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301\r
- * USA\r
- *\r
- * $Id: packet.c 25179 2008-03-30 16:34:57Z androsyn $\r
- */\r
-#include "stdinc.h"\r
-#include "struct.h"\r
-#include "s_conf.h"\r
-#include "s_serv.h"\r
-#include "client.h"\r
-#include "ircd.h"\r
-#include "parse.h"\r
-#include "packet.h"\r
-#include "match.h"\r
-#include "hook.h"\r
-#include "send.h"\r
-#include "common.h"\r
-\r
-static char readBuf[READBUF_SIZE];\r
-static void client_dopacket(struct Client *client_p, char *buffer, size_t length);\r
-\r
-\r
-/*\r
- * parse_client_queued - parse client queued messages\r
- */\r
-static void\r
-parse_client_queued(struct Client *client_p)\r
-{\r
- int dolen = 0;\r
- int checkflood = 1;\r
-\r
- if(IsAnyDead(client_p))\r
- return;\r
-\r
- if(IsUnknown(client_p))\r
- {\r
- for (;;)\r
- {\r
- if(client_p->localClient->sent_parsed >= client_p->localClient->allow_read)\r
- break;\r
-\r
- dolen = rb_linebuf_get(&client_p->localClient->\r
- buf_recvq, readBuf, READBUF_SIZE,\r
- LINEBUF_COMPLETE, LINEBUF_PARSED);\r
-\r
- if(dolen <= 0 || IsDead(client_p))\r
- break;\r
-\r
- client_dopacket(client_p, readBuf, dolen);\r
- client_p->localClient->sent_parsed++;\r
-\r
- /* He's dead cap'n */\r
- if(IsAnyDead(client_p))\r
- return;\r
- /* if theyve dropped out of the unknown state, break and move\r
- * to the parsing for their appropriate status. --fl\r
- */\r
- if(!IsUnknown(client_p))\r
- {\r
- /* reset their flood limits, they're now\r
- * graced to flood\r
- */\r
- client_p->localClient->sent_parsed = 0;\r
- break;\r
- }\r
- }\r
- }\r
-\r
- if(IsAnyServer(client_p) || IsExemptFlood(client_p))\r
- {\r
- while (!IsAnyDead(client_p) && (dolen = rb_linebuf_get(&client_p->localClient->buf_recvq,\r
- readBuf, READBUF_SIZE, LINEBUF_COMPLETE,\r
- LINEBUF_PARSED)) > 0)\r
- {\r
- client_dopacket(client_p, readBuf, dolen);\r
- }\r
- }\r
- else if(IsClient(client_p))\r
- {\r
-\r
- if(IsOper(client_p) && ConfigFileEntry.no_oper_flood)\r
- checkflood = 0;\r
- /*\r
- * Handle flood protection here - if we exceed our flood limit on\r
- * messages in this loop, we simply drop out of the loop prematurely.\r
- * -- adrian\r
- */\r
- for (;;)\r
- {\r
- /* This flood protection works as follows:\r
- *\r
- * A client is given allow_read lines to send to the server. Every\r
- * time a line is parsed, sent_parsed is increased. sent_parsed\r
- * is decreased by 1 every time flood_recalc is called.\r
- *\r
- * Thus a client can 'burst' allow_read lines to the server, any\r
- * excess lines will be parsed one per flood_recalc() call.\r
- *\r
- * Therefore a client will be penalised more if they keep flooding,\r
- * as sent_parsed will always hover around the allow_read limit\r
- * and no 'bursts' will be permitted.\r
- */\r
- if(checkflood)\r
- {\r
- if(client_p->localClient->sent_parsed >= client_p->localClient->allow_read)\r
- break;\r
- }\r
-\r
- /* allow opers 4 times the amount of messages as users. why 4?\r
- * why not. :) --fl_\r
- */\r
- else if(client_p->localClient->sent_parsed >= (4 * client_p->localClient->allow_read))\r
- break;\r
-\r
- dolen = rb_linebuf_get(&client_p->localClient->\r
- buf_recvq, readBuf, READBUF_SIZE,\r
- LINEBUF_COMPLETE, LINEBUF_PARSED);\r
-\r
- if(!dolen)\r
- break;\r
-\r
- client_dopacket(client_p, readBuf, dolen);\r
- if(IsAnyDead(client_p))\r
- return;\r
- client_p->localClient->sent_parsed++;\r
- }\r
- }\r
-}\r
-\r
+/*
+ * ircd-ratbox: A slightly useful ircd.
+ * packet.c: Packet handlers.
+ *
+ * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
+ * Copyright (C) 1996-2002 Hybrid Development Team
+ * Copyright (C) 2002-2005 ircd-ratbox development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * $Id: packet.c 3446 2007-05-14 22:21:16Z jilles $
+ */
+#include "stdinc.h"
+#include "s_conf.h"
+#include "s_serv.h"
+#include "client.h"
+#include "common.h"
+#include "ircd.h"
+#include "parse.h"
+#include "packet.h"
+#include "irc_string.h"
+#include "hook.h"
+#include "send.h"
+
+static char readBuf[READBUF_SIZE];
+static void client_dopacket(struct Client *client_p, char *buffer, size_t length);
+
+
+/*
+ * parse_client_queued - parse client queued messages
+ */
+static void
+parse_client_queued(struct Client *client_p)
+{
+ int dolen = 0;
+ int checkflood = 1;
+
+ if(IsAnyDead(client_p))
+ return;
+
+ if(IsUnknown(client_p))
+ {
+ for (;;)
+ {
+ if(client_p->localClient->sent_parsed >= client_p->localClient->allow_read)
+ break;
+
+ dolen = rb_linebuf_get(&client_p->localClient->
+ buf_recvq, readBuf, READBUF_SIZE,
+ LINEBUF_COMPLETE, LINEBUF_PARSED);
+
+ if(dolen <= 0 || IsDead(client_p))
+ break;
+
+ client_dopacket(client_p, readBuf, dolen);
+ client_p->localClient->sent_parsed++;
+
+ /* He's dead cap'n */
+ if(IsAnyDead(client_p))
+ return;
+ /* if theyve dropped out of the unknown state, break and move
+ * to the parsing for their appropriate status. --fl
+ */
+ if(!IsUnknown(client_p))
+ {
+ /* reset their flood limits, they're now
+ * graced to flood
+ */
+ client_p->localClient->sent_parsed = 0;
+ break;
+ }
+
+ }
+ }
+
+ if(IsAnyServer(client_p) || IsExemptFlood(client_p))
+ {
+ while (!IsAnyDead(client_p) && (dolen = rb_linebuf_get(&client_p->localClient->buf_recvq,
+ readBuf, READBUF_SIZE, LINEBUF_COMPLETE,
+ LINEBUF_PARSED)) > 0)
+ {
+ client_dopacket(client_p, readBuf, dolen);
+ }
+ }
+ else if(IsClient(client_p))
+ {
+
+ if(IsOper(client_p) && ConfigFileEntry.no_oper_flood)
+ checkflood = 0;
+ /*
+ * Handle flood protection here - if we exceed our flood limit on
+ * messages in this loop, we simply drop out of the loop prematurely.
+ * -- adrian
+ */
+ for (;;)
+ {
+ /* This flood protection works as follows:
+ *
+ * A client is given allow_read lines to send to the server. Every
+ * time a line is parsed, sent_parsed is increased. sent_parsed
+ * is decreased by 1 every time flood_recalc is called.
+ *
+ * Thus a client can 'burst' allow_read lines to the server, any
+ * excess lines will be parsed one per flood_recalc() call.
+ *
+ * Therefore a client will be penalised more if they keep flooding,
+ * as sent_parsed will always hover around the allow_read limit
+ * and no 'bursts' will be permitted.
+ */
+ if(checkflood)
+ {
+ if(client_p->localClient->sent_parsed >= client_p->localClient->allow_read)
+ break;
+ }
+
+ /* allow opers 4 times the amount of messages as users. why 4?
+ * why not. :) --fl_
+ */
+ else if(client_p->localClient->sent_parsed >= (4 * client_p->localClient->allow_read))
+ break;
+
+ dolen = rb_linebuf_get(&client_p->localClient->
+ buf_recvq, readBuf, READBUF_SIZE,
+ LINEBUF_COMPLETE, LINEBUF_PARSED);
+
+ if(!dolen)
+ break;
+
+ client_dopacket(client_p, readBuf, dolen);
+ if(IsAnyDead(client_p))
+ return;
+ client_p->localClient->sent_parsed++;
+ }
+ }
+}
+
+/* flood_endgrace()
+ *
+ * marks the end of the clients grace period
+ */
+void
+flood_endgrace(struct Client *client_p)
+{
+ SetFloodDone(client_p);
+
+ /* Drop their flood limit back down */
+ client_p->localClient->allow_read = MAX_FLOOD;
+
+ /* sent_parsed could be way over MAX_FLOOD but under MAX_FLOOD_BURST,
+ * so reset it.
+ */
+ client_p->localClient->sent_parsed = 0;
+}
+
/*\r
* flood_recalc\r
*\r
client_p->localClient->actually_read = 0;\r
\r
parse_client_queued(client_p);\r
- }\r
-}\r
-\r
-\r
-/*\r
- * read_packet - Read a 'packet' of data from a connection and process it.\r
- */\r
-void\r
-read_packet(rb_fde_t *F, void *data)\r
-{\r
- struct Client *client_p = data;\r
- struct LocalUser *lclient_p = client_p->localClient;\r
- int length = 0;\r
- int lbuf_len;\r
-\r
- int binary = 0;\r
-#ifdef USE_IODEBUG_HOOKS\r
- hook_data_int hdata;\r
-#endif\r
-\r
-\r
- while(1) /* note..for things like rt sigio to work you *must* loop on read until you get EAGAIN */\r
- {\r
- if(IsAnyDead(client_p))\r
- return;\r
-\r
- /*\r
- * Read some data. We *used to* do anti-flood protection here, but\r
- * I personally think it makes the code too hairy to make sane.\r
- * -- adrian\r
- */\r
- length = rb_read(client_p->localClient->F, readBuf, READBUF_SIZE);\r
- if(length < 0)\r
- {\r
- if(rb_ignore_errno(errno))\r
- {\r
- rb_setselect(client_p->localClient->F, \r
- RB_SELECT_READ, read_packet, client_p);\r
- } else\r
- error_exit_client(client_p, length);\r
- return;\r
- } else\r
- if(length == 0)\r
- {\r
- error_exit_client(client_p, length);\r
- return;\r
- }\r
- \r
-#ifdef USE_IODEBUG_HOOKS\r
- hdata.client = client_p;\r
- hdata.arg1 = readBuf;\r
- hdata.arg2 = length;\r
- call_hook(h_iorecv_id, &hdata);\r
-#endif\r
-\r
- if(client_p->localClient->lasttime < rb_current_time())\r
- client_p->localClient->lasttime = rb_current_time();\r
- client_p->flags &= ~FLAGS_PINGSENT;\r
-\r
- /*\r
- * Before we even think of parsing what we just read, stick\r
- * it on the end of the receive queue and do it when its\r
- * turn comes around.\r
- */\r
- if(IsHandshake(client_p) || IsUnknown(client_p))\r
- binary = 1;\r
-\r
- lbuf_len = rb_linebuf_parse(&client_p->localClient->buf_recvq, readBuf, length, binary);\r
-\r
- lclient_p->actually_read += lbuf_len;\r
-\r
- if(IsAnyDead(client_p))\r
- return;\r
- \r
- /* Attempt to parse what we have */\r
- parse_client_queued(client_p);\r
-\r
- if(IsAnyDead(client_p))\r
- return;\r
- \r
- /* Check to make sure we're not flooding */\r
- if(!IsAnyServer(client_p) &&\r
- (rb_linebuf_alloclen(&client_p->localClient->buf_recvq) > ConfigFileEntry.client_flood))\r
- {\r
- if(!(ConfigFileEntry.no_oper_flood && IsOper(client_p)))\r
- {\r
- exit_client(client_p, client_p, client_p, "Excess Flood");\r
- return;\r
- }\r
- \r
- }\r
-\r
- /* bail if short read */\r
- if(length < READBUF_SIZE)\r
- {\r
- rb_setselect(client_p->localClient->F, RB_SELECT_READ, read_packet, client_p);\r
- return;\r
- }\r
}\r
-}\r
-\r
-/*\r
- * client_dopacket - copy packet to client buf and parse it\r
- * client_p - pointer to client structure for which the buffer data\r
- * applies.\r
- * buffer - pointr to the buffer containing the newly read data\r
- * length - number of valid bytes of data in the buffer\r
- *\r
- * Note:\r
- * It is implicitly assumed that dopacket is called only\r
- * with client_p of "local" variation, which contains all the\r
- * necessary fields (buffer etc..)\r
- */\r
-void\r
-client_dopacket(struct Client *client_p, char *buffer, size_t length)\r
-{\r
- s_assert(client_p != NULL);\r
- s_assert(buffer != NULL);\r
-\r
- if(client_p == NULL || buffer == NULL)\r
- return;\r
- if(IsAnyDead(client_p))\r
- return;\r
- /* \r
- * Update messages received\r
- */\r
- ++me.localClient->receiveM;\r
- ++client_p->localClient->receiveM;\r
-\r
- /* \r
- * Update bytes received\r
- */\r
- client_p->localClient->receiveB += length;\r
- me.localClient->receiveB += length;\r
-\r
- parse(client_p, buffer, buffer + length);\r
-}\r
-\r
-/* flood_endgrace()\r
- *\r
- * marks the end of the clients grace period\r
- */\r
-void\r
-flood_endgrace(struct Client *client_p)\r
-{\r
- SetFloodDone(client_p);\r
-\r
- /* Drop their flood limit back down */\r
- client_p->localClient->allow_read = MAX_FLOOD;\r
-\r
- /* sent_parsed could be way over MAX_FLOOD but under MAX_FLOOD_BURST,\r
- * so reset it.\r
- */\r
- client_p->localClient->sent_parsed = 0;\r
-}\r
+}
+
+/*
+ * read_ctrl_packet - Read a 'packet' of data from a servlink control
+ * link and process it.
+ */
+void
+read_ctrl_packet(int fd, void *data)
+{
+ struct Client *server = data;
+ struct LocalUser *lserver = server->localClient;
+ struct SlinkRpl *reply;
+ int length = 0;
+ unsigned char tmp[2];
+ unsigned char *len = tmp;
+ struct SlinkRplDef *replydef;
+#ifdef USE_IODEBUG_HOOKS
+ hook_data_int hdata;
+#endif
+
+ s_assert(lserver != NULL);
+ if(IsAnyDead(server))
+ return;
+
+ reply = &lserver->slinkrpl;
+
+
+ if(!reply->command)
+ {
+ reply->gotdatalen = 0;
+ reply->readdata = 0;
+ reply->data = NULL;
+
+ length = read(fd, tmp, 1);
+
+ if(length <= 0)
+ {
+ if((length == -1) && ignoreErrno(errno))
+ goto nodata;
+ error_exit_client(server, length);
+ return;
+ }
+
+ reply->command = tmp[0];
+ }
+
+ for (replydef = slinkrpltab; replydef->handler; replydef++)
+ {
+ if((int)replydef->replyid == reply->command)
+ break;
+ }
+
+ /* we should be able to trust a local slink process...
+ * and if it sends an invalid command, that's a bug.. */
+ s_assert(replydef->handler);
+
+ if((replydef->flags & SLINKRPL_FLAG_DATA) && (reply->gotdatalen < 2))
+ {
+ /* we need a datalen u16 which we don't have yet... */
+ length = read(fd, len, (2 - reply->gotdatalen));
+ if(length <= 0)
+ {
+ if((length == -1) && ignoreErrno(errno))
+ goto nodata;
+ error_exit_client(server, length);
+ return;
+ }
+
+ if(reply->gotdatalen == 0)
+ {
+ reply->datalen = *len << 8;
+ reply->gotdatalen++;
+ length--;
+ len++;
+ }
+ if(length && (reply->gotdatalen == 1))
+ {
+ reply->datalen |= *len;
+ reply->gotdatalen++;
+ if(reply->datalen > 0)
+ reply->data = rb_malloc(reply->datalen);
+ }
+
+ if(reply->gotdatalen < 2)
+ return; /* wait for more data */
+ }
+
+ if(reply->readdata < reply->datalen) /* try to get any remaining data */
+ {
+ length = read(fd, (reply->data + reply->readdata),
+ (reply->datalen - reply->readdata));
+ if(length <= 0)
+ {
+ if((length == -1) && ignoreErrno(errno))
+ goto nodata;
+ error_exit_client(server, length);
+ return;
+ }
+
+ reply->readdata += length;
+ if(reply->readdata < reply->datalen)
+ return; /* wait for more data */
+ }
+
+#ifdef USE_IODEBUG_HOOKS
+ hdata.client = server;
+ hdata.arg1 = NULL;
+ hdata.arg2 = reply->command;
+ hdata.data = NULL;
+ call_hook(h_iorecvctrl_id, &hdata);
+#endif
+
+ /* we now have the command and any data, pass it off to the handler */
+ (*replydef->handler) (reply->command, reply->datalen, reply->data, server);
+
+ /* reset SlinkRpl */
+ if(reply->datalen > 0)
+ rb_free(reply->data);
+ reply->command = 0;
+
+ if(IsAnyDead(server))
+ return;
+
+ nodata:
+ /* If we get here, we need to register for another COMM_SELECT_READ */
+ rb_setselect(fd, FDLIST_SERVER, COMM_SELECT_READ, read_ctrl_packet, server, 0);
+}
+
+/*
+ * read_packet - Read a 'packet' of data from a connection and process it.
+ */
+void
+read_packet(int fd, void *data)
+{
+ struct Client *client_p = data;
+ struct LocalUser *lclient_p = client_p->localClient;
+ int length = 0;
+ int lbuf_len;
+
+ int binary = 0;
+#ifdef USE_IODEBUG_HOOKS
+ hook_data_int hdata;
+#endif
+ if(IsAnyDead(client_p))
+ return;
+
+ /*
+ * Read some data. We *used to* do anti-flood protection here, but
+ * I personally think it makes the code too hairy to make sane.
+ * -- adrian
+ */
+ length = client_p->localClient->F->read_impl(client_p->localClient->F, readBuf, READBUF_SIZE);
+
+ if(length <= 0)
+ {
+ if((length == -1) && ignoreErrno(errno))
+ {
+ rb_setselect(client_p->localClient->F->fd, FDLIST_IDLECLIENT,
+ COMM_SELECT_READ, read_packet, client_p, 0);
+ return;
+ }
+ error_exit_client(client_p, length);
+ return;
+ }
+
+#ifdef USE_IODEBUG_HOOKS
+ hdata.client = client_p;
+ hdata.arg1 = readBuf;
+ hdata.arg2 = length;
+ call_hook(h_iorecv_id, &hdata);
+#endif
+
+ if(client_p->localClient->lasttime < rb_current_time())
+ client_p->localClient->lasttime = rb_current_time();
+ client_p->flags &= ~FLAGS_PINGSENT;
+
+ /*
+ * Before we even think of parsing what we just read, stick
+ * it on the end of the receive queue and do it when its
+ * turn comes around.
+ */
+ if(IsHandshake(client_p) || IsUnknown(client_p))
+ binary = 1;
+
+ lbuf_len = rb_linebuf_parse(&client_p->localClient->buf_recvq, readBuf, length, binary);
+
+ lclient_p->actually_read += lbuf_len;
+
+ if(IsAnyDead(client_p))
+ return;
+
+ /* Attempt to parse what we have */
+ parse_client_queued(client_p);
+
+ if(IsAnyDead(client_p))
+ return;
+
+ /* Check to make sure we're not flooding */
+ if(!IsAnyServer(client_p) &&
+ (rb_linebuf_alloclen(&client_p->localClient->buf_recvq) > ConfigFileEntry.client_flood))
+ {
+ if(!(ConfigFileEntry.no_oper_flood && IsOper(client_p)))
+ {
+ exit_client(client_p, client_p, client_p, "Excess Flood");
+ return;
+ }
+ }
+
+ /* If we get here, we need to register for another COMM_SELECT_READ */
+ if(PARSE_AS_SERVER(client_p))
+ {
+ rb_setselect(client_p->localClient->F->fd, FDLIST_SERVER, COMM_SELECT_READ,
+ read_packet, client_p, 0);
+ }
+ else
+ {
+ rb_setselect(client_p->localClient->F->fd, FDLIST_IDLECLIENT,
+ COMM_SELECT_READ, read_packet, client_p, 0);
+ }
+}
+
+/*
+ * client_dopacket - copy packet to client buf and parse it
+ * client_p - pointer to client structure for which the buffer data
+ * applies.
+ * buffer - pointr to the buffer containing the newly read data
+ * length - number of valid bytes of data in the buffer
+ *
+ * Note:
+ * It is implicitly assumed that dopacket is called only
+ * with client_p of "local" variation, which contains all the
+ * necessary fields (buffer etc..)
+ */
+void
+client_dopacket(struct Client *client_p, char *buffer, size_t length)
+{
+ s_assert(client_p != NULL);
+ s_assert(buffer != NULL);
+
+ if(client_p == NULL || buffer == NULL)
+ return;
+ if(IsAnyDead(client_p))
+ return;
+ /*
+ * Update messages received
+ */
+ ++me.localClient->receiveM;
+ ++client_p->localClient->receiveM;
+
+ /*
+ * Update bytes received
+ */
+ client_p->localClient->receiveB += length;
+
+ if(client_p->localClient->receiveB > 1023)
+ {
+ client_p->localClient->receiveK += (client_p->localClient->receiveB >> 10);
+ client_p->localClient->receiveB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */
+ }
+
+ me.localClient->receiveB += length;
+
+ if(me.localClient->receiveB > 1023)
+ {
+ me.localClient->receiveK += (me.localClient->receiveB >> 10);
+ me.localClient->receiveB &= 0x03ff;
+ }
+
+ parse(client_p, buffer, buffer + length);
+}