commit 178f9cbdac3bbeb58600268791916f3bfbcbd958 from: Alexander Barton date: Tue Sep 23 09:53:16 2008 UTC 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. commit - 6356418ae5fd66f94abda78f1ae67bbc7a59b0e4 commit + 178f9cbdac3bbeb58600268791916f3bfbcbd958 blob - cccf48b43e5f719d3a19146f995956e36fd6803e blob + 7abe641994ea49953acb3d1d690f1fa15d3a96f4 --- src/ngircd/defines.h +++ 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 blob - c189228a124617679a361cb516ea62608170f7b5 blob + 943612edf03f06ef7204890e27fa0fdbe1544670 --- src/ngircd/irc-login.c +++ 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 *d 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, blob - 0bfb3eed6a32e366a8d16d00a5a78348dc3ae647 blob + 47f86528004d24a009504076108ded7fc7b2f8c6 --- src/ngircd/irc.c +++ src/ngircd/irc.c @@ -400,7 +400,8 @@ Send_Message(CLIENT * Client, REQUEST * Req, int Force } 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 && blob - 927989db9923d99567703938a9800ec60b8803ab blob + ab6476654bc08c25a84f2e1789324bd0b633b03a --- src/ngircd/numeric.c +++ 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)),