ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/packet.c
Revision: 8752
Committed: Tue Jan 1 11:07:01 2019 UTC (5 years, 2 months ago) by michael
Content type: text/x-csrc
File size: 9233 byte(s)
Log Message:
- Update copyright years

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2019 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
43 enum { READBUF_SIZE = 16384 };
44
45 static char readBuf[READBUF_SIZE];
46
47
48 /*
49 * client_dopacket - copy packet to client buf and parse it
50 * client_p - pointer to client structure for which the buffer data
51 * applies.
52 * buffer - pointer to the buffer containing the newly read data
53 * length - number of valid bytes of data in the buffer
54 *
55 * Note:
56 * It is implicitly assumed that client_dopacket() is called only
57 * with client_p of "local" variation, which contains all the
58 * necessary fields (buffer etc..)
59 */
60 static void
61 client_dopacket(struct Client *client_p, char *buffer, unsigned int length)
62 {
63 /* Update messages received */
64 ++me.connection->recv.messages;
65 ++client_p->connection->recv.messages;
66
67 /* Update bytes received */
68 client_p->connection->recv.bytes += length;
69 me.connection->recv.bytes += length;
70
71 parse(client_p, buffer, buffer + length);
72 }
73
74 /* extract_one_line()
75 *
76 * inputs - pointer to a dbuf queue
77 * - pointer to buffer to copy data to
78 * output - length of <buffer>
79 * side effects - one line is copied and removed from the dbuf
80 */
81 static size_t
82 extract_one_line(struct dbuf_queue *qptr, char *buffer)
83 {
84 size_t line_bytes = 0, eol_bytes = 0;
85 dlink_node *node;
86
87 DLINK_FOREACH(node, qptr->blocks.head)
88 {
89 const struct dbuf_block *block = node->data;
90 unsigned int idx;
91
92 if (node == qptr->blocks.head)
93 idx = qptr->pos;
94 else
95 idx = 0;
96
97 for (; idx < block->size; ++idx)
98 {
99 char c = block->data[idx];
100
101 if (IsEol(c))
102 {
103 ++eol_bytes;
104
105 /* Allow 2 eol bytes per message */
106 if (eol_bytes == 2)
107 goto out;
108 }
109 else if (eol_bytes)
110 goto out;
111 else if (line_bytes++ < IRCD_BUFSIZE - 2)
112 *buffer++ = c;
113 }
114 }
115
116 out:
117
118 /*
119 * Now, if we haven't found an EOL, ignore all line bytes
120 * that we have read, since this is a partial line case.
121 */
122 if (eol_bytes)
123 *buffer = '\0';
124 else
125 line_bytes = 0;
126
127 /* Remove what is now unnecessary */
128 dbuf_delete(qptr, line_bytes + eol_bytes);
129
130 return IRCD_MIN(line_bytes, IRCD_BUFSIZE - 2);
131 }
132
133 /*
134 * parse_client_queued - parse client queued messages
135 */
136 static void
137 parse_client_queued(struct Client *client_p)
138 {
139 if (IsUnknown(client_p))
140 {
141 unsigned int i = 0;
142
143 while (true)
144 {
145 if (IsDefunct(client_p))
146 return;
147
148 /* Rate unknown clients at MAX_FLOOD per loop */
149 if (i >= MAX_FLOOD)
150 break;
151
152 size_t dolen = extract_one_line(&client_p->connection->buf_recvq, readBuf);
153 if (dolen == 0)
154 break;
155
156 client_dopacket(client_p, readBuf, dolen);
157 ++i;
158
159 /*
160 * If they've dropped out of the unknown state, break and move
161 * to the parsing for their appropriate status. --fl
162 */
163 if (!IsUnknown(client_p))
164 break;
165 }
166 }
167
168 if (IsServer(client_p) || IsConnecting(client_p) || IsHandshake(client_p))
169 {
170 while (true)
171 {
172 if (IsDefunct(client_p))
173 return;
174
175 size_t dolen = extract_one_line(&client_p->connection->buf_recvq, readBuf);
176 if (dolen == 0)
177 break;
178
179 client_dopacket(client_p, readBuf, dolen);
180 }
181 }
182 else if (IsClient(client_p))
183 {
184 bool checkflood = true;
185
186 if (ConfigGeneral.no_oper_flood && HasUMode(client_p, UMODE_OPER))
187 checkflood = false;
188 else if (HasFlag(client_p, FLAGS_CANFLOOD))
189 checkflood = false;
190
191 /*
192 * Handle flood protection here - if we exceed our flood limit on messages
193 * in this loop, we simply drop out of the loop prematurely.
194 * -- adrian
195 */
196 while (true)
197 {
198 if (IsDefunct(client_p))
199 break;
200
201 /*
202 * This flood protection works as follows:
203 *
204 * A client is given allow_read lines to send to the server. Every
205 * time a line is parsed, sent_parsed is increased. sent_parsed
206 * is decreased by 1 every time flood_recalc is called.
207 *
208 * Thus a client can 'burst' allow_read lines to the server, any
209 * excess lines will be parsed one per flood_recalc() call.
210 *
211 * Therefore a client will be penalised more if they keep flooding,
212 * as sent_parsed will always hover around the allow_read limit
213 * and no 'bursts' will be permitted.
214 */
215 if (checkflood == true)
216 if (client_p->connection->sent_parsed >= (IsFloodDone(client_p) ? MAX_FLOOD : MAX_FLOOD_BURST))
217 break;
218
219 size_t dolen = extract_one_line(&client_p->connection->buf_recvq, readBuf);
220 if (dolen == 0)
221 break;
222
223 client_dopacket(client_p, readBuf, dolen);
224 ++client_p->connection->sent_parsed;
225 }
226 }
227 }
228
229 /* flood_endgrace()
230 *
231 * marks the end of the clients grace period
232 */
233 void
234 flood_endgrace(struct Client *client_p)
235 {
236 if (IsFloodDone(client_p))
237 return; /* Grace period has already ended */
238
239 AddFlag(client_p, FLAGS_FLOODDONE);
240
241 /*
242 * sent_parsed could be way over MAX_FLOOD but under MAX_FLOOD_BURST,
243 * so reset it.
244 */
245 client_p->connection->sent_parsed = 0;
246 }
247
248 /*
249 * flood_recalc
250 *
251 * recalculate the number of allowed flood lines. this should be called
252 * once a second on any given client. We then attempt to flush some data.
253 */
254 void
255 flood_recalc(fde_t *F, void *data)
256 {
257 struct Client *const client_p = data;
258
259 /*
260 * Allow a bursting client their allocation per second, allow
261 * a client who is flooding an extra 2 per second
262 */
263 if (IsFloodDone(client_p))
264 client_p->connection->sent_parsed -= 2;
265 else
266 client_p->connection->sent_parsed = 0;
267
268 if (client_p->connection->sent_parsed < 0)
269 client_p->connection->sent_parsed = 0;
270
271 parse_client_queued(client_p);
272
273 /* And now, try flushing .. */
274 if (!IsDead(client_p))
275 {
276 /* and finally, reset the flood check */
277 comm_setflush(F, 1000, flood_recalc, client_p);
278 }
279 }
280
281 /*
282 * read_packet - Read a 'packet' of data from a connection and process it.
283 */
284 void
285 read_packet(fde_t *F, void *data)
286 {
287 struct Client *const client_p = data;
288 int length = 0;
289
290 assert(client_p);
291 assert(client_p->connection);
292 assert(client_p->connection->fd);
293 assert(client_p->connection->fd == F);
294
295 if (IsDefunct(client_p))
296 return;
297
298 /*
299 * Read some data. We *used to* do anti-flood protection here, but
300 * I personally think it makes the code too hairy to make sane.
301 * -- adrian
302 */
303 do
304 {
305 if (tls_isusing(&F->ssl))
306 {
307 bool want_write = false;
308 length = tls_read(&F->ssl, readBuf, sizeof(readBuf), &want_write);
309
310 if (want_write == true)
311 comm_setselect(F, COMM_SELECT_WRITE, sendq_unblocked, client_p, 0);
312 }
313 else
314 length = recv(F->fd, readBuf, sizeof(readBuf), 0);
315
316 if (length <= 0)
317 {
318 /*
319 * If true, then we can recover from this error. Just jump out of
320 * the loop and re-register a new io-request.
321 */
322 if (length < 0 && comm_ignore_errno(errno) == true)
323 break;
324
325 dead_link_on_read(client_p, length);
326 return;
327 }
328
329 dbuf_put(&client_p->connection->buf_recvq, readBuf, length);
330
331 if (client_p->connection->lasttime < CurrentTime)
332 client_p->connection->lasttime = CurrentTime;
333
334 if (client_p->connection->lasttime > client_p->connection->since)
335 client_p->connection->since = CurrentTime;
336
337 DelFlag(client_p, FLAGS_PINGSENT);
338
339 /* Attempt to parse what we have */
340 parse_client_queued(client_p);
341
342 if (IsDefunct(client_p))
343 return;
344
345 /* Check to make sure we're not flooding */
346 if (!(IsServer(client_p) || IsHandshake(client_p) || IsConnecting(client_p)) &&
347 (dbuf_length(&client_p->connection->buf_recvq) >
348 get_recvq(&client_p->connection->confs)))
349 {
350 exit_client(client_p, "Excess Flood");
351 return;
352 }
353 } while (length == sizeof(readBuf) || tls_isusing(&F->ssl));
354
355 /* If we get here, we need to register for another COMM_SELECT_READ */
356 comm_setselect(F, COMM_SELECT_READ, read_packet, client_p, 0);
357 }

Properties

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