2 * ircd-ratbox: A slightly useful ircd.
3 * packet.c: Packet handlers.
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
34 #include "irc_string.h"
39 static char readBuf
[READBUF_SIZE
];
40 static void client_dopacket(struct Client
*client_p
, char *buffer
, size_t length
);
44 * parse_client_queued - parse client queued messages
47 parse_client_queued(struct Client
*client_p
)
52 if(IsAnyDead(client_p
))
55 if(IsUnknown(client_p
))
61 /* rate unknown clients at MAX_FLOOD per loop */
65 dolen
= linebuf_get(&client_p
->localClient
->
66 buf_recvq
, readBuf
, READBUF_SIZE
,
67 LINEBUF_COMPLETE
, LINEBUF_PARSED
);
69 if(dolen
<= 0 || IsDead(client_p
))
72 client_dopacket(client_p
, readBuf
, dolen
);
76 if(IsAnyDead(client_p
))
78 /* if theyve dropped out of the unknown state, break and move
79 * to the parsing for their appropriate status. --fl
81 if(!IsUnknown(client_p
))
87 if(IsAnyServer(client_p
) || IsExemptFlood(client_p
))
89 while (!IsAnyDead(client_p
) && (dolen
= linebuf_get(&client_p
->localClient
->buf_recvq
,
90 readBuf
, READBUF_SIZE
, LINEBUF_COMPLETE
,
93 client_dopacket(client_p
, readBuf
, dolen
);
96 else if(IsClient(client_p
))
99 if(IsOper(client_p
) && ConfigFileEntry
.no_oper_flood
)
102 * Handle flood protection here - if we exceed our flood limit on
103 * messages in this loop, we simply drop out of the loop prematurely.
108 /* This flood protection works as follows:
110 * A client is given allow_read lines to send to the server. Every
111 * time a line is parsed, sent_parsed is increased. sent_parsed
112 * is decreased by 1 every time flood_recalc is called.
114 * Thus a client can 'burst' allow_read lines to the server, any
115 * excess lines will be parsed one per flood_recalc() call.
117 * Therefore a client will be penalised more if they keep flooding,
118 * as sent_parsed will always hover around the allow_read limit
119 * and no 'bursts' will be permitted.
123 if(client_p
->localClient
->sent_parsed
>= client_p
->localClient
->allow_read
)
127 /* allow opers 4 times the amount of messages as users. why 4?
130 else if(client_p
->localClient
->sent_parsed
>= (4 * client_p
->localClient
->allow_read
))
133 dolen
= linebuf_get(&client_p
->localClient
->
134 buf_recvq
, readBuf
, READBUF_SIZE
,
135 LINEBUF_COMPLETE
, LINEBUF_PARSED
);
140 client_dopacket(client_p
, readBuf
, dolen
);
141 if(IsAnyDead(client_p
))
143 client_p
->localClient
->sent_parsed
++;
150 * marks the end of the clients grace period
153 flood_endgrace(struct Client
*client_p
)
155 SetFloodDone(client_p
);
157 /* Drop their flood limit back down */
158 client_p
->localClient
->allow_read
= MAX_FLOOD
;
160 /* sent_parsed could be way over MAX_FLOOD but under MAX_FLOOD_BURST,
163 client_p
->localClient
->sent_parsed
= 0;
169 * recalculate the number of allowed flood lines. this should be called
170 * once a second on any given client. We then attempt to flush some data.
173 flood_recalc(int fd
, void *data
)
175 struct Client
*client_p
= data
;
176 struct LocalUser
*lclient_p
= client_p
->localClient
;
178 /* This can happen in the event that the client detached. */
182 /* allow a bursting client their allocation per second, allow
183 * a client whos flooding an extra 2 per second
185 if(IsFloodDone(client_p
))
186 lclient_p
->sent_parsed
-= 2;
188 lclient_p
->sent_parsed
= 0;
190 if(lclient_p
->sent_parsed
< 0)
191 lclient_p
->sent_parsed
= 0;
193 if(--lclient_p
->actually_read
< 0)
194 lclient_p
->actually_read
= 0;
196 parse_client_queued(client_p
);
198 if(IsAnyDead(client_p
))
201 /* and finally, reset the flood check */
202 comm_setflush(fd
, 1000, flood_recalc
, client_p
);
206 * read_ctrl_packet - Read a 'packet' of data from a servlink control
207 * link and process it.
210 read_ctrl_packet(int fd
, void *data
)
212 struct Client
*server
= data
;
213 struct LocalUser
*lserver
= server
->localClient
;
214 struct SlinkRpl
*reply
;
216 unsigned char tmp
[2];
217 unsigned char *len
= tmp
;
218 struct SlinkRplDef
*replydef
;
219 #ifdef USE_IODEBUG_HOOKS
223 s_assert(lserver
!= NULL
);
224 if(IsAnyDead(server
))
227 reply
= &lserver
->slinkrpl
;
232 reply
->gotdatalen
= 0;
236 length
= read(fd
, tmp
, 1);
240 if((length
== -1) && ignoreErrno(errno
))
242 error_exit_client(server
, length
);
246 reply
->command
= tmp
[0];
249 for (replydef
= slinkrpltab
; replydef
->handler
; replydef
++)
251 if((int)replydef
->replyid
== reply
->command
)
255 /* we should be able to trust a local slink process...
256 * and if it sends an invalid command, that's a bug.. */
257 s_assert(replydef
->handler
);
259 if((replydef
->flags
& SLINKRPL_FLAG_DATA
) && (reply
->gotdatalen
< 2))
261 /* we need a datalen u16 which we don't have yet... */
262 length
= read(fd
, len
, (2 - reply
->gotdatalen
));
265 if((length
== -1) && ignoreErrno(errno
))
267 error_exit_client(server
, length
);
271 if(reply
->gotdatalen
== 0)
273 reply
->datalen
= *len
<< 8;
278 if(length
&& (reply
->gotdatalen
== 1))
280 reply
->datalen
|= *len
;
282 if(reply
->datalen
> 0)
283 reply
->data
= MyMalloc(reply
->datalen
);
286 if(reply
->gotdatalen
< 2)
287 return; /* wait for more data */
290 if(reply
->readdata
< reply
->datalen
) /* try to get any remaining data */
292 length
= read(fd
, (reply
->data
+ reply
->readdata
),
293 (reply
->datalen
- reply
->readdata
));
296 if((length
== -1) && ignoreErrno(errno
))
298 error_exit_client(server
, length
);
302 reply
->readdata
+= length
;
303 if(reply
->readdata
< reply
->datalen
)
304 return; /* wait for more data */
307 #ifdef USE_IODEBUG_HOOKS
308 hdata
.client
= server
;
310 hdata
.arg2
= reply
->command
;
312 call_hook(h_iorecvctrl_id
, &hdata
);
315 /* we now have the command and any data, pass it off to the handler */
316 (*replydef
->handler
) (reply
->command
, reply
->datalen
, reply
->data
, server
);
319 if(reply
->datalen
> 0)
323 if(IsAnyDead(server
))
327 /* If we get here, we need to register for another COMM_SELECT_READ */
328 comm_setselect(fd
, FDLIST_SERVER
, COMM_SELECT_READ
, read_ctrl_packet
, server
, 0);
332 * read_packet - Read a 'packet' of data from a connection and process it.
335 read_packet(int fd
, void *data
)
337 struct Client
*client_p
= data
;
338 struct LocalUser
*lclient_p
= client_p
->localClient
;
343 #ifdef USE_IODEBUG_HOOKS
346 if(IsAnyDead(client_p
))
350 * Read some data. We *used to* do anti-flood protection here, but
351 * I personally think it makes the code too hairy to make sane.
354 length
= read(client_p
->localClient
->fd
, readBuf
, READBUF_SIZE
);
358 if((length
== -1) && ignoreErrno(errno
))
360 comm_setselect(client_p
->localClient
->fd
, FDLIST_IDLECLIENT
,
361 COMM_SELECT_READ
, read_packet
, client_p
, 0);
364 error_exit_client(client_p
, length
);
368 #ifdef USE_IODEBUG_HOOKS
369 hdata
.client
= client_p
;
370 hdata
.arg1
= readBuf
;
372 call_hook(h_iorecv_id
, &hdata
);
375 if(client_p
->localClient
->lasttime
< CurrentTime
)
376 client_p
->localClient
->lasttime
= CurrentTime
;
377 client_p
->flags
&= ~FLAGS_PINGSENT
;
380 * Before we even think of parsing what we just read, stick
381 * it on the end of the receive queue and do it when its
384 if(IsHandshake(client_p
) || IsUnknown(client_p
))
387 lbuf_len
= linebuf_parse(&client_p
->localClient
->buf_recvq
, readBuf
, length
, binary
);
389 lclient_p
->actually_read
+= lbuf_len
;
391 if(IsAnyDead(client_p
))
394 /* Attempt to parse what we have */
395 parse_client_queued(client_p
);
397 if(IsAnyDead(client_p
))
400 /* Check to make sure we're not flooding */
401 if(!IsAnyServer(client_p
) &&
402 (linebuf_alloclen(&client_p
->localClient
->buf_recvq
) > ConfigFileEntry
.client_flood
))
404 if(!(ConfigFileEntry
.no_oper_flood
&& IsOper(client_p
)))
406 exit_client(client_p
, client_p
, client_p
, "Excess Flood");
411 /* If we get here, we need to register for another COMM_SELECT_READ */
412 if(PARSE_AS_SERVER(client_p
))
414 comm_setselect(client_p
->localClient
->fd
, FDLIST_SERVER
, COMM_SELECT_READ
,
415 read_packet
, client_p
, 0);
419 comm_setselect(client_p
->localClient
->fd
, FDLIST_IDLECLIENT
,
420 COMM_SELECT_READ
, read_packet
, client_p
, 0);
425 * client_dopacket - copy packet to client buf and parse it
426 * client_p - pointer to client structure for which the buffer data
428 * buffer - pointr to the buffer containing the newly read data
429 * length - number of valid bytes of data in the buffer
432 * It is implicitly assumed that dopacket is called only
433 * with client_p of "local" variation, which contains all the
434 * necessary fields (buffer etc..)
437 client_dopacket(struct Client
*client_p
, char *buffer
, size_t length
)
439 s_assert(client_p
!= NULL
);
440 s_assert(buffer
!= NULL
);
442 if(client_p
== NULL
|| buffer
== NULL
)
444 if(IsAnyDead(client_p
))
447 * Update messages received
449 ++me
.localClient
->receiveM
;
450 ++client_p
->localClient
->receiveM
;
453 * Update bytes received
455 client_p
->localClient
->receiveB
+= length
;
457 if(client_p
->localClient
->receiveB
> 1023)
459 client_p
->localClient
->receiveK
+= (client_p
->localClient
->receiveB
>> 10);
460 client_p
->localClient
->receiveB
&= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */
463 me
.localClient
->receiveB
+= length
;
465 if(me
.localClient
->receiveB
> 1023)
467 me
.localClient
->receiveK
+= (me
.localClient
->receiveB
>> 10);
468 me
.localClient
->receiveB
&= 0x03ff;
471 parse(client_p
, buffer
, buffer
+ length
);