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:
parent
4e507881f3
commit
2a790861a1
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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,86 +1240,104 @@ 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:
|
||||||
ptr = strstr( array_start(&My_Connections[Idx].rbuf), "\r\n" );
|
* "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");
|
||||||
|
|
||||||
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
|
||||||
ptr1 = strchr( array_start(&My_Connections[Idx].rbuf), '\r' );
|
* out there that do this -- incl. "mIRC" :-( */
|
||||||
ptr2 = strchr( array_start(&My_Connections[Idx].rbuf), '\n' );
|
|
||||||
delta = 1;
|
delta = 1;
|
||||||
if( ptr1 && ptr2 ) ptr = ptr1 > ptr2 ? ptr2 : ptr1;
|
ptr1 = strchr(array_start(&My_Connections[Idx].rbuf), '\r');
|
||||||
else if( ptr1 ) ptr = ptr1;
|
ptr2 = strchr(array_start(&My_Connections[Idx].rbuf), '\n');
|
||||||
else if( ptr2 ) ptr = ptr2;
|
if (ptr1 && ptr2)
|
||||||
|
ptr = ptr1 > ptr2 ? ptr2 : ptr1;
|
||||||
|
else if (ptr1)
|
||||||
|
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)!",
|
||||||
Conn_Close( Idx, NULL, "Request too long", true );
|
Idx, array_bytes(&My_Connections[Idx].rbuf),
|
||||||
|
COMMAND_LEN - 1);
|
||||||
|
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;
|
||||||
|
|
||||||
array_moveleft(&My_Connections[Idx].rbuf, 1, len);
|
array_moveleft(&My_Connections[Idx].rbuf, 1, len);
|
||||||
LogDebug("Connection %d: %d bytes left in read buffer.",
|
LogDebug("Connection %d: %d bytes left in read buffer.",
|
||||||
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
|
||||||
array_bytes(&My_Connections[Idx].zip.rbuf));
|
("Moved already received data (%u bytes) to uncompression buffer.",
|
||||||
|
array_bytes(&My_Connections[Idx].zip.rbuf));
|
||||||
}
|
}
|
||||||
#endif /* ZLIB */
|
#endif
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} /* Handle_Buffer */
|
} /* Handle_Buffer */
|
||||||
|
|
Loading…
Reference in New Issue