ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/packet.c
Revision: 4982
Committed: Sat Dec 6 18:17:20 2014 UTC (10 years, 8 months ago) by michael
Content type: text/x-csrc
File size: 10517 byte(s)
Log Message:
- Cleaned up style; reformatting; const correctness

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 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 int checkflood = 1;
153
154 if (IsUnknown(client_p))
155 {
156 unsigned int i = 0;
157
158 while (1)
159 {
160 if (IsDefunct(client_p))
161 return;
162
163 /* Rate unknown clients at MAX_FLOOD per loop */
164 if (i >= MAX_FLOOD)
165 break;
166
167 dolen = extract_one_line(&client_p->connection->buf_recvq, readBuf);
168
169 if (dolen == 0)
170 break;
171
172 client_dopacket(client_p, readBuf, dolen);
173 ++i;
174
175 /*
176 * If they've dropped out of the unknown state, break and move
177 * to the parsing for their appropriate status. --fl
178 */
179 if (!IsUnknown(client_p))
180 break;
181 }
182 }
183
184 if (IsServer(client_p) || IsConnecting(client_p) || IsHandshake(client_p))
185 {
186 while (1)
187 {
188 if (IsDefunct(client_p))
189 return;
190
191 if ((dolen = extract_one_line(&client_p->connection->buf_recvq, readBuf)) == 0)
192 break;
193
194 client_dopacket(client_p, readBuf, dolen);
195 }
196 }
197 else if (IsClient(client_p))
198 {
199 if (ConfigGeneral.no_oper_flood && (HasUMode(client_p, UMODE_OPER) || IsCanFlood(client_p)))
200 {
201 if (ConfigGeneral.true_no_oper_flood)
202 checkflood = -1;
203 else
204 checkflood = 0;
205 }
206
207 /*
208 * Handle flood protection here - if we exceed our flood limit on
209 * messages in this loop, we simply drop out of the loop prematurely.
210 * -- adrian
211 */
212 while (1)
213 {
214 if (IsDefunct(client_p))
215 break;
216
217 /*
218 * This flood protection works as follows:
219 *
220 * A client is given allow_read lines to send to the server. Every
221 * time a line is parsed, sent_parsed is increased. sent_parsed
222 * is decreased by 1 every time flood_recalc is called.
223 *
224 * Thus a client can 'burst' allow_read lines to the server, any
225 * excess lines will be parsed one per flood_recalc() call.
226 *
227 * Therefore a client will be penalised more if they keep flooding,
228 * as sent_parsed will always hover around the allow_read limit
229 * and no 'bursts' will be permitted.
230 */
231 if (checkflood > 0)
232 {
233 if (client_p->connection->sent_parsed >= client_p->connection->allow_read)
234 break;
235 }
236
237 /*
238 * Allow opers 4 times the amount of messages as users. why 4?
239 * why not. :) --fl_
240 */
241 else if (client_p->connection->sent_parsed >=
242 (4 * client_p->connection->allow_read) && checkflood != -1)
243 break;
244
245 dolen = extract_one_line(&client_p->connection->buf_recvq, readBuf);
246
247 if (dolen == 0)
248 break;
249
250 client_dopacket(client_p, readBuf, dolen);
251 ++client_p->connection->sent_parsed;
252 }
253 }
254 }
255
256 /* flood_endgrace()
257 *
258 * marks the end of the clients grace period
259 */
260 void
261 flood_endgrace(struct Client *client_p)
262 {
263 SetFloodDone(client_p);
264
265 /* Drop their flood limit back down */
266 client_p->connection->allow_read = MAX_FLOOD;
267
268 /*
269 * sent_parsed could be way over MAX_FLOOD but under MAX_FLOOD_BURST,
270 * so reset it.
271 */
272 client_p->connection->sent_parsed = 0;
273 }
274
275 /*
276 * flood_recalc
277 *
278 * recalculate the number of allowed flood lines. this should be called
279 * once a second on any given client. We then attempt to flush some data.
280 */
281 void
282 flood_recalc(fde_t *fd, void *data)
283 {
284 struct Client *const client_p = data;
285
286 /*
287 * Allow a bursting client their allocation per second, allow
288 * a client whos flooding an extra 2 per second
289 */
290 if (IsFloodDone(client_p))
291 client_p->connection->sent_parsed -= 2;
292 else
293 client_p->connection->sent_parsed = 0;
294
295 if (client_p->connection->sent_parsed < 0)
296 client_p->connection->sent_parsed = 0;
297
298 parse_client_queued(client_p);
299
300 /* And now, try flushing .. */
301 if (!IsDead(client_p))
302 {
303 /* and finally, reset the flood check */
304 comm_setflush(fd, 1000, flood_recalc, client_p);
305 }
306 }
307
308 /*
309 * read_packet - Read a 'packet' of data from a connection and process it.
310 */
311 void
312 read_packet(fde_t *fd, void *data)
313 {
314 struct Client *const client_p = data;
315 int length = 0;
316
317 if (IsDefunct(client_p))
318 return;
319
320 /*
321 * Read some data. We *used to* do anti-flood protection here, but
322 * I personally think it makes the code too hairy to make sane.
323 * -- adrian
324 */
325 do
326 {
327 #ifdef HAVE_LIBCRYPTO
328 if (fd->ssl)
329 {
330 length = SSL_read(fd->ssl, readBuf, sizeof(readBuf));
331
332 /* translate openssl error codes, sigh */
333 if (length < 0)
334 switch (SSL_get_error(fd->ssl, length))
335 {
336 case SSL_ERROR_WANT_WRITE:
337 comm_setselect(fd, COMM_SELECT_WRITE, sendq_unblocked, client_p, 0);
338 return;
339 case SSL_ERROR_WANT_READ:
340 errno = EWOULDBLOCK;
341 case SSL_ERROR_SYSCALL:
342 break;
343 case SSL_ERROR_SSL:
344 if (errno == EAGAIN)
345 break;
346 default:
347 length = errno = 0;
348 }
349 }
350 else
351 #endif
352 {
353 length = recv(fd->fd, readBuf, sizeof(readBuf), 0);
354 }
355
356 if (length <= 0)
357 {
358 /*
359 * If true, then we can recover from this error. Just jump out of
360 * the loop and re-register a new io-request.
361 */
362 if (length < 0 && ignoreErrno(errno))
363 break;
364
365 dead_link_on_read(client_p, length);
366 return;
367 }
368
369 dbuf_put(&client_p->connection->buf_recvq, readBuf, length);
370
371 if (client_p->connection->lasttime < CurrentTime)
372 client_p->connection->lasttime = CurrentTime;
373
374 if (client_p->connection->lasttime > client_p->connection->since)
375 client_p->connection->since = CurrentTime;
376
377 ClearPingSent(client_p);
378
379 /* Attempt to parse what we have */
380 parse_client_queued(client_p);
381
382 if (IsDefunct(client_p))
383 return;
384
385 /* Check to make sure we're not flooding */
386 if (!(IsServer(client_p) || IsHandshake(client_p) || IsConnecting(client_p))
387 && (dbuf_length(&client_p->connection->buf_recvq) >
388 get_recvq(&client_p->connection->confs)))
389 {
390 if (!(ConfigGeneral.no_oper_flood && HasUMode(client_p, UMODE_OPER)))
391 {
392 exit_client(client_p, "Excess Flood");
393 return;
394 }
395 }
396 }
397 #ifdef HAVE_LIBCRYPTO
398 while (length == sizeof(readBuf) || fd->ssl);
399 #else
400 while (length == sizeof(readBuf));
401 #endif
402
403 /* If we get here, we need to register for another COMM_SELECT_READ */
404 comm_setselect(fd, COMM_SELECT_READ, read_packet, client_p, 0);
405 }

Properties

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