]> jfr.im git - solanum.git/blame - src/packet.c
forward configure.ac changes
[solanum.git] / src / packet.c
CommitLineData
4714a17f
VY
1/*\r
2 * ircd-ratbox: A slightly useful ircd.\r
3 * packet.c: Packet handlers.\r
4 *\r
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center\r
6 * Copyright (C) 1996-2002 Hybrid Development Team\r
7 * Copyright (C) 2002-2005 ircd-ratbox development team\r
8 *\r
9 * This program is free software; you can redistribute it and/or modify\r
10 * it under the terms of the GNU General Public License as published by\r
11 * the Free Software Foundation; either version 2 of the License, or\r
12 * (at your option) any later version.\r
13 *\r
14 * This program is distributed in the hope that it will be useful,\r
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
17 * GNU General Public License for more details.\r
18 *\r
19 * You should have received a copy of the GNU General Public License\r
20 * along with this program; if not, write to the Free Software\r
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301\r
22 * USA\r
23 *\r
24 * $Id: packet.c 25179 2008-03-30 16:34:57Z androsyn $\r
25 */\r
26#include "stdinc.h"\r
4714a17f
VY
27#include "s_conf.h"\r
28#include "s_serv.h"\r
29#include "client.h"\r
30#include "ircd.h"\r
31#include "parse.h"\r
32#include "packet.h"\r
4714a17f
VY
33#include "hook.h"\r
34#include "send.h"\r
e4d8860e 35#include "common.h"\r
4714a17f
VY
36\r
37static char readBuf[READBUF_SIZE];\r
38static void client_dopacket(struct Client *client_p, char *buffer, size_t length);\r
39\r
40\r
41/*\r
42 * parse_client_queued - parse client queued messages\r
43 */\r
44static void\r
45parse_client_queued(struct Client *client_p)\r
46{\r
47 int dolen = 0;\r
48 int checkflood = 1;\r
49\r
50 if(IsAnyDead(client_p))\r
51 return;\r
52\r
53 if(IsUnknown(client_p))\r
54 {\r
55 for (;;)\r
56 {\r
57 if(client_p->localClient->sent_parsed >= client_p->localClient->allow_read)\r
58 break;\r
59\r
60 dolen = rb_linebuf_get(&client_p->localClient->\r
61 buf_recvq, readBuf, READBUF_SIZE,\r
62 LINEBUF_COMPLETE, LINEBUF_PARSED);\r
63\r
64 if(dolen <= 0 || IsDead(client_p))\r
65 break;\r
66\r
67 client_dopacket(client_p, readBuf, dolen);\r
68 client_p->localClient->sent_parsed++;\r
69\r
70 /* He's dead cap'n */\r
71 if(IsAnyDead(client_p))\r
72 return;\r
73 /* if theyve dropped out of the unknown state, break and move\r
74 * to the parsing for their appropriate status. --fl\r
75 */\r
76 if(!IsUnknown(client_p))\r
77 {\r
78 /* reset their flood limits, they're now\r
79 * graced to flood\r
80 */\r
81 client_p->localClient->sent_parsed = 0;\r
82 break;\r
83 }\r
84 }\r
85 }\r
86\r
87 if(IsAnyServer(client_p) || IsExemptFlood(client_p))\r
88 {\r
89 while (!IsAnyDead(client_p) && (dolen = rb_linebuf_get(&client_p->localClient->buf_recvq,\r
90 readBuf, READBUF_SIZE, LINEBUF_COMPLETE,\r
91 LINEBUF_PARSED)) > 0)\r
92 {\r
93 client_dopacket(client_p, readBuf, dolen);\r
94 }\r
95 }\r
96 else if(IsClient(client_p))\r
97 {\r
98\r
99 if(IsOper(client_p) && ConfigFileEntry.no_oper_flood)\r
100 checkflood = 0;\r
101 /*\r
102 * Handle flood protection here - if we exceed our flood limit on\r
103 * messages in this loop, we simply drop out of the loop prematurely.\r
104 * -- adrian\r
105 */\r
106 for (;;)\r
107 {\r
108 /* This flood protection works as follows:\r
109 *\r
110 * A client is given allow_read lines to send to the server. Every\r
111 * time a line is parsed, sent_parsed is increased. sent_parsed\r
112 * is decreased by 1 every time flood_recalc is called.\r
113 *\r
114 * Thus a client can 'burst' allow_read lines to the server, any\r
115 * excess lines will be parsed one per flood_recalc() call.\r
116 *\r
117 * Therefore a client will be penalised more if they keep flooding,\r
118 * as sent_parsed will always hover around the allow_read limit\r
119 * and no 'bursts' will be permitted.\r
120 */\r
121 if(checkflood)\r
122 {\r
123 if(client_p->localClient->sent_parsed >= client_p->localClient->allow_read)\r
124 break;\r
125 }\r
126\r
127 /* allow opers 4 times the amount of messages as users. why 4?\r
128 * why not. :) --fl_\r
129 */\r
130 else if(client_p->localClient->sent_parsed >= (4 * client_p->localClient->allow_read))\r
131 break;\r
132\r
133 dolen = rb_linebuf_get(&client_p->localClient->\r
134 buf_recvq, readBuf, READBUF_SIZE,\r
135 LINEBUF_COMPLETE, LINEBUF_PARSED);\r
136\r
137 if(!dolen)\r
138 break;\r
139\r
140 client_dopacket(client_p, readBuf, dolen);\r
141 if(IsAnyDead(client_p))\r
142 return;\r
143 client_p->localClient->sent_parsed++;\r
144 }\r
145 }\r
146}\r
147\r
148/*\r
149 * flood_recalc\r
150 *\r
151 * recalculate the number of allowed flood lines. this should be called\r
152 * once a second on any given client. We then attempt to flush some data.\r
153 */\r
154void\r
155flood_recalc(void *unused)\r
156{\r
157 rb_dlink_node *ptr, *next;\r
158 struct Client *client_p;\r
159\r
160 RB_DLINK_FOREACH_SAFE(ptr, next, lclient_list.head)\r
161 {\r
162 client_p = ptr->data;\r
163\r
164 if(unlikely(IsMe(client_p)))\r
165 continue;\r
166 \r
167 if(unlikely(client_p->localClient == NULL))\r
168 continue;\r
169 \r
170 if(IsFloodDone(client_p))\r
171 client_p->localClient->sent_parsed -= 2;\r
172 else\r
173 client_p->localClient->sent_parsed = 0;\r
174 \r
175 if(client_p->localClient->sent_parsed < 0)\r
176 client_p->localClient->sent_parsed = 0;\r
177\r
178 if(--client_p->localClient->actually_read < 0)\r
179 client_p->localClient->actually_read = 0;\r
180\r
181 parse_client_queued(client_p);\r
182 \r
183 if(unlikely(IsAnyDead(client_p)))\r
184 continue;\r
185\r
186 }\r
187\r
188 RB_DLINK_FOREACH_SAFE(ptr, next, unknown_list.head)\r
189 {\r
190 client_p = ptr->data;\r
191\r
192 if(client_p->localClient == NULL)\r
193 continue;\r
194\r
195 client_p->localClient->sent_parsed--;\r
196\r
197 if(client_p->localClient->sent_parsed < 0)\r
198 client_p->localClient->sent_parsed = 0;\r
199\r
200 if(--client_p->localClient->actually_read < 0)\r
201 client_p->localClient->actually_read = 0;\r
202\r
203 parse_client_queued(client_p);\r
204 }\r
205}\r
206\r
207\r
208/*\r
209 * read_packet - Read a 'packet' of data from a connection and process it.\r
210 */\r
211void\r
212read_packet(rb_fde_t *F, void *data)\r
213{\r
214 struct Client *client_p = data;\r
215 struct LocalUser *lclient_p = client_p->localClient;\r
216 int length = 0;\r
217 int lbuf_len;\r
218\r
219 int binary = 0;\r
220#ifdef USE_IODEBUG_HOOKS\r
221 hook_data_int hdata;\r
222#endif\r
223\r
224\r
225 while(1) /* note..for things like rt sigio to work you *must* loop on read until you get EAGAIN */\r
226 {\r
227 if(IsAnyDead(client_p))\r
228 return;\r
229\r
230 /*\r
231 * Read some data. We *used to* do anti-flood protection here, but\r
232 * I personally think it makes the code too hairy to make sane.\r
233 * -- adrian\r
234 */\r
235 length = rb_read(client_p->localClient->F, readBuf, READBUF_SIZE);\r
236 if(length < 0)\r
237 {\r
238 if(rb_ignore_errno(errno))\r
239 {\r
240 rb_setselect(client_p->localClient->F, \r
241 RB_SELECT_READ, read_packet, client_p);\r
242 } else\r
243 error_exit_client(client_p, length);\r
244 return;\r
245 } else\r
246 if(length == 0)\r
247 {\r
248 error_exit_client(client_p, length);\r
249 return;\r
250 }\r
251 \r
252#ifdef USE_IODEBUG_HOOKS\r
253 hdata.client = client_p;\r
254 hdata.arg1 = readBuf;\r
255 hdata.arg2 = length;\r
256 call_hook(h_iorecv_id, &hdata);\r
257#endif\r
258\r
259 if(client_p->localClient->lasttime < rb_current_time())\r
260 client_p->localClient->lasttime = rb_current_time();\r
261 client_p->flags &= ~FLAGS_PINGSENT;\r
262\r
263 /*\r
264 * Before we even think of parsing what we just read, stick\r
265 * it on the end of the receive queue and do it when its\r
266 * turn comes around.\r
267 */\r
268 if(IsHandshake(client_p) || IsUnknown(client_p))\r
269 binary = 1;\r
270\r
271 lbuf_len = rb_linebuf_parse(&client_p->localClient->buf_recvq, readBuf, length, binary);\r
272\r
273 lclient_p->actually_read += lbuf_len;\r
274\r
275 if(IsAnyDead(client_p))\r
276 return;\r
277 \r
278 /* Attempt to parse what we have */\r
279 parse_client_queued(client_p);\r
280\r
281 if(IsAnyDead(client_p))\r
282 return;\r
283 \r
284 /* Check to make sure we're not flooding */\r
285 if(!IsAnyServer(client_p) &&\r
286 (rb_linebuf_alloclen(&client_p->localClient->buf_recvq) > ConfigFileEntry.client_flood))\r
287 {\r
288 if(!(ConfigFileEntry.no_oper_flood && IsOper(client_p)))\r
289 {\r
290 exit_client(client_p, client_p, client_p, "Excess Flood");\r
291 return;\r
292 }\r
293 \r
294 }\r
295\r
296 /* bail if short read */\r
297 if(length < READBUF_SIZE)\r
298 {\r
299 rb_setselect(client_p->localClient->F, RB_SELECT_READ, read_packet, client_p);\r
300 return;\r
301 }\r
302 }\r
303}\r
304\r
305/*\r
306 * client_dopacket - copy packet to client buf and parse it\r
307 * client_p - pointer to client structure for which the buffer data\r
308 * applies.\r
309 * buffer - pointr to the buffer containing the newly read data\r
310 * length - number of valid bytes of data in the buffer\r
311 *\r
312 * Note:\r
313 * It is implicitly assumed that dopacket is called only\r
314 * with client_p of "local" variation, which contains all the\r
315 * necessary fields (buffer etc..)\r
316 */\r
317void\r
318client_dopacket(struct Client *client_p, char *buffer, size_t length)\r
319{\r
320 s_assert(client_p != NULL);\r
321 s_assert(buffer != NULL);\r
322\r
323 if(client_p == NULL || buffer == NULL)\r
324 return;\r
325 if(IsAnyDead(client_p))\r
326 return;\r
327 /* \r
328 * Update messages received\r
329 */\r
330 ++me.localClient->receiveM;\r
331 ++client_p->localClient->receiveM;\r
332\r
333 /* \r
334 * Update bytes received\r
335 */\r
336 client_p->localClient->receiveB += length;\r
337 me.localClient->receiveB += length;\r
338\r
339 parse(client_p, buffer, buffer + length);\r
340}\r
341\r
342/* flood_endgrace()\r
343 *\r
344 * marks the end of the clients grace period\r
345 */\r
346void\r
347flood_endgrace(struct Client *client_p)\r
348{\r
349 SetFloodDone(client_p);\r
350\r
351 /* Drop their flood limit back down */\r
352 client_p->localClient->allow_read = MAX_FLOOD;\r
353\r
354 /* sent_parsed could be way over MAX_FLOOD but under MAX_FLOOD_BURST,\r
355 * so reset it.\r
356 */\r
357 client_p->localClient->sent_parsed = 0;\r
358}\r