RFC 2812 states that the SQUIT command is available to (IRC) operators, but when any client (IRCop or not) issues an SQUIT command, ngIRCd replies with ERR_NOTREGISTERED, which is not permitted by the RFC.
Also, quoting from RFC 2812:
The INFO command is REQUIRED to return information describing the server: its version, when it was compiled, the patchlevel, when it was started, and any other miscellaneous information which may be considered to be relevant.
Implementing the SUMMON command is not required, but:
If summon is not enabled in a server, it MUST return the ERR_SUMMONDISABLED numeric.
Likewise for the USERS command:
If disabled, the correct numeric MUST be returned to indicate this.
The patch below adds a test (called misc-test for lack of a better name) for these commands and modifies:
* testsuite/Makefile.am to run misc-test; * parse.c and irc-server.c to make IRC_SQUIT handle client requests; and * parse.c and irc-info.{h,c} to reply to INFO, SUMMON, and USERS.
It is perhaps worth noting the commands SERVICE, SERVLIST, and SQUERY, which remain unimplemented, are described in RFC 2812 section 3, which also states that "All commands described in this section MUST be implemented by any server for this protocol."
Dana
--- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/testsuite/misc-test.e 17 Feb 2008 06:47:42 -0000 @@ -0,0 +1,50 @@ +# $Id$ + +spawn telnet localhost 6789 +expect { + timeout { exit 1 } + "Connected" +} + +send "nick nick\r" +send "user user . . :User\r" +expect { + timeout { exit 1 } + "376" +} + +send "summon\r" +expect { + timeout { exit 1 } + "445" +} + +send "users\r" +expect { + timeout { exit 1 } + "446" +} + +send "info\r" +expect { + timeout { exit 1 } + "371" +} +expect { + timeout { exit 1 } + "374" +} + +send "squit\r" +expect { + timeout { exit 1 } + "481" +} + +send "quit\r" +expect { + timeout { exit 1 } + "ERROR" +} + +# -eof- --- src/testsuite/Makefile.am 17 Feb 2008 00:00:13 -0000 1.17 +++ src/testsuite/Makefile.am 17 Feb 2008 06:47:42 -0000 @@ -21,7 +21,7 @@ EXTRA_DIST = \ start-server.sh stop-server.sh tests.sh stress-server.sh \ test-loop.sh wait-tests.sh \ connect-test.e channel-test.e mode-test.e \ - who-test.e + who-test.e misc-test.e stress-A.e stress-B.e check-idle.e \ ngircd-test.conf
@@ -52,6 +52,10 @@ who-test: tests.sh rm -f who-test ln -s $(srcdir)/tests.sh who-test
+misc-test: tests.sh + rm -f misc-test + ln -s $(srcdir)/tests.sh misc-test + mode-test: tests.sh rm -f mode-test ln -s $(srcdir)/tests.sh mode-test @@ -60,6 +64,7 @@ TESTS = start-server.sh \ connect-test \ channel-test \ who-test \ + misc-test \ mode-test \ stress-server.sh \ stop-server.sh --- src/ngircd/parse.c 5 Feb 2008 13:07:14 -0000 1.71 +++ src/ngircd/parse.c 17 Feb 2008 06:47:42 -0000 @@ -67,6 +67,7 @@ static COMMAND My_Commands[] = { "DISCONNECT", IRC_DISCONNECT, CLIENT_USER, 0, 0, 0 }, { "ERROR", IRC_ERROR, 0xFFFF, 0, 0, 0 }, { "HELP", IRC_HELP, CLIENT_USER, 0, 0, 0 }, + { "INFO", IRC_INFO, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, { "INVITE", IRC_INVITE, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, { "ISON", IRC_ISON, CLIENT_USER, 0, 0, 0 }, { "JOIN", IRC_JOIN, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, @@ -91,13 +92,15 @@ static COMMAND My_Commands[] = { "REHASH", IRC_REHASH, CLIENT_USER, 0, 0, 0 }, { "RESTART", IRC_RESTART, CLIENT_USER, 0, 0, 0 }, { "SERVER", IRC_SERVER, 0xFFFF, 0, 0, 0 }, - { "SQUIT", IRC_SQUIT, CLIENT_SERVER, 0, 0, 0 }, + { "SQUIT", IRC_SQUIT, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, { "STATS", IRC_STATS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, + { "SUMMON", IRC_SUMMON, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, { "TIME", IRC_TIME, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, { "TOPIC", IRC_TOPIC, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, { "TRACE", IRC_TRACE, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, { "USER", IRC_USER, 0xFFFF, 0, 0, 0 }, { "USERHOST", IRC_USERHOST, CLIENT_USER, 0, 0, 0 }, + { "USERS", IRC_USERS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, { "VERSION", IRC_VERSION, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, { "WALLOPS", IRC_WALLOPS, CLIENT_USER|CLIENT_SERVER, 0, 0, 0 }, { "WHO", IRC_WHO, CLIENT_USER, 0, 0, 0 }, --- src/ngircd/irc-info.h 11 Feb 2008 11:06:31 -0000 1.5 +++ src/ngircd/irc-info.h 17 Feb 2008 06:47:42 -0000 @@ -19,14 +19,17 @@
GLOBAL bool IRC_ADMIN PARAMS(( CLIENT *Client, REQUEST *Req )); +GLOBAL bool IRC_INFO PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_ISON PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_LINKS PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_LUSERS PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_MOTD PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_NAMES PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_STATS PARAMS(( CLIENT *Client, REQUEST *Req )); +GLOBAL bool IRC_SUMMON PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_TIME PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_USERHOST PARAMS(( CLIENT *Client, REQUEST *Req )); +GLOBAL bool IRC_USERS PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_VERSION PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_WHO PARAMS(( CLIENT *Client, REQUEST *Req )); GLOBAL bool IRC_WHOIS PARAMS(( CLIENT *Client, REQUEST *Req )); --- src/ngircd/messages.h 11 Dec 2007 11:29:44 -0000 1.74 +++ src/ngircd/messages.h 17 Feb 2008 06:47:42 -0000 @@ -77,6 +77,8 @@ #define RPL_BANLIST_MSG "367 %s %s %s" #define RPL_ENDOFBANLIST_MSG "368 %s %s :End of channel ban list" #define RPL_ENDOFWHOWAS_MSG "369 %s %s :End of WHOWAS list" +#define RPL_INFO_MSG "371 %s :%s" +#define RPL_ENDOFINFO_MSG "374 %s :End of INFO list" #define RPL_MOTD_MSG "372 %s :- %s" #define RPL_MOTDSTART_MSG "375 %s :- %s message of the day" #define RPL_ENDOFMOTD_MSG "376 %s :End of MOTD command" @@ -100,6 +102,8 @@ #define ERR_USERNOTINCHANNEL_MSG "441 %s %s %s :They aren't on that channel" #define ERR_NOTONCHANNEL_MSG "442 %s %s :You are not on that channel" #define ERR_USERONCHANNEL_MSG "443 %s %s %s :is already on channel" +#define ERR_SUMMONDISABLED_MSG "445 %s %s :Command disabled" +#define ERR_USERSDISABLED_MSG "446 %s %s :Command disabled" #define ERR_NOTREGISTERED_MSG "451 %s :Connection not registered" #define ERR_NOTREGISTEREDSERVER_MSG "451 %s :Connection not registered as server link" #define ERR_NEEDMOREPARAMS_MSG "461 %s %s :Syntax error" --- src/ngircd/irc-server.c 21 Nov 2007 12:16:36 -0000 1.46 +++ src/ngircd/irc-server.c 17 Feb 2008 06:47:42 -0000 @@ -285,6 +285,9 @@ IRC_SQUIT( CLIENT *Client, REQUEST *Req assert( Client != NULL ); assert( Req != NULL );
+ if (! (Client_OperByMe(Client) || Client_Type(Client) == CLIENT_SERVER)) + return IRC_WriteStrClient( Client, ERR_NOPRIVILEGES_MSG, Client_ID( Client ) ); + /* Falsche Anzahl Parameter? */ if( Req->argc != 2 ) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
--- src/ngircd/irc-info.c 17 Feb 2008 00:00:12 -0000 1.43 +++ src/ngircd/irc-info.c 17 Feb 2008 06:47:42 -0000 @@ -86,6 +86,71 @@ IRC_ADMIN(CLIENT *Client, REQUEST *Req )
GLOBAL bool +IRC_INFO( CLIENT *Client, REQUEST *Req ) +{ + CLIENT *target, *prefix; + + assert( Client != NULL ); + assert( Req != NULL ); + + /* Wrong number of parameters? */ + if(( Req->argc > 1 )) return IRC_WriteStrClient( Client, ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command ); + + /* Look for a target. */ + if( Req->argc == 1 ) target = Client_Search( Req->argv[0] ); + else target = Client_ThisServer( ); + + /* Determine prefix. */ + if( Client_Type( Client ) == CLIENT_SERVER ) prefix = Client_Search( Req->prefix ); + else prefix = Client; + if( ! prefix ) return IRC_WriteStrClient( Client, ERR_NOSUCHNICK_MSG, Client_ID( Client ), Req->prefix ); + + /* Pass on to another server? */ + if( target != Client_ThisServer( )) + { + if(( ! target ) || ( Client_Type( target ) != CLIENT_SERVER )) return IRC_WriteStrClient( prefix, ERR_NOSUCHSERVER_MSG, Client_ID( prefix ), Req->argv[0] ); + + /* forward */ + IRC_WriteStrClientPrefix( target, prefix, "INFO %s", Req->argv[0] ); + return CONNECTED; + } + + /* Answer with version info. */ + { + char msg[510]; + + strlcpy(msg, PACKAGE_NAME, sizeof(msg)); + strlcat(msg, "-", sizeof(msg)); + strlcat(msg, PACKAGE_VERSION, sizeof(msg)); + if( ! IRC_WriteStrClient( Client, RPL_INFO_MSG, Client_ID( prefix ), msg )) return DISCONNECTED; + +#ifdef CVSDATE + strlcpy(msg, "last revision ", sizeof(msg)); + strlcat(msg, CVSDATE, sizeof(msg)); + if( ! IRC_WriteStrClient( Client, RPL_INFO_MSG, Client_ID( prefix ), msg )) return DISCONNECTED; +#endif + + strlcpy(msg, "compiled ", sizeof(msg)); + strlcat(msg, __DATE__, sizeof(msg)); + if( ! IRC_WriteStrClient( Client, RPL_INFO_MSG, Client_ID( prefix ), msg )) return DISCONNECTED; + + strlcpy(msg, "with options ", sizeof(msg)); + strlcat(msg, NGIRCd_VersionAddition, sizeof(msg)); + if( ! IRC_WriteStrClient( Client, RPL_INFO_MSG, Client_ID( prefix ), msg )) return DISCONNECTED; + + strlcpy(msg, "started ", sizeof(msg)); + strlcat(msg, NGIRCd_StartStr, sizeof(msg)); + if( ! IRC_WriteStrClient( Client, RPL_INFO_MSG, Client_ID( prefix ), msg )) return DISCONNECTED; + + if( ! IRC_WriteStrClient( Client, RPL_ENDOFINFO_MSG, Client_ID( prefix ) )) return DISCONNECTED; + } + + IRC_SetPenalty( Client, 1 ); + return CONNECTED; +} /* IRC_INFO */ + + +GLOBAL bool IRC_ISON( CLIENT *Client, REQUEST *Req ) { char rpl[COMMAND_LEN]; @@ -470,6 +535,13 @@ IRC_STATS( CLIENT *Client, REQUEST *Req
GLOBAL bool +IRC_SUMMON( CLIENT *Client, REQUEST *Req ) +{ + return IRC_WriteStrClient( Client, ERR_SUMMONDISABLED_MSG, Client_ID( Client ), Req->command ); +} /* IRC_SUMMON */ + + +GLOBAL bool IRC_TIME( CLIENT *Client, REQUEST *Req ) { CLIENT *from, *target; @@ -547,6 +619,13 @@ IRC_USERHOST( CLIENT *Client, REQUEST *R
GLOBAL bool +IRC_USERS( CLIENT *Client, REQUEST *Req ) +{ + return IRC_WriteStrClient( Client, ERR_USERSDISABLED_MSG, Client_ID( Client ), Req->command ); +} /* IRC_USERS */ + + +GLOBAL bool IRC_VERSION( CLIENT *Client, REQUEST *Req ) { CLIENT *target, *prefix;
Hi Dana!
Thanks, I applied your slightly modified patch to support INFO, SUMMON, and USERS to CVS HEAD.
I simplified INFO a little bit to only send the already prepared NGIRCd_Version string and the start time to get rid of a lot of strlcpy/strlcat calls.
It is perhaps worth noting the commands SERVICE, SERVLIST, and SQUERY, which remain unimplemented, are described in RFC 2812 section 3, which also states that "All commands described in this section MUST be implemented by any server for this protocol."
You are right, probably it would make sense to implement "dummy handlers" until we eventually support services ... I'll have a look at it.
--- src/ngircd/irc-server.c 21 Nov 2007 12:16:36 -0000 1.46 +++ src/ngircd/irc-server.c 17 Feb 2008 06:47:42 -0000 @@ -285,6 +285,9 @@ IRC_SQUIT( CLIENT *Client, REQUEST *Req assert( Client != NULL ); assert( Req != NULL );
- if (! (Client_OperByMe(Client) || Client_Type(Client) ==
CLIENT_SERVER))
return IRC_WriteStrClient( Client, ERR_NOPRIVILEGES_MSG,
Client_ID( Client ) );
- /* Falsche Anzahl Parameter? */ if( Req->argc != 2 ) return IRC_WriteStrClient( Client,
ERR_NEEDMOREPARAMS_MSG, Client_ID( Client ), Req->command );
This isn't sufficient at all, because at the moment SQUIT only unregisters the server but didn't disconnect it at all: the result is that the server isn't known any more but the connection still is established. I'll think about it ...
Regards Alex