Handle 1-character messages terminated with CR or LF correctly

Code cleanup and fix for Bug #83, "ngIRCd chokes on 1-character messages" in
function Handle_Buffer(): the buffer is now correctly cleared when ngIRCd
receives 1-character messages terminated with either CR or LF (in violation
to RFC 2812, section 2.3 "Messages", 5th paragraph).
This commit is contained in:
Alexander Barton 2008-05-02 02:14:15 +02:00
parent 4e507881f3
commit 2a790861a1
2 changed files with 63 additions and 42 deletions

View File

@ -12,6 +12,7 @@
ngIRCd-dev ngIRCd-dev
- Fixed Bug 83: ngIRCd chokes on 1-character messages.
- Add support for modeless channels ("+channels"). - Add support for modeless channels ("+channels").
(Bryan Caldwell, Ali Shemiran) (Bryan Caldwell, Ali Shemiran)

View File

@ -1218,11 +1218,13 @@ Read_Request( CONN_ID Idx )
} /* Read_Request */ } /* Read_Request */
/**
* Handle data in connection read-buffer.
* @return true if a reuqest was handled, false otherwise (and on errors).
*/
static bool static bool
Handle_Buffer(CONN_ID Idx) Handle_Buffer(CONN_ID Idx)
{ {
/* Handle Data in Connections Read-Buffer.
* Return true if a reuqest was handled, false otherwise (also returned on errors). */
#ifndef STRICT_RFC #ifndef STRICT_RFC
char *ptr1, *ptr2; char *ptr1, *ptr2;
#endif #endif
@ -1238,64 +1240,80 @@ Handle_Buffer( CONN_ID Idx )
result = false; result = false;
for (;;) { for (;;) {
/* Check penalty */ /* Check penalty */
if( My_Connections[Idx].delaytime > starttime) return result; if (My_Connections[Idx].delaytime > starttime)
return result;
#ifdef ZLIB #ifdef ZLIB
/* unpack compressed data */ /* Unpack compressed data, if compression is in use */
if ( Conn_OPTION_ISSET( &My_Connections[Idx], CONN_ZIP )) if (Conn_OPTION_ISSET(&My_Connections[Idx], CONN_ZIP)) {
if( ! Unzip_Buffer( Idx )) return false; if (!Unzip_Buffer(Idx))
return false;
}
#endif #endif
if (0 == array_bytes(&My_Connections[Idx].rbuf)) if (0 == array_bytes(&My_Connections[Idx].rbuf))
break; break;
if (!array_cat0_temporary(&My_Connections[Idx].rbuf)) /* make sure buf is NULL terminated */ /* Make sure that the buffer is NULL terminated */
if (!array_cat0_temporary(&My_Connections[Idx].rbuf))
return false; return false;
/* A Complete Request end with CR+LF, see RFC 2812. */ /* RFC 2812, section "2.3 Messages", 5th paragraph:
* "IRC messages are always lines of characters terminated
* with a CR-LF (Carriage Return - Line Feed) pair [...]". */
delta = 2;
ptr = strstr(array_start(&My_Connections[Idx].rbuf), "\r\n"); ptr = strstr(array_start(&My_Connections[Idx].rbuf), "\r\n");
if( ptr ) delta = 2; /* complete request */
#ifndef STRICT_RFC #ifndef STRICT_RFC
else { if (!ptr) {
/* Check for non-RFC-compliant request (only CR or LF)? Unfortunately, /* Check for non-RFC-compliant request (only CR or
* there are quite a few clients that do this (incl. "mIRC" :-( */ * LF)? Unfortunately, there are quite a few clients
* out there that do this -- incl. "mIRC" :-( */
delta = 1;
ptr1 = strchr(array_start(&My_Connections[Idx].rbuf), '\r'); ptr1 = strchr(array_start(&My_Connections[Idx].rbuf), '\r');
ptr2 = strchr(array_start(&My_Connections[Idx].rbuf), '\n'); ptr2 = strchr(array_start(&My_Connections[Idx].rbuf), '\n');
delta = 1; if (ptr1 && ptr2)
if( ptr1 && ptr2 ) ptr = ptr1 > ptr2 ? ptr2 : ptr1; ptr = ptr1 > ptr2 ? ptr2 : ptr1;
else if( ptr1 ) ptr = ptr1; else if (ptr1)
else if( ptr2 ) ptr = ptr2; ptr = ptr1;
else if (ptr2)
ptr = ptr2;
} }
#endif #endif
if (!ptr) if (!ptr)
break; break;
/* End of request found */ /* Complete (=line terminated) request found, handle it! */
*ptr = '\0'; *ptr = '\0';
len = ( ptr - (char*) array_start(&My_Connections[Idx].rbuf)) + delta; len = ptr - (char *)array_start(&My_Connections[Idx].rbuf) + delta;
if (len > (COMMAND_LEN - 1)) { if (len > (COMMAND_LEN - 1)) {
/* Request must not exceed 512 chars (incl. CR+LF!), see /* Request must not exceed 512 chars (incl. CR+LF!),
* RFC 2812. Disconnect Client if this happens. */ * see RFC 2812. Disconnect Client if this happens. */
Log( LOG_ERR, "Request too long (connection %d): %d bytes (max. %d expected)!", Log(LOG_ERR,
Idx, array_bytes(&My_Connections[Idx].rbuf), COMMAND_LEN - 1 ); "Request too long (connection %d): %d bytes (max. %d expected)!",
Idx, array_bytes(&My_Connections[Idx].rbuf),
COMMAND_LEN - 1);
Conn_Close(Idx, NULL, "Request too long", true); Conn_Close(Idx, NULL, "Request too long", true);
return false; return false;
} }
if (len <= 2) { /* request was empty (only '\r\n') */ if (len <= delta) {
array_moveleft(&My_Connections[Idx].rbuf, 1, delta); /* delta is either 1 or 2 */ /* 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);
break; break;
} }
#ifdef ZLIB #ifdef ZLIB
/* remember if stream is already compressed */ /* remember if stream is already compressed */
old_z = My_Connections[Idx].options & CONN_ZIP; old_z = My_Connections[Idx].options & CONN_ZIP;
#endif #endif
My_Connections[Idx].msg_in++; My_Connections[Idx].msg_in++;
if (!Parse_Request(Idx, (char*)array_start(&My_Connections[Idx].rbuf) )) if (!Parse_Request
(Idx, (char *)array_start(&My_Connections[Idx].rbuf)))
return false; return false;
result = true; result = true;
@ -1305,19 +1323,21 @@ Handle_Buffer( CONN_ID Idx )
Idx, array_bytes(&My_Connections[Idx].rbuf)); Idx, array_bytes(&My_Connections[Idx].rbuf));
#ifdef ZLIB #ifdef ZLIB
if ((!old_z) && (My_Connections[Idx].options & CONN_ZIP) && if ((!old_z) && (My_Connections[Idx].options & CONN_ZIP) &&
( array_bytes(&My_Connections[Idx].rbuf) > 0 )) (array_bytes(&My_Connections[Idx].rbuf) > 0)) {
{ /* The last command activated socket compression.
/* The last Command activated Socket-Compression. * Data that was read after that needs to be copied
* Data that was read after that needs to be copied to Unzip-buf * to the unzip buffer for decompression: */
* for decompression */ if (!array_copy
if (!array_copy( &My_Connections[Idx].zip.rbuf, &My_Connections[Idx].rbuf )) (&My_Connections[Idx].zip.rbuf,
&My_Connections[Idx].rbuf))
return false; return false;
array_trunc(&My_Connections[Idx].rbuf); array_trunc(&My_Connections[Idx].rbuf);
LogDebug("Moved already received data (%u bytes) to uncompression buffer.", LogDebug
("Moved already received data (%u bytes) to uncompression buffer.",
array_bytes(&My_Connections[Idx].zip.rbuf)); array_bytes(&My_Connections[Idx].zip.rbuf));
} }
#endif /* ZLIB */ #endif
} }
return result; return result;
} /* Handle_Buffer */ } /* Handle_Buffer */