/[svn]/ircd-hybrid/src/packet.c
ViewVC logotype

Contents of /ircd-hybrid/src/packet.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 178 - (show annotations)
Sat Oct 22 10:02:43 2005 UTC (17 years, 1 month ago) by adx
File MIME type: text/x-chdr
File size: 13585 byte(s)
- somehow it didn't get committed, fixing

1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 * packet.c: Packet handlers.
4 *
5 * Copyright (C) 2002 by the past and present ircd coders, and others.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
22 * $Id$
23 */
24 #include "stdinc.h"
25 #include "s_conf.h"
26 #include "s_serv.h"
27 #include "client.h"
28 #include "common.h"
29 #include "ircd.h"
30 #include "parse.h"
31 #include "packet.h"
32 #include "send.h"
33
34 #define READBUF_SIZE 16384
35
36 struct Callback *iorecv_cb = NULL;
37 struct Callback *iorecvctrl_cb = NULL;
38
39 static char readBuf[READBUF_SIZE];
40 static void client_dopacket(struct Client *, char *, size_t);
41
42 /* extract_one_line()
43 *
44 * inputs - pointer to a dbuf queue
45 * - pointer to buffer to copy data to
46 * output - length of <buffer>
47 * side effects - one line is copied and removed from the dbuf
48 */
49 static int
50 extract_one_line(struct dbuf_queue *qptr, char *buffer)
51 {
52 struct dbuf_block *block;
53 int line_bytes = 0, empty_bytes = 0, phase = 0;
54 unsigned int idx;
55
56 char c;
57 dlink_node *ptr;
58
59 /*
60 * Phase 0: "empty" characters before the line
61 * Phase 1: copying the line
62 * Phase 2: "empty" characters after the line
63 * (delete them as well and free some space in the dbuf)
64 *
65 * Empty characters are CR, LF and space (but, of course, not
66 * in the middle of a line). We try to remove as much of them as we can,
67 * since they simply eat server memory.
68 *
69 * --adx
70 */
71 DLINK_FOREACH(ptr, qptr->blocks.head)
72 {
73 block = ptr->data;
74
75 for (idx = 0; idx < block->size; idx++)
76 {
77 c = block->data[idx];
78 if (IsEol(c) || (c == ' ' && phase != 1))
79 {
80 empty_bytes++;
81 if (phase == 1)
82 phase = 2;
83 }
84 else switch (phase)
85 {
86 case 0: phase = 1;
87 case 1: if (line_bytes++ < IRCD_BUFSIZE - 2)
88 *buffer++ = c;
89 break;
90 case 2: *buffer = '\0';
91 dbuf_delete(qptr, line_bytes + empty_bytes);
92 return IRCD_MIN(line_bytes, IRCD_BUFSIZE - 2);
93 }
94 }
95 }
96
97 /*
98 * Now, if we haven't reached phase 2, ignore all line bytes
99 * that we have read, since this is a partial line case.
100 */
101 if (phase != 2)
102 line_bytes = 0;
103 else
104 *buffer = '\0';
105
106 /* Remove what is now unnecessary */
107 dbuf_delete(qptr, line_bytes + empty_bytes);
108 return IRCD_MIN(line_bytes, IRCD_BUFSIZE - 2);
109 }
110
111 /*
112 * parse_client_queued - parse client queued messages
113 */
114 static void
115 parse_client_queued(struct Client *client_p)
116 {
117 int dolen = 0;
118 int checkflood = 1;
119 struct LocalUser *lclient_p = client_p->localClient;
120
121 if (IsUnknown(client_p))
122 {
123 int i = 0;
124
125 for(;;)
126 {
127 if (IsDefunct(client_p))
128 return;
129
130 /* rate unknown clients at MAX_FLOOD per loop */
131 if (i >= MAX_FLOOD)
132 break;
133
134 dolen = extract_one_line(&lclient_p->buf_recvq, readBuf);
135 if (dolen == 0)
136 break;
137
138 client_dopacket(client_p, readBuf, dolen);
139 i++;
140
141 /* if they've dropped out of the unknown state, break and move
142 * to the parsing for their appropriate status. --fl
143 */
144 if(!IsUnknown(client_p))
145 break;
146 }
147 }
148
149 if (IsServer(client_p) || IsConnecting(client_p) || IsHandshake(client_p))
150 {
151 while (1)
152 {
153 if (IsDefunct(client_p))
154 return;
155 if ((dolen = extract_one_line(&lclient_p->buf_recvq,
156 readBuf)) == 0)
157 break;
158 client_dopacket(client_p, readBuf, dolen);
159 }
160 }
161 else if (IsClient(client_p))
162 {
163 if (ConfigFileEntry.no_oper_flood && (IsOper(client_p) || IsCanFlood(client_p)))
164 {
165 if (ConfigFileEntry.true_no_oper_flood)
166 checkflood = -1;
167 else
168 checkflood = 0;
169 }
170
171 /*
172 * Handle flood protection here - if we exceed our flood limit on
173 * messages in this loop, we simply drop out of the loop prematurely.
174 * -- adrian
175 */
176 for (;;)
177 {
178 if (IsDefunct(client_p))
179 break;
180
181 /* This flood protection works as follows:
182 *
183 * A client is given allow_read lines to send to the server. Every
184 * time a line is parsed, sent_parsed is increased. sent_parsed
185 * is decreased by 1 every time flood_recalc is called.
186 *
187 * Thus a client can 'burst' allow_read lines to the server, any
188 * excess lines will be parsed one per flood_recalc() call.
189 *
190 * Therefore a client will be penalised more if they keep flooding,
191 * as sent_parsed will always hover around the allow_read limit
192 * and no 'bursts' will be permitted.
193 */
194 if (checkflood > 0)
195 {
196 if(lclient_p->sent_parsed >= lclient_p->allow_read)
197 break;
198 }
199
200 /* allow opers 4 times the amount of messages as users. why 4?
201 * why not. :) --fl_
202 */
203 else if (lclient_p->sent_parsed >= (4 * lclient_p->allow_read) &&
204 checkflood != -1)
205 break;
206
207 dolen = extract_one_line(&lclient_p->buf_recvq, readBuf);
208 if (dolen == 0)
209 break;
210
211 client_dopacket(client_p, readBuf, dolen);
212 lclient_p->sent_parsed++;
213 }
214 }
215 }
216
217 /* flood_endgrace()
218 *
219 * marks the end of the clients grace period
220 */
221 void
222 flood_endgrace(struct Client *client_p)
223 {
224 SetFloodDone(client_p);
225
226 /* Drop their flood limit back down */
227 client_p->localClient->allow_read = MAX_FLOOD;
228
229 /* sent_parsed could be way over MAX_FLOOD but under MAX_FLOOD_BURST,
230 * so reset it.
231 */
232 client_p->localClient->sent_parsed = 0;
233 }
234
235 /*
236 * flood_recalc
237 *
238 * recalculate the number of allowed flood lines. this should be called
239 * once a second on any given client. We then attempt to flush some data.
240 */
241 void
242 flood_recalc(fde_t *fd, void *data)
243 {
244 struct Client *client_p = data;
245 struct LocalUser *lclient_p = client_p->localClient;
246
247 /* allow a bursting client their allocation per second, allow
248 * a client whos flooding an extra 2 per second
249 */
250 if (IsFloodDone(client_p))
251 lclient_p->sent_parsed -= 2;
252 else
253 lclient_p->sent_parsed = 0;
254
255 if (lclient_p->sent_parsed < 0)
256 lclient_p->sent_parsed = 0;
257
258 parse_client_queued(client_p);
259
260 /* And now, try flushing .. */
261 if (!IsDead(client_p))
262 {
263 /* and finally, reset the flood check */
264 comm_setflush(fd, 1000, flood_recalc, client_p);
265 }
266 }
267
268 /*
269 * read_ctrl_packet - Read a 'packet' of data from a servlink control
270 * link and process it.
271 */
272 void
273 read_ctrl_packet(fde_t *fd, void *data)
274 {
275 struct Client *server = data;
276 struct LocalUser *lserver = server->localClient;
277 struct SlinkRpl *reply;
278 int length = 0;
279 unsigned char tmp[2];
280 unsigned char *len = tmp;
281 struct SlinkRplDef *replydef;
282
283 assert(lserver != NULL);
284
285 reply = &lserver->slinkrpl;
286
287 if (IsDefunct(server))
288 return;
289
290 if (!reply->command)
291 {
292 reply->gotdatalen = 0;
293 reply->readdata = 0;
294 reply->data = NULL;
295
296 length = recv(fd->fd, tmp, 1, 0);
297
298 if (length <= 0)
299 {
300 if ((length == -1) && ignoreErrno(errno))
301 goto nodata;
302 dead_link_on_read(server, length);
303 return;
304 }
305 reply->command = tmp[0];
306 }
307
308 for (replydef = slinkrpltab; replydef->handler; replydef++)
309 {
310 if (replydef->replyid == (unsigned int)reply->command)
311 break;
312 }
313
314 /* we should be able to trust a local slink process...
315 * and if it sends an invalid command, that's a bug.. */
316 assert(replydef->handler);
317
318 if ((replydef->flags & SLINKRPL_FLAG_DATA) && (reply->gotdatalen < 2))
319 {
320 /* we need a datalen u16 which we don't have yet... */
321 length = recv(fd->fd, len, (2 - reply->gotdatalen), 0);
322 if (length <= 0)
323 {
324 if ((length == -1) && ignoreErrno(errno))
325 goto nodata;
326 dead_link_on_read(server, length);
327 return;
328 }
329
330 if (reply->gotdatalen == 0)
331 {
332 reply->datalen = *len << 8;
333 reply->gotdatalen++;
334 length--;
335 len++;
336 }
337 if (length && (reply->gotdatalen == 1))
338 {
339 reply->datalen |= *len;
340 reply->gotdatalen++;
341 if (reply->datalen > 0)
342 reply->data = MyMalloc(reply->datalen);
343 }
344
345 if (reply->gotdatalen < 2)
346 return; /* wait for more data */
347 }
348
349 if (reply->readdata < reply->datalen) /* try to get any remaining data */
350 {
351 length = recv(fd->fd, (reply->data + reply->readdata),
352 (reply->datalen - reply->readdata), 0);
353 if (length <= 0)
354 {
355 if ((length == -1) && ignoreErrno(errno))
356 goto nodata;
357 dead_link_on_read(server, length);
358 return;
359 }
360
361 reply->readdata += length;
362 if (reply->readdata < reply->datalen)
363 return; /* wait for more data */
364 }
365
366 execute_callback(iorecvctrl_cb, server, reply->command);
367
368 /* we now have the command and any data, pass it off to the handler */
369 (*replydef->handler)(reply->command, reply->datalen, reply->data, server);
370
371 /* reset SlinkRpl */
372 if (reply->datalen > 0)
373 MyFree(reply->data);
374 reply->command = 0;
375
376 if (IsDead(server))
377 return;
378
379 nodata:
380 /* If we get here, we need to register for another COMM_SELECT_READ */
381 comm_setselect(fd, COMM_SELECT_READ, read_ctrl_packet, server, 0);
382 }
383
384 /*
385 * iorecv_default - append a packet to the recvq dbuf
386 */
387 void *
388 iorecv_default(va_list args)
389 {
390 struct Client *client_p = va_arg(args, struct Client *);
391 int length = va_arg(args, int);
392 char *buf = va_arg(args, char *);
393
394 dbuf_put(&client_p->localClient->buf_recvq, buf, length);
395 return NULL;
396 }
397
398 /*
399 * read_packet - Read a 'packet' of data from a connection and process it.
400 */
401 void
402 read_packet(fde_t *fd, void *data)
403 {
404 struct Client *client_p = data;
405 int length = 0;
406
407 if (IsDefunct(client_p))
408 return;
409
410 /*
411 * Read some data. We *used to* do anti-flood protection here, but
412 * I personally think it makes the code too hairy to make sane.
413 * -- adrian
414 */
415 do {
416 #ifdef HAVE_LIBCRYPTO
417 if (fd->ssl)
418 {
419 length = SSL_read(fd->ssl, readBuf, READBUF_SIZE);
420
421 /* translate openssl error codes, sigh */
422 if (length < 0)
423 switch (SSL_get_error(fd->ssl, length))
424 {
425 case SSL_ERROR_WANT_WRITE:
426 fd->flags.pending_read = 1;
427 SetSendqBlocked(client_p);
428 comm_setselect(fd, COMM_SELECT_WRITE, (PF *) sendq_unblocked,
429 client_p, 0);
430 return;
431 case SSL_ERROR_WANT_READ:
432 errno = EWOULDBLOCK;
433 case SSL_ERROR_SYSCALL:
434 break;
435 default:
436 length = errno = 0;
437 }
438 }
439 else
440 #endif
441 {
442 length = recv(fd->fd, readBuf, READBUF_SIZE, 0);
443 #ifdef _WIN32
444 if (length < 0)
445 errno = WSAGetLastError();
446 #endif
447 }
448
449 if (length <= 0)
450 {
451 /*
452 * If true, then we can recover from this error. Just jump out of
453 * the loop and re-register a new io-request.
454 */
455 if (length < 0 && ignoreErrno(errno))
456 break;
457
458 dead_link_on_read(client_p, length);
459 return;
460 }
461
462 execute_callback(iorecv_cb, client_p, length, readBuf);
463
464 if (client_p->lasttime < CurrentTime)
465 client_p->lasttime = CurrentTime;
466 if (client_p->lasttime > client_p->since)
467 client_p->since = CurrentTime;
468 ClearPingSent(client_p);
469
470 /* Attempt to parse what we have */
471 parse_client_queued(client_p);
472
473 if (IsDefunct(client_p))
474 return;
475
476 /* Check to make sure we're not flooding */
477 /* TBD - ConfigFileEntry.client_flood should be a size_t */
478 if (!(IsServer(client_p) || IsHandshake(client_p) || IsConnecting(client_p))
479 && (dbuf_length(&client_p->localClient->buf_recvq) >
480 (unsigned int)ConfigFileEntry.client_flood))
481 {
482 if (!(ConfigFileEntry.no_oper_flood && IsOper(client_p)))
483 {
484 exit_client(client_p, client_p, "Excess Flood");
485 return;
486 }
487 }
488 }
489 #ifdef HAVE_LIBCRYPTO
490 while (length == sizeof(readBuf) || fd->ssl);
491 #else
492 while (length == sizeof(readBuf));
493 #endif
494
495 /* If we get here, we need to register for another COMM_SELECT_READ */
496 comm_setselect(fd, COMM_SELECT_READ, read_packet, client_p, 0);
497 }
498
499 /*
500 * client_dopacket - copy packet to client buf and parse it
501 * client_p - pointer to client structure for which the buffer data
502 * applies.
503 * buffer - pointr to the buffer containing the newly read data
504 * length - number of valid bytes of data in the buffer
505 *
506 * Note:
507 * It is implicitly assumed that dopacket is called only
508 * with client_p of "local" variation, which contains all the
509 * necessary fields (buffer etc..)
510 */
511 static void
512 client_dopacket(struct Client *client_p, char *buffer, size_t length)
513 {
514 /*
515 * Update messages received
516 */
517 ++me.localClient->recv.messages;
518 ++client_p->localClient->recv.messages;
519
520 /*
521 * Update bytes received
522 */
523 client_p->localClient->recv.bytes += length;
524 me.localClient->recv.bytes += length;
525
526 parse(client_p, buffer, buffer + length);
527 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision

svnadmin@ircd-hybrid.org
ViewVC Help
Powered by ViewVC 1.1.28