/*
* ngIRCd -- The Next Generation IRC Daemon
- * Copyright (c)2001-2007 Alexander Barton (alex@barton.de)
+ * Copyright (c)2001-2009 Alexander Barton (alex@barton.de)
*
* 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
#define SERVER_WAIT (NONE - 1)
+#define MAX_COMMANDS 3
+#define MAX_COMMANDS_SERVER 10
+
static bool Handle_Write PARAMS(( CONN_ID Idx ));
static bool Conn_Write PARAMS(( CONN_ID Idx, char *Data, size_t Len ));
static int New_Connection PARAMS(( int Sock ));
static CONN_ID Socket2Index PARAMS(( int Sock ));
static void Read_Request PARAMS(( CONN_ID Idx ));
-static void Handle_Buffer PARAMS(( CONN_ID Idx ));
+static unsigned int Handle_Buffer PARAMS(( CONN_ID Idx ));
static void Check_Connections PARAMS(( void ));
static void Check_Servers PARAMS(( void ));
static void Init_Conn_Struct PARAMS(( CONN_ID Idx ));
static void
cb_connserver(int sock, UNUSED short what)
{
- int res, err;
+ int res, err, server;
socklen_t sock_len;
CONN_ID idx = Socket2Index( sock );
+
if (idx <= NONE) {
LogDebug("cb_connserver wants to write on unknown socket?!");
io_close(sock);
return;
}
- assert( what & IO_WANTWRITE);
+ assert(what & IO_WANTWRITE);
+
+ /* Make sure that the server is still configured; it could have been
+ * removed in the meantime! */
+ server = Conf_GetServer(idx);
+ if (server < 0) {
+ Log(LOG_ERR, "Connection on socket %d to \"%s\" aborted!",
+ sock, My_Connections[idx].host);
+ Conn_Close(idx, "Connection aborted!", NULL, false);
+ return;
+ }
/* connect() finished, get result. */
- sock_len = sizeof( err );
- res = getsockopt( My_Connections[idx].sock, SOL_SOCKET, SO_ERROR, &err, &sock_len );
- assert( sock_len == sizeof( err ));
+ sock_len = (socklen_t)sizeof(err);
+ res = getsockopt(My_Connections[idx].sock, SOL_SOCKET, SO_ERROR,
+ &err, &sock_len );
+ assert(sock_len == sizeof(err));
/* Error while connecting? */
if ((res != 0) || (err != 0)) {
else
Log(LOG_CRIT,
"Can't connect socket to \"%s:%d\" (connection %d): %s!",
- My_Connections[idx].host,
- Conf_Server[Conf_GetServer(idx)].port,
+ My_Connections[idx].host, Conf_Server[server].port,
idx, strerror(err));
- res = Conf_GetServer(idx);
- assert(res >= 0);
-
Conn_Close(idx, "Can't connect!", NULL, false);
- if (res < 0)
- return;
- if (ng_ipaddr_af(&Conf_Server[res].dst_addr[0])) {
+ if (ng_ipaddr_af(&Conf_Server[server].dst_addr[0])) {
/* more addresses to try... */
- New_Server(res, &Conf_Server[res].dst_addr[0]);
- /* connection to dst_addr[0] in progress, remove this address... */
- Conf_Server[res].dst_addr[0] = Conf_Server[res].dst_addr[1];
-
- memset(&Conf_Server[res].dst_addr[1], 0, sizeof(&Conf_Server[res].dst_addr[1]));
+ New_Server(res, &Conf_Server[server].dst_addr[0]);
+ /* connection to dst_addr[0] is now in progress, so
+ * remove this address... */
+ Conf_Server[server].dst_addr[0] =
+ Conf_Server[server].dst_addr[1];
+ memset(&Conf_Server[server].dst_addr[1], 0,
+ sizeof(Conf_Server[server].dst_addr[1]));
}
return;
}
- res = Conf_GetServer(idx);
- assert(res >= 0);
- if (res >= 0) /* connect succeeded, remove all additional addresses */
- memset(&Conf_Server[res].dst_addr, 0, sizeof(&Conf_Server[res].dst_addr));
+ /* connect() succeeded, remove all additional addresses */
+ memset(&Conf_Server[server].dst_addr, 0,
+ sizeof(Conf_Server[server].dst_addr));
+
Conn_OPTION_DEL( &My_Connections[idx], CONN_ISCONNECTING );
#ifdef SSL_SUPPORT
if ( Conn_OPTION_ISSET( &My_Connections[idx], CONN_SSL_CONNECT )) {
if (af != AF_INET6)
return;
- if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)))
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, (socklen_t)sizeof(on)))
Log(LOG_ERR, "Could not set IPV6_V6ONLY: %s", strerror(errno));
#else
(void)af;
Conn_Handler(void)
{
int i;
- unsigned int wdatalen;
+ unsigned int wdatalen, bytes_processed;
struct timeval tv;
time_t t;
for (i = 0; i < Pool_Size; i++) {
if ((My_Connections[i].sock > NONE)
&& (array_bytes(&My_Connections[i].rbuf) > 0)
- && (My_Connections[i].delaytime < t)) {
+ && (My_Connections[i].delaytime <= t)) {
/* ... and try to handle the received data */
- Handle_Buffer(i);
+ bytes_processed = Handle_Buffer(i);
+ /* if we processed data, and there might be
+ * more commands in the input buffer, do not
+ * try to read any more data now */
+ if (bytes_processed &&
+ array_bytes(&My_Connections[i].rbuf) > 2) {
+ LogDebug
+ ("Throttling connection %d: command limit reached!",
+ i);
+ Conn_SetPenalty(i, 1);
+ }
}
}
* sub-processes are closed down. */
CLIENT *c;
- const char *txt;
double in_k, out_k;
UINT16 port;
#ifdef ZLIB
/* Mark link as "closing" */
Conn_OPTION_ADD( &My_Connections[Idx], CONN_ISCLOSING );
- if (LogMsg)
- txt = LogMsg;
- else
- txt = FwdMsg;
- if (! txt)
- txt = "Reason unknown";
-
port = ng_ipaddr_getport(&My_Connections[Idx].addr);
Log(LOG_INFO, "Shutting down connection %d (%s) with %s:%d ...", Idx,
LogMsg ? LogMsg : FwdMsg, My_Connections[Idx].host, port);
(double)My_Connections[Idx].bytes_out / 1024);
}
#endif
- /* Send ERROR to client (see RFC!) */
+ /* Send ERROR to client (see RFC 2812, section 3.1.7) */
if (FwdMsg)
Conn_WriteStr(Idx, "ERROR :%s", FwdMsg);
else
Read_Request( CONN_ID Idx )
{
ssize_t len;
+ static const unsigned int maxbps = COMMAND_LEN / 2;
char readbuf[READBUFFER_LEN];
+ time_t t;
CLIENT *c;
assert( Idx > NONE );
assert( My_Connections[Idx].sock > NONE );
if (c && (Client_Type(c) == CLIENT_USER
|| Client_Type(c) == CLIENT_SERVER
|| Client_Type(c) == CLIENT_SERVICE)) {
- My_Connections[Idx].lastdata = time(NULL);
+ t = time(NULL);
+ if (My_Connections[Idx].lastdata != t)
+ My_Connections[Idx].bps = 0;
+
+ My_Connections[Idx].lastdata = t;
My_Connections[Idx].lastping = My_Connections[Idx].lastdata;
}
/* Look at the data in the (read-) buffer of this connection */
- Handle_Buffer(Idx);
+ My_Connections[Idx].bps += Handle_Buffer(Idx);
+ if (Client_Type(c) != CLIENT_SERVER
+ && My_Connections[Idx].bps >= maxbps) {
+ LogDebug("Throttling connection %d: BPS exceeded! (%u >= %u)",
+ Idx, My_Connections[Idx].bps, maxbps);
+ Conn_SetPenalty(Idx, 1);
+ }
} /* Read_Request */
/**
* Handle all data in the connection read-buffer.
- * All data is precessed until no complete command is left. When a fatal
- * error occurs, the connection is shut down.
+ * Data is processed until no complete command is left in the read buffer,
+ * or MAX_COMMANDS[_SERVER] commands were processed.
+ * When a fatal error occurs, the connection is shut down.
+ * @param Idx Index of the connection.
+ * @return number of bytes processed.
*/
-static void
+static unsigned int
Handle_Buffer(CONN_ID Idx)
{
#ifndef STRICT_RFC
#ifdef ZLIB
bool old_z;
#endif
+ unsigned int i, maxcmd = MAX_COMMANDS, len_processed = 0;
+ CLIENT *c;
+
+ c = Conn_GetClient(Idx);
+ assert( c != NULL);
+
+ /* Servers do get special command limits, so they can process
+ * all the messages that are required while peering. */
+ if (Client_Type(c) == CLIENT_SERVER)
+ maxcmd = MAX_COMMANDS_SERVER;
starttime = time(NULL);
- for (;;) {
+ for (i=0; i < maxcmd; i++) {
/* Check penalty */
if (My_Connections[Idx].delaytime > starttime)
- return;
+ return 0;
#ifdef ZLIB
/* Unpack compressed data, if compression is in use */
if (Conn_OPTION_ISSET(&My_Connections[Idx], CONN_ZIP)) {
/* When unzipping fails, Unzip_Buffer() shuts
* down the connection itself */
if (!Unzip_Buffer(Idx))
- return;
+ return 0;
}
#endif
if (0 == array_bytes(&My_Connections[Idx].rbuf))
- return;
+ break;
/* Make sure that the buffer is NULL terminated */
if (!array_cat0_temporary(&My_Connections[Idx].rbuf)) {
Conn_Close(Idx, NULL,
"Can't allocate memory [Handle_Buffer]",
true);
- return;
+ return 0;
}
/* RFC 2812, section "2.3 Messages", 5th paragraph:
#endif
if (!ptr)
- return;
+ break;
/* Complete (=line terminated) request found, handle it! */
*ptr = '\0';
Idx, array_bytes(&My_Connections[Idx].rbuf),
COMMAND_LEN - 1);
Conn_Close(Idx, NULL, "Request too long", true);
- return;
+ return 0;
}
+ len_processed += (unsigned int)len;
if (len <= delta) {
/* Request is empty (only '\r\n', '\r' or '\n');
* delta is 2 ('\r\n') or 1 ('\r' or '\n'), see above */
array_moveleft(&My_Connections[Idx].rbuf, 1, len);
- return;
+ continue;
}
-
#ifdef ZLIB
/* remember if stream is already compressed */
old_z = My_Connections[Idx].options & CONN_ZIP;
My_Connections[Idx].msg_in++;
if (!Parse_Request
(Idx, (char *)array_start(&My_Connections[Idx].rbuf)))
- return;
+ return 0; /* error -> connection has been closed */
array_moveleft(&My_Connections[Idx].rbuf, 1, len);
LogDebug("Connection %d: %d bytes left in read buffer.",
Conn_Close(Idx, NULL,
"Can't allocate memory [Handle_Buffer]",
true);
- return;
+ return 0;
}
array_trunc(&My_Connections[Idx].rbuf);
}
#endif
}
+ return len_processed;
} /* Handle_Buffer */
return;
}
+ /* Conn_Close() decrements this counter again */
+ NumConnections++;
Client_SetIntroducer( c, c );
Client_SetToken( c, TOKEN_OUTBOUND );
return;
}
#endif
- NumConnections++;
LogDebug("Registered new connection %d on socket %d (%ld in total).",
new_sock, My_Connections[new_sock].sock, NumConnections);
Conn_OPTION_ADD( &My_Connections[new_sock], CONN_ISCONNECTING );
size_t len;
ng_ipaddr_t dest_addrs[4]; /* we can handle at most 3; but we read up to
four so we can log the 'more than we can handle'
- condition */
+ condition. First result is tried immediately, rest
+ is saved for later if needed. */
LogDebug("Resolver: Got forward lookup callback on fd %d, events %d", fd, events);
LogDebug("Got result from resolver: %u structs (%u bytes).", len/sizeof(ng_ipaddr_t), len);
- memset(&Conf_Server[i].dst_addr, 0, sizeof(&Conf_Server[i].dst_addr));
+ memset(&Conf_Server[i].dst_addr, 0, sizeof(Conf_Server[i].dst_addr));
if (len > sizeof(ng_ipaddr_t)) {
/* more than one address for this hostname, remember them
* in case first address is unreachable/not available */
len -= sizeof(ng_ipaddr_t);
- if (len > sizeof(&Conf_Server[i].dst_addr)) {
- len = sizeof(&Conf_Server[i].dst_addr);
+ if (len > sizeof(Conf_Server[i].dst_addr)) {
+ len = sizeof(Conf_Server[i].dst_addr);
Log(LOG_NOTICE,
"Notice: Resolver returned more IP Addresses for host than we can handle, additional addresses dropped.");
}
return Conn_OPTION_ISSET(&My_Connections[Idx], CONN_SSL);
}
#endif
+
+
/* -eof- */