/************************************************************************
* IRC - Internet Relay Chat, server/s_packet.c
*
* Copyright (C) 2000-2003 TR-IRCD Development
*
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Co Center
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* $Id: s_packet.c,v 1.6 2004/07/14 14:26:04 tr-ircd Exp $
*/
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "s_bsd.h"
#include "parse.h"
#include "fd.h"
#include "packet.h"
#include "zlink.h"
#include "dh.h"
#include "s_conf.h"
static char readBuf[READBUF_SIZE];
int client_dopacket(aClient *cptr, char *buffer, size_t length);
int server_dopacket(aClient *cptr, char *buffer, size_t length);
int binary_dopacket(aClient *cptr, char *buffer, size_t length);
void init_dopacket(void *unused)
{
memset(readBuf, 0, READBUF_SIZE);
return;
}
void parse_client_queued(aClient *client_p)
{
int dolen = 0;
int fd_r;
/*
* Handle flood protection here - if we exceed our flood limit on
* messages in this loop, we simply drop out of the loop prematurely.
* -- adrian
*/
for (;;) {
if (!IsAnOper(client_p) && (client_p->sent_parsed > client_p->allow_read))
break;
dolen = linebuf_get(&client_p->recvQ, readBuf, READBUF_SIZE,
LINEBUF_COMPLETE, LINEBUF_PARSED);
if (!dolen)
break;
if (client_dopacket(client_p, readBuf, dolen) == -2)
return;
#if 0
client_p->sent_parsed++;
#endif
}
/* server fd may have changed */
fd_r = client_p->fd;
if (!IsDead(client_p)) {
/* If we get here, we need to register for another COMM_SELECT_READ */
if (IsServer(client_p) || IsNegoServer(client_p))
comm_setselect(fd_r, FDLIST_IRCD, COMM_SELECT_READ, read_server_packet, client_p, 0);
else
comm_setselect(fd_r, FDLIST_IRCD, COMM_SELECT_READ, read_client_packet, client_p, 0);
}
}
void read_client_packet(int fd, void *data)
{
aClient *client_p = data;
int length = 0;
int lbuf_len;
int fd_r = client_p->fd;
int binary = 0;
/* if the client is dead, kill it off now -davidt */
if (IsDead(client_p)) {
exit_client(client_p, &me,
(client_p->flags & FLAGS_SENDQEX) ? "SendQ exceeded" : "Dead socket");
return;
}
/*
* Read some data. We *used to* do anti-flood protection here, but
* I personally think it makes the code too hairy to make sane.
* -- adrian
*/
memset(readBuf, 0, READBUF_SIZE);
#ifdef HAVE_ENCRYPTION_ON
if (IsSSL(client_p))
length = safe_SSL_read(client_p, readBuf, READBUF_SIZE);
else
#endif
length = read(fd_r, readBuf, READBUF_SIZE);
if (length <= 0) {
if ((length == -1) && ignoreErrno(errno)) {
comm_setselect(fd_r, FDLIST_IRCD, COMM_SELECT_READ, read_client_packet, client_p, 0);
return;
}
dead_link_on_read(client_p, length);
return;
}
if (client_p->lasttime < timeofday)
client_p->lasttime = timeofday;
if (client_p->lasttime > client_p->since)
client_p->since = timeofday;
client_p->flags &= ~FLAGS_PINGSENT;
/*
* Before we even think of parsing what we just read, stick
* it on the end of the receive queue and do it when its
* turn comes around.
*/
if (IsServer(client_p) || IsNegoServer(client_p) || DoZipThis(client_p) || WantDKEY(client_p))
binary = 1;
if (binary) {
binary_dopacket(client_p, readBuf, length);
} else {
lbuf_len = linebuf_parse(&client_p->recvQ, readBuf, length, binary);
if (lbuf_len < 0) {
dead_link_on_read(client_p, 0);
return;
}
/* Check to make sure we're not flooding */
if (IsPerson(client_p) && !IsAnOper(client_p) &&
(linebuf_alloclen(&client_p->recvQ) > ServerOpts.client_flood)) {
exit_client(client_p, client_p, "Excess Flood");
return;
}
parse_client_queued(client_p);
}
remove_exited_clients(NULL);
}
void read_server_packet(int fd, void *data)
{
aClient *client_p = data;
int length = 0;
int fd_r = client_p->fd;
/* if the client is dead, kill it off now -davidt */
if (IsDead(client_p)) {
exit_client(client_p, &me,
(client_p->flags & FLAGS_SENDQEX) ? "SendQ exceeded" : "Dead socket");
return;
}
/*
* Read some data. We *used to* do anti-flood protection here, but
* I personally think it makes the code too hairy to make sane.
* -- adrian
*/
memset(readBuf, 0, READBUF_SIZE);
length = read(fd_r, readBuf, READBUF_SIZE);
if (length <= 0) {
if ((length == -1) && ignoreErrno(errno)) {
comm_setselect(fd_r, FDLIST_IRCD, COMM_SELECT_READ, read_server_packet, client_p, 0);
return;
}
dead_link_on_read(client_p, length);
return;
}
if (client_p->lasttime < timeofday)
client_p->lasttime = timeofday;
if (client_p->lasttime > client_p->since)
client_p->since = timeofday;
client_p->flags &= ~FLAGS_PINGSENT;
binary_dopacket(client_p, readBuf, length);
/* server fd may have changed */
fd_r = client_p->fd;
if (!IsDead(client_p))
comm_setselect(fd_r, FDLIST_IRCD, COMM_SELECT_READ, read_server_packet, client_p, 0);
remove_exited_clients(NULL);
}
static void process_bytecount(aClient *cptr, int length)
{
++me.receiveM;
++cptr->receiveM;
cptr->receiveB += length;
if (cptr->receiveB > 1023) {
cptr->receiveK += (cptr->receiveB >> 10);
cptr->receiveB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */
}
me.receiveB += length;
if (me.receiveB > 1023) {
me.receiveK += (me.receiveB >> 10);
me.receiveB &= 0x03ff;
}
}
/*
* client_dopacket - copy packet to client buf and parse it
* client_p - pointer to client structure for which the buffer data
* applies.
* buffer - pointr to the buffer containing the newly read data
* length - number of valid bytes of data in the buffer
*
*/
static inline void restbuf_create(aClient *cptr)
{
if (!HasRestbuf(cptr)) {
cptr->restbuf = MyMalloc(sizeof(char *) * READBUF_SIZE * 4);
cptr->protoflags |= PFLAGS_HASRESTBUF;
}
}
int binary_dopacket(aClient *cptr, char *buffer, size_t length)
{
#ifdef HAVE_ENCRYPTION_ON
if (IsRC4IN(cptr))
rc4_process_stream(cptr->serv->rc4_in, buffer, length);
#endif
if (ZipIn(cptr)) {
if (unzip_buffer(cptr, buffer, length, server_dopacket) < 0) {
sendto_gnotice("Unzip error for %s: (%d) %s", cptr->name, errno, buffer);
return exit_client(cptr, &me, "fatal error in unzip!");
}
return 0;
}
return server_dopacket(cptr, buffer, length);
}
int server_dopacket(aClient *cptr, char *buffer, size_t length)
{
int broken = 1;
int tmplen = 1;
int parseval = 0;
int fd_r = 0;
char t;
if (cptr->waitlen > 0) {
memcpy(cptr->restbuf + cptr->waitlen, buffer, length);
buffer = cptr->restbuf;
length += cptr->waitlen;
memset(cptr->restbuf + length, 0, sizeof(char) * ((READBUF_SIZE * 4) - length));
}
process_bytecount(cptr, length);
/* Now we have to play the following game:
* It is obvious, that the buffer can contain cr/lf characters.
* we have to parse it, as soon as we receive a \r or a \n.
* and then we have to start parse again, for the next part of
* the buffer. this is done continously, until the buffer is
* is finished. -TimeMr14C
*/
while (length) {
tmplen = 0;
broken = 1;
cptr->waitlen = 0;
while (length) {
t = buffer[tmplen];
length--;
if ((t == '\n') || (t == '\r')) {
broken = 0;
break;
}
tmplen++;
}
if ((tmplen > 0) && !broken) {
buffer[tmplen] = '\0';
parseval = server_parse(cptr, buffer, buffer + tmplen);
buffer = buffer + tmplen + 1;
switch (parseval) {
case CLIENT_EXITED:
/* Exit_client is finished, fd is removed, there is
* nothing to send to or read from. ignore. -TimeMr14C
*/
return 0;
case FLUSH_BUFFER:
comm_setselect(cptr->fd, FDLIST_IRCD, COMM_SELECT_WRITE, send_queued, cptr, 0);
break;
case ZIP_NEXT_BUFFER:
if (ZipIn(cptr)) {
if (unzip_buffer(cptr, buffer, length, server_dopacket) < 0) {
sendto_gnotice("Another Unzip error for %s: (%d) %s", cptr->name, errno,
buffer);
exit_client(cptr, &me, "fatal error in unzip!");
return parseval;
}
return 0;
}
break;
#ifdef HAVE_ENCRYPTION_ON
case RC4_NEXT_BUFFER:
if (length)
rc4_process_stream(cptr->serv->rc4_in, buffer, length);
break;
#endif
default:
break;
}
} else if ((tmplen <= 0) && !broken) {
buffer = buffer + tmplen + 1;
} else if (broken) {
restbuf_create(cptr);
cptr->waitlen = tmplen;
memcpy(cptr->restbuf, buffer, cptr->waitlen);
}
}
/* server fd may have changed */
fd_r = cptr->fd;
if (!IsDead(cptr))
comm_setselect(fd_r, FDLIST_IRCD, COMM_SELECT_READ, read_server_packet, cptr, 0);
return 0;
}
int client_dopacket(aClient *cptr, char *buffer, size_t length)
{
process_bytecount(cptr, length);
if (user_parse(cptr, buffer, buffer + length) < 0)
return -2;
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1