From: Alexander Barton Date: Sun, 17 Aug 2008 15:29:41 +0000 (+0200) Subject: Announce IRC services in the network. X-Git-Tag: rel-13-rc1~31 X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=commitdiff_plain;h=178f9cbdac3bbeb58600268791916f3bfbcbd958 Announce IRC services in the network. This patch - introduces a new server flag "S" to indicate that the server can handle the SERVICE command (on server links), - implements the IRC command "SERVICE" for server-server links, - uses the "SERVICE" command to announce IRC services when a new server connects to it, - and fixes the Send_Message() function to let it send messages to services using a "target mask". If the remote server doesn't indicate that it can handle the "SERVICE" command (it has not set the "S" flag), services are announced as regular users as before. --- diff --git a/src/ngircd/defines.h b/src/ngircd/defines.h index cccf48b4..7abe6419 100644 --- a/src/ngircd/defines.h +++ b/src/ngircd/defines.h @@ -82,7 +82,7 @@ protocol, see doc/Protocol.txt */ #ifdef IRCPLUS -# define IRCPLUSFLAGS "CHL" /* Standard IRC+ flags */ +# define IRCPLUSFLAGS "CHLS" /* Standard IRC+ flags */ #endif #define STARTUP_DELAY 1 /* Delay outgoing connections n seconds diff --git a/src/ngircd/irc-login.c b/src/ngircd/irc-login.c index c189228a..943612ed 100644 --- a/src/ngircd/irc-login.c +++ b/src/ngircd/irc-login.c @@ -464,17 +464,25 @@ IRC_USER(CLIENT * Client, REQUEST * Req) /** - * Service registration. - * ngIRCd does not support services at the moment, so this function is a - * dummy that returns ERR_ERRONEUSNICKNAME on each call. + * Handler for the IRC command "SERVICE". + * This function implements IRC Services registration using the SERVICE command + * defined in RFC 2812 3.1.6 and RFC 2813 4.1.4. + * At the moment ngIRCd doesn't support directly linked services, so this + * function returns ERR_ERRONEUSNICKNAME when the SERVICE command has not been + * received from a peer server. */ GLOBAL bool IRC_SERVICE(CLIENT *Client, REQUEST *Req) { + CLIENT *c, *intr_c; + char *nick, *user, *host, *info, *modes, *ptr; + int token, hops; + assert(Client != NULL); assert(Req != NULL); - if (Client_Type(Client) != CLIENT_GOTPASS) + if (Client_Type(Client) != CLIENT_GOTPASS && + Client_Type(Client) != CLIENT_SERVER) return IRC_WriteStrClient(Client, ERR_ALREADYREGISTRED_MSG, Client_ID(Client)); @@ -482,8 +490,74 @@ IRC_SERVICE(CLIENT *Client, REQUEST *Req) return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, Client_ID(Client), Req->command); - return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG, + if (Client_Type(Client) != CLIENT_SERVER) + return IRC_WriteStrClient(Client, ERR_ERRONEUSNICKNAME_MSG, Client_ID(Client), Req->argv[0]); + + /* Bad number of parameters? */ + if (Req->argc != 6) + return IRC_WriteStrClient(Client, ERR_NEEDMOREPARAMS_MSG, + Client_ID(Client), Req->command); + + nick = Req->argv[0]; + user = NULL; host = NULL; + token = atoi(Req->argv[1]); + hops = atoi(Req->argv[4]); + info = Req->argv[5]; + + /* Validate service name ("nick name") */ + c = Client_Search(nick); + if(c) { + /* Nick name collission: disconnect (KILL) both clients! */ + Log(LOG_ERR, "Server %s introduces already registered service \"%s\"!", + Client_ID(Client), nick); + Kill_Nick(nick, "Nick collision"); + return CONNECTED; + } + + /* Get the server to which the service is connected */ + intr_c = Client_GetFromToken(Client, token); + if (! intr_c) { + Log(LOG_ERR, "Server %s introduces service \"%s\" on unknown server!?", + Client_ID(Client), nick); + Kill_Nick(nick, "Unknown server"); + return CONNECTED; + } + + /* Get user and host name */ + ptr = strchr(nick, '@'); + if (ptr) { + *ptr = '\0'; + host = ++ptr; + } + if (!host) + host = Client_Hostname(intr_c); + ptr = strchr(nick, '!'); + if (ptr) { + *ptr = '\0'; + user = ++ptr; + } + if (!user) + user = nick; + + /* According to RFC 2812/2813 parameter 4 "is currently reserved + * for future usage"; but we use it to transfer the modes and check + * that the first character is a '+' sign and ignore it otherwise. */ + modes = (Req->argv[3][0] == '+') ? ++Req->argv[3] : ""; + + c = Client_NewRemoteUser(intr_c, nick, hops, user, host, + token, modes, info, true); + if (! c) { + /* Couldn't create client structure, so KILL the service to + * keep network status consistent ... */ + Log(LOG_ALERT, "Can't create client structure! (on connection %d)", + Client_Conn(Client)); + Kill_Nick(nick, "Server error"); + return CONNECTED; + } + + Introduce_Client(Client, c, CLIENT_SERVICE); + return CONNECTED; } /* IRC_SERVICE */ @@ -782,8 +856,17 @@ cb_introduceClient(CLIENT *To, CLIENT *Prefix, void *data) Conn_WriteStr(conn, ":%s MODE %s +%s", Client_ID(c), Client_ID(c), modes); } else { - /* RFC 2813 mode: one combined NICK command */ - IRC_WriteStrClientPrefix(To, Prefix, + /* RFC 2813 mode: one combined NICK or SERVICE command */ + if (Client_Type(c) == CLIENT_SERVICE + && strchr(Client_Flags(To), 'S')) + IRC_WriteStrClientPrefix(To, Prefix, + "SERVICE %s %d * +%s %d :%s", + Client_Mask(c), + Client_MyToken(Client_Introducer(c)), + Client_Modes(c), Client_Hops(c) + 1, + Client_Info(c)); + else + IRC_WriteStrClientPrefix(To, Prefix, "NICK %s %d %s %s %d +%s :%s", Client_ID(c), Client_Hops(c) + 1, user, host, diff --git a/src/ngircd/irc.c b/src/ngircd/irc.c index 0bfb3eed..47f86528 100644 --- a/src/ngircd/irc.c +++ b/src/ngircd/irc.c @@ -400,7 +400,8 @@ Send_Message(CLIENT * Client, REQUEST * Req, int ForceType, bool SendErrors) } for (cl = Client_First(); cl != NULL; cl = Client_Next(cl)) { - if (Client_Type(cl) != CLIENT_USER) + if (Client_Type(cl) != CLIENT_USER && + Client_Type(cl) != CLIENT_SERVICE) continue; if (nick != NULL && host != NULL) { if (strcmp(nick, Client_ID(cl)) == 0 && diff --git a/src/ngircd/numeric.c b/src/ngircd/numeric.c index 927989db..ab647665 100644 --- a/src/ngircd/numeric.c +++ b/src/ngircd/numeric.c @@ -100,8 +100,17 @@ Announce_User(CLIENT * Client, CLIENT * User) } return CONNECTED; } else { - /* RFC 2813 mode: one combined NICK command */ - return IRC_WriteStrClient(Client, "NICK %s %d %s %s %d +%s :%s", + /* RFC 2813 mode: one combined NICK or SERVICE command */ + if (Client_Type(User) == CLIENT_SERVICE + && strchr(Client_Flags(Client), 'S')) + return IRC_WriteStrClient(Client, + "SERVICE %s %d * +%s %d :%s", Client_Mask(User), + Client_MyToken(Client_Introducer(User)), + Client_Modes(User), Client_Hops(User) + 1, + Client_Info(User)); + else + return IRC_WriteStrClient(Client, + "NICK %s %d %s %s %d +%s :%s", Client_ID(User), Client_Hops(User) + 1, Client_User(User), Client_Hostname(User), Client_MyToken(Client_Introducer(User)),