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

# User Rev Content
1 adx 30 /*
2 michael 2916 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 adx 30 *
4 michael 2916 * Copyright (c) 1997-2014 ircd-hybrid development team
5 adx 30 *
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 michael 4565 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 adx 30 * USA
20     */
21 michael 2916
22     /*! \file packet.c
23     * \brief Packet handlers.
24     * \version $Id$
25     */
26    
27 adx 30 #include "stdinc.h"
28 michael 1011 #include "list.h"
29 adx 30 #include "s_bsd.h"
30 michael 1309 #include "conf.h"
31 michael 3347 #include "server.h"
32 adx 30 #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 michael 3347 #include "misc.h"
41 adx 30
42     #define READBUF_SIZE 16384
43    
44     static char readBuf[READBUF_SIZE];
45    
46 michael 4982
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 adx 30 /* 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 michael 3107 unsigned int idx = 0;
85 michael 4815 dlink_node *node = NULL;
86 adx 30
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 michael 4815 DLINK_FOREACH(node, qptr->blocks.head)
100 adx 30 {
101 michael 4815 struct dbuf_block *block = node->data;
102 adx 30
103 michael 4815 if (node == qptr->blocks.head)
104 michael 3107 idx = qptr->pos;
105     else
106     idx = 0;
107    
108     for (; idx < block->size; ++idx)
109 adx 30 {
110 michael 3107 char c = block->data[idx];
111    
112 adx 30 if (IsEol(c) || (c == ' ' && phase != 1))
113     {
114 michael 3107 ++empty_bytes;
115 michael 3453
116 adx 30 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 michael 2916 {
151 adx 30 int dolen = 0;
152     int checkflood = 1;
153    
154     if (IsUnknown(client_p))
155     {
156 michael 4982 unsigned int i = 0;
157 adx 30
158 michael 3453 while (1)
159 adx 30 {
160     if (IsDefunct(client_p))
161 michael 2916 return;
162 adx 30
163 michael 4982 /* Rate unknown clients at MAX_FLOOD per loop */
164 adx 30 if (i >= MAX_FLOOD)
165     break;
166    
167 michael 4982 dolen = extract_one_line(&client_p->connection->buf_recvq, readBuf);
168 michael 4890
169 adx 30 if (dolen == 0)
170 michael 2916 break;
171 adx 30
172     client_dopacket(client_p, readBuf, dolen);
173 michael 4982 ++i;
174 adx 30
175 michael 4982 /*
176     * If they've dropped out of the unknown state, break and move
177 adx 30 * to the parsing for their appropriate status. --fl
178     */
179 michael 3453 if (!IsUnknown(client_p))
180 adx 30 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 michael 4890
191 michael 4982 if ((dolen = extract_one_line(&client_p->connection->buf_recvq, readBuf)) == 0)
192 adx 30 break;
193 michael 3453
194 adx 30 client_dopacket(client_p, readBuf, dolen);
195     }
196     }
197 adx 163 else if (IsClient(client_p))
198 adx 30 {
199 michael 4340 if (ConfigGeneral.no_oper_flood && (HasUMode(client_p, UMODE_OPER) || IsCanFlood(client_p)))
200 adx 30 {
201 michael 4340 if (ConfigGeneral.true_no_oper_flood)
202 adx 30 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 michael 3453 while (1)
213 adx 30 {
214     if (IsDefunct(client_p))
215 michael 2916 break;
216 adx 30
217 michael 4982 /*
218     * This flood protection works as follows:
219 adx 30 *
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 michael 4894 {
233 michael 4982 if (client_p->connection->sent_parsed >= client_p->connection->allow_read)
234 adx 30 break;
235 michael 4894 }
236 michael 2916
237 michael 4890 /*
238     * Allow opers 4 times the amount of messages as users. why 4?
239 adx 30 * why not. :) --fl_
240     */
241 michael 4982 else if (client_p->connection->sent_parsed >=
242     (4 * client_p->connection->allow_read) && checkflood != -1)
243 adx 30 break;
244    
245 michael 4982 dolen = extract_one_line(&client_p->connection->buf_recvq, readBuf);
246 michael 4890
247 adx 30 if (dolen == 0)
248     break;
249    
250     client_dopacket(client_p, readBuf, dolen);
251 michael 4982 ++client_p->connection->sent_parsed;
252 adx 30 }
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 michael 4588 client_p->connection->allow_read = MAX_FLOOD;
267 adx 30
268 michael 4982 /*
269     * sent_parsed could be way over MAX_FLOOD but under MAX_FLOOD_BURST,
270 adx 30 * so reset it.
271     */
272 michael 4588 client_p->connection->sent_parsed = 0;
273 adx 30 }
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 michael 4982 struct Client *const client_p = data;
285 michael 2916
286 michael 4890 /*
287     * Allow a bursting client their allocation per second, allow
288 adx 30 * a client whos flooding an extra 2 per second
289     */
290     if (IsFloodDone(client_p))
291 michael 4982 client_p->connection->sent_parsed -= 2;
292 adx 30 else
293 michael 4982 client_p->connection->sent_parsed = 0;
294 michael 2916
295 michael 4982 if (client_p->connection->sent_parsed < 0)
296     client_p->connection->sent_parsed = 0;
297 michael 2916
298 adx 30 parse_client_queued(client_p);
299 michael 2916
300 adx 30 /* 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 michael 4982 struct Client *const client_p = data;
315 adx 30 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 michael 2916 do
326     {
327 adx 30 #ifdef HAVE_LIBCRYPTO
328     if (fd->ssl)
329     {
330 michael 3453 length = SSL_read(fd->ssl, readBuf, sizeof(readBuf));
331 adx 30
332     /* translate openssl error codes, sigh */
333     if (length < 0)
334     switch (SSL_get_error(fd->ssl, length))
335 michael 2881 {
336 adx 30 case SSL_ERROR_WANT_WRITE:
337 michael 4461 comm_setselect(fd, COMM_SELECT_WRITE, sendq_unblocked, client_p, 0);
338 michael 2881 return;
339     case SSL_ERROR_WANT_READ:
340     errno = EWOULDBLOCK;
341 adx 30 case SSL_ERROR_SYSCALL:
342 michael 2881 break;
343 michael 428 case SSL_ERROR_SSL:
344     if (errno == EAGAIN)
345     break;
346 adx 30 default:
347 michael 2881 length = errno = 0;
348     }
349 adx 30 }
350     else
351     #endif
352     {
353 michael 3453 length = recv(fd->fd, readBuf, sizeof(readBuf), 0);
354 adx 30 }
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 michael 4588 dbuf_put(&client_p->connection->buf_recvq, readBuf, length);
370 adx 30
371 michael 4588 if (client_p->connection->lasttime < CurrentTime)
372     client_p->connection->lasttime = CurrentTime;
373 michael 4890
374 michael 4588 if (client_p->connection->lasttime > client_p->connection->since)
375     client_p->connection->since = CurrentTime;
376 michael 3453
377 adx 30 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 michael 4588 && (dbuf_length(&client_p->connection->buf_recvq) >
388     get_recvq(&client_p->connection->confs)))
389 adx 30 {
390 michael 4340 if (!(ConfigGeneral.no_oper_flood && HasUMode(client_p, UMODE_OPER)))
391 adx 30 {
392 michael 3171 exit_client(client_p, "Excess Flood");
393 adx 30 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