static bool Hello_User PARAMS(( CLIENT *Client ));
static void Kill_Nick PARAMS(( char *Nick, char *Reason ));
-static void Introduce_Client PARAMS((CLIENT *To, CLIENT *Client));
+static void Introduce_Client PARAMS((CLIENT *To, CLIENT *Client, int Type));
static void cb_introduceClient PARAMS((CLIENT *Client, CLIENT *Prefix,
void *i));
Client_Type(target) != CLIENT_SERVICE &&
Client_Type(target) != CLIENT_SERVER) {
/* New client */
- Log( LOG_DEBUG, "Connection %d: got valid NICK command ...",
+ LogDebug("Connection %d: got valid NICK command ...",
Client_Conn( Client ));
/* Register new nickname of this client */
info = Req->argv[0];
}
- /* Nick ueberpruefen */
c = Client_Search(nick);
if(c) {
- /* Der neue Nick ist auf diesem Server bereits registriert:
- * sowohl der neue, als auch der alte Client muessen nun
- * disconnectiert werden. */
+ /*
+ * the new nick is already present on this server:
+ * the new and the old one have to be disconnected now.
+ */
Log( LOG_ERR, "Server %s introduces already registered nick \"%s\"!", Client_ID( Client ), Req->argv[0] );
Kill_Nick( Req->argv[0], "Nick collision" );
return CONNECTED;
}
- /* Server, zu dem der Client connectiert ist, suchen */
+ /* Find the Server this client is connected to */
intr_c = Client_GetFromToken(Client, token);
if( ! intr_c )
{
return CONNECTED;
}
- /* Neue Client-Struktur anlegen */
c = Client_NewRemoteUser(intr_c, nick, hops, user, hostname,
token, modes, info, true);
if( ! c )
{
- /* Eine neue Client-Struktur konnte nicht angelegt werden.
- * Der Client muss disconnectiert werden, damit der Netz-
- * status konsistent bleibt. */
+ /* out of memory, need to disconnect client to keep network state consistent */
Log( LOG_ALERT, "Can't create client structure! (on connection %d)", Client_Conn( Client ));
Kill_Nick( Req->argv[0], "Server error" );
return CONNECTED;
Client_Mask(c));
Client_SetType(c, CLIENT_GOTNICK);
} else
- Introduce_Client(Client, c);
+ Introduce_Client(Client, c, CLIENT_USER);
return CONNECTED;
}
/* RFC 1459 style user registration?
* Introduce client to network: */
if (Client_Type(c) == CLIENT_GOTNICK)
- Introduce_Client(Client, c);
+ Introduce_Client(Client, c, CLIENT_USER);
return CONNECTED;
} else if (Client_Type(Client) == CLIENT_USER) {
/**
- * 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));
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 <type> "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 */
target = Client_Search( Req->prefix );
if( ! target )
{
- /* Den Client kennen wir nicht (mehr), also nichts zu tun. */
Log( LOG_WARNING, "Got QUIT from %s for unknown client!?", Client_ID( Client ));
return CONNECTED;
}
strlcat(quitmsg, "\"", sizeof quitmsg );
}
- /* User, Service, oder noch nicht registriert */
+ /* User, Service, or not yet registered */
Conn_Close( Client_Conn( Client ), "Got QUIT command.", Req->argc == 1 ? quitmsg : NULL, true);
return DISCONNECTED;
assert(Client != NULL);
assert(Req != NULL);
- /* Wrong number of arguments? */
if (Req->argc < 1)
return IRC_WriteStrClient(Client, ERR_NOORIGIN_MSG,
Client_ID(Client));
/* The connection timestamp has already been updated when the data has
* been read from so socket, so we don't need to update it here. */
-
+#ifdef DEBUG
if (Client_Conn(Client) > NONE)
Log(LOG_DEBUG,
"Connection %d: received PONG. Lag: %ld seconds.",
else
Log(LOG_DEBUG,
"Connection %d: received PONG.", Client_Conn(Client));
-
+#endif
return CONNECTED;
} /* IRC_PONG */
if (strcmp(Client_Password(Client), Conf_ServerPwd) != 0) {
/* Bad password! */
Log(LOG_ERR,
- "User \"%s\" rejected (connection %d): Bad password!",
+ "Client \"%s\" rejected (connection %d): Bad password!",
Client_Mask(Client), Client_Conn(Client));
Conn_Close(Client_Conn(Client), NULL, "Bad password", true);
return DISCONNECTED;
}
- Introduce_Client(NULL, Client);
+ Introduce_Client(NULL, Client, CLIENT_USER);
if (!IRC_WriteStrClient
(Client, RPL_WELCOME_MSG, Client_ID(Client), Client_Mask(Client)))
static void
-Introduce_Client(CLIENT *From, CLIENT *Client)
+Introduce_Client(CLIENT *From, CLIENT *Client, int Type)
{
- char *type;
-
- Client_SetType(Client, CLIENT_USER);
+ /* Set client type (user or service) */
+ Client_SetType(Client, Type);
if (From) {
- if (Conf_IsService(Conf_GetServer(Client_Conn(From)), Client_ID(Client))) {
- type = "Service";
- } else
- type = "User";
+ if (Conf_IsService(Conf_GetServer(Client_Conn(From)),
+ Client_ID(Client)))
+ Client_SetType(Client, CLIENT_SERVICE);
LogDebug("%s \"%s\" (+%s) registered (via %s, on %s, %d hop%s).",
- type, Client_Mask(Client), Client_Modes(Client),
- Client_ID(From), Client_ID(Client_Introducer(Client)),
+ Client_TypeText(Client), Client_Mask(Client),
+ Client_Modes(Client), Client_ID(From),
+ Client_ID(Client_Introducer(Client)),
Client_Hops(Client), Client_Hops(Client) > 1 ? "s": "");
} else
- Log(LOG_NOTICE, "User \"%s\" registered (connection %d).",
- Client_Mask(Client), Client_Conn(Client));
+ Log(LOG_NOTICE, "%s \"%s\" registered (connection %d).",
+ Client_TypeText(Client), Client_Mask(Client),
+ Client_Conn(Client));
/* Inform other servers */
IRC_WriteStrServersPrefixFlag_CB(From,
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,