ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/packet.c
Revision: 4989
Committed: Mon Dec 8 20:07:36 2014 UTC (10 years, 8 months ago) by michael
Content type: text/x-csrc
File size: 10098 byte(s)
Log Message:
- The general::true_no_oper_flood configuration option has been deprecated.
  Operators still can have higher 'flood' limits with no_oper_flood = yes;
  hoewever, they are no longer allowed to bypass RecvQ limits.

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2014 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file packet.c
23 * \brief Packet handlers.
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "s_bsd.h"
30 #include "conf.h"
31 #include "server.h"
32 #include "client.h"
33 #include "ircd.h"
34 #include "parse.h"
35 #include "fdlist.h"
36 #include "packet.h"
37 #include "irc_string.h"
38 #include "memory.h"
39 #include "send.h"
40 #include "misc.h"
41
42 #define READBUF_SIZE 16384
43
44 static char readBuf[READBUF_SIZE];
45
46
47 /*
48 * client_dopacket - copy packet to client buf and parse it
49 * client_p - pointer to client structure for which the buffer data
50 * applies.
51 * buffer - pointr to the buffer containing the newly read data
52 * length - number of valid bytes of data in the buffer
53 *
54 * Note:
55 * It is implicitly assumed that client_dopacket() is called only
56 * with client_p of "local" variation, which contains all the
57 * necessary fields (buffer etc..)
58 */
59 static void
60 client_dopacket(struct Client *client_p, char *buffer, size_t length)
61 {
62 /* Update messages received */
63 ++me.connection->recv.messages;
64 ++client_p->connection->recv.messages;
65
66 /* Update bytes received */
67 client_p->connection->recv.bytes += length;
68 me.connection->recv.bytes += length;
69
70 parse(client_p, buffer, buffer + length);
71 }
72
73 /* extract_one_line()
74 *
75 * inputs - pointer to a dbuf queue
76 * - pointer to buffer to copy data to
77 * output - length of <buffer>
78 * side effects - one line is copied and removed from the dbuf
79 */
80 static int
81 extract_one_line(struct dbuf_queue *qptr, char *buffer)
82 {
83 int line_bytes = 0, empty_bytes = 0, phase = 0;
84 unsigned int idx = 0;
85 dlink_node *node = NULL;
86
87 /*
88 * Phase 0: "empty" characters before the line
89 * Phase 1: copying the line
90 * Phase 2: "empty" characters after the line
91 * (delete them as well and free some space in the dbuf)
92 *
93 * Empty characters are CR, LF and space (but, of course, not
94 * in the middle of a line). We try to remove as much of them as we can,
95 * since they simply eat server memory.
96 *
97 * --adx
98 */
99 DLINK_FOREACH(node, qptr->blocks.head)
100 {
101 struct dbuf_block *block = node->data;
102
103 if (node == qptr->blocks.head)
104 idx = qptr->pos;
105 else
106 idx = 0;
107
108 for (; idx < block->size; ++idx)
109 {
110 char c = block->data[idx];
111
112 if (IsEol(c) || (c == ' ' && phase != 1))
113 {
114 ++empty_bytes;
115
116 if (phase == 1)
117 phase = 2;
118 }
119 else switch (phase)
120 {
121 case 0: phase = 1;
122 case 1: if (line_bytes++ < IRCD_BUFSIZE - 2)
123 *buffer++ = c;
124 break;
125 case 2: *buffer = '\0';
126 dbuf_delete(qptr, line_bytes + empty_bytes);
127 return IRCD_MIN(line_bytes, IRCD_BUFSIZE - 2);
128 }
129 }
130 }
131
132 /*
133 * Now, if we haven't reached phase 2, ignore all line bytes
134 * that we have read, since this is a partial line case.
135 */
136 if (phase != 2)
137 line_bytes = 0;
138 else
139 *buffer = '\0';
140
141 /* Remove what is now unnecessary */
142 dbuf_delete(qptr, line_bytes + empty_bytes);
143 return IRCD_MIN(line_bytes, IRCD_BUFSIZE - 2);
144 }
145 /*
146 * parse_client_queued - parse client queued messages
147 */
148 static void
149 parse_client_queued(struct Client *client_p)
150 {
151 int dolen = 0;
152
153 if (IsUnknown(client_p))
154 {
155 unsigned int i = 0;
156
157 while (1)
158 {
159 if (IsDefunct(client_p))
160 return;
161
162 /* Rate unknown clients at MAX_FLOOD per loop */
163 if (i >= MAX_FLOOD)
164 break;
165
166 dolen = extract_one_line(&client_p->connection->buf_recvq, readBuf);
167
168 if (dolen == 0)
169 break;
170
171 client_dopacket(client_p, readBuf, dolen);
172 ++i;
173
174 /*
175 * If they've dropped out of the unknown state, break and move
176 * to the parsing for their appropriate status. --fl
177 */
178 if (!IsUnknown(client_p))
179 break;
180 }
181 }
182
183 if (IsServer(client_p) || IsConnecting(client_p) || IsHandshake(client_p))
184 {
185 while (1)
186 {
187 if (IsDefunct(client_p))
188 return;
189
190 if ((dolen = extract_one_line(&client_p->connection->buf_recvq, readBuf)) == 0)
191 break;
192
193 client_dopacket(client_p, readBuf, dolen);
194 }
195 }
196 else if (IsClient(client_p))
197 {
198 unsigned int checkflood = 1;
199
200 if (ConfigGeneral.no_oper_flood && HasUMode(client_p, UMODE_OPER))
201 checkflood = 0;
202 else if (IsCanFlood(client_p))
203 checkflood = 0;
204
205 /*
206 * Handle flood protection here - if we exceed our flood limit on
207 * messages in this loop, we simply drop out of the loop prematurely.
208 * -- adrian
209 */
210 while (1)
211 {
212 if (IsDefunct(client_p))
213 break;
214
215 /*
216 * This flood protection works as follows:
217 *
218 * A client is given allow_read lines to send to the server. Every
219 * time a line is parsed, sent_parsed is increased. sent_parsed
220 * is decreased by 1 every time flood_recalc is called.
221 *
222 * Thus a client can 'burst' allow_read lines to the server, any
223 * excess lines will be parsed one per flood_recalc() call.
224 *
225 * Therefore a client will be penalised more if they keep flooding,
226 * as sent_parsed will always hover around the allow_read limit
227 * and no 'bursts' will be permitted.
228 */
229 if (checkflood)
230 if (client_p->connection->sent_parsed >= client_p->connection->allow_read)
231 break;
232
233 dolen = extract_one_line(&client_p->connection->buf_recvq, readBuf);
234
235 if (dolen == 0)
236 break;
237
238 client_dopacket(client_p, readBuf, dolen);
239 ++client_p->connection->sent_parsed;
240 }
241 }
242 }
243
244 /* flood_endgrace()
245 *
246 * marks the end of the clients grace period
247 */
248 void
249 flood_endgrace(struct Client *client_p)
250 {
251 SetFloodDone(client_p);
252
253 /* Drop their flood limit back down */
254 client_p->connection->allow_read = MAX_FLOOD;
255
256 /*
257 * sent_parsed could be way over MAX_FLOOD but under MAX_FLOOD_BURST,
258 * so reset it.
259 */
260 client_p->connection->sent_parsed = 0;
261 }
262
263 /*
264 * flood_recalc
265 *
266 * recalculate the number of allowed flood lines. this should be called
267 * once a second on any given client. We then attempt to flush some data.
268 */
269 void
270 flood_recalc(fde_t *fd, void *data)
271 {
272 struct Client *const client_p = data;
273
274 /*
275 * Allow a bursting client their allocation per second, allow
276 * a client whos flooding an extra 2 per second
277 */
278 if (IsFloodDone(client_p))
279 client_p->connection->sent_parsed -= 2;
280 else
281 client_p->connection->sent_parsed = 0;
282
283 if (client_p->connection->sent_parsed < 0)
284 client_p->connection->sent_parsed = 0;
285
286 parse_client_queued(client_p);
287
288 /* And now, try flushing .. */
289 if (!IsDead(client_p))
290 {
291 /* and finally, reset the flood check */
292 comm_setflush(fd, 1000, flood_recalc, client_p);
293 }
294 }
295
296 /*
297 * read_packet - Read a 'packet' of data from a connection and process it.
298 */
299 void
300 read_packet(fde_t *fd, void *data)
301 {
302 struct Client *const client_p = data;
303 int length = 0;
304
305 if (IsDefunct(client_p))
306 return;
307
308 /*
309 * Read some data. We *used to* do anti-flood protection here, but
310 * I personally think it makes the code too hairy to make sane.
311 * -- adrian
312 */
313 do
314 {
315 #ifdef HAVE_LIBCRYPTO
316 if (fd->ssl)
317 {
318 length = SSL_read(fd->ssl, readBuf, sizeof(readBuf));
319
320 /* translate openssl error codes, sigh */
321 if (length < 0)
322 switch (SSL_get_error(fd->ssl, length))
323 {
324 case SSL_ERROR_WANT_WRITE:
325 comm_setselect(fd, COMM_SELECT_WRITE, sendq_unblocked, client_p, 0);
326 return;
327 case SSL_ERROR_WANT_READ:
328 errno = EWOULDBLOCK;
329 case SSL_ERROR_SYSCALL:
330 break;
331 case SSL_ERROR_SSL:
332 if (errno == EAGAIN)
333 break;
334 default:
335 length = errno = 0;
336 }
337 }
338 else
339 #endif
340 {
341 length = recv(fd->fd, readBuf, sizeof(readBuf), 0);
342 }
343
344 if (length <= 0)
345 {
346 /*
347 * If true, then we can recover from this error. Just jump out of
348 * the loop and re-register a new io-request.
349 */
350 if (length < 0 && ignoreErrno(errno))
351 break;
352
353 dead_link_on_read(client_p, length);
354 return;
355 }
356
357 dbuf_put(&client_p->connection->buf_recvq, readBuf, length);
358
359 if (client_p->connection->lasttime < CurrentTime)
360 client_p->connection->lasttime = CurrentTime;
361
362 if (client_p->connection->lasttime > client_p->connection->since)
363 client_p->connection->since = CurrentTime;
364
365 ClearPingSent(client_p);
366
367 /* Attempt to parse what we have */
368 parse_client_queued(client_p);
369
370 if (IsDefunct(client_p))
371 return;
372
373 /* Check to make sure we're not flooding */
374 if (!(IsServer(client_p) || IsHandshake(client_p) || IsConnecting(client_p)) &&
375 (dbuf_length(&client_p->connection->buf_recvq) >
376 get_recvq(&client_p->connection->confs)))
377 {
378 exit_client(client_p, "Excess Flood");
379 return;
380 }
381 }
382 #ifdef HAVE_LIBCRYPTO
383 while (length == sizeof(readBuf) || fd->ssl);
384 #else
385 while (length == sizeof(readBuf));
386 #endif
387
388 /* If we get here, we need to register for another COMM_SELECT_READ */
389 comm_setselect(fd, COMM_SELECT_READ, read_packet, client_p, 0);
390 }

Properties

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