about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ngircd/channel.c29
-rw-r--r--src/ngircd/client.c38
-rw-r--r--src/ngircd/conf.c11
-rw-r--r--src/ngircd/conf.h2
-rw-r--r--src/ngircd/conn-ssl.c2
-rw-r--r--src/ngircd/conn.c84
-rw-r--r--src/ngircd/conn.h7
-rw-r--r--src/ngircd/defines.h4
-rw-r--r--src/ngircd/io.c14
-rw-r--r--src/ngircd/irc-cap.c4
-rw-r--r--src/ngircd/irc-info.c29
-rw-r--r--src/ngircd/irc-mode.c23
-rw-r--r--src/ngircd/messages.h5
-rw-r--r--src/ngircd/ngircd.c1
-rw-r--r--src/ngircd/sighandlers.c1
-rw-r--r--src/tool/tool.c16
16 files changed, 196 insertions, 74 deletions
diff --git a/src/ngircd/channel.c b/src/ngircd/channel.c
index ff470246..f0a9525d 100644
--- a/src/ngircd/channel.c
+++ b/src/ngircd/channel.c
@@ -66,16 +66,8 @@ static void Set_KeyFile PARAMS((CHANNEL *Chan, const char *KeyFile));
 GLOBAL void
 Channel_Init( void )
 {
-	CHANNEL *sc;
-
 	My_Channels = NULL;
 	My_Cl2Chan = NULL;
-
-	sc = Channel_Create("&SERVER");
-	if (sc) {
-		Channel_SetModes(sc, "mnPt");
-		Channel_SetTopic(sc, Client_ThisServer(), "Server Messages");
-	}
 } /* Channel_Init */
 
 
@@ -103,11 +95,12 @@ Channel_GetListInvites(CHANNEL *c)
 }
 
 
+/**
+ * Generate predefined persistent channels and &SERVER
+ */
 GLOBAL void
 Channel_InitPredefined( void )
 {
-	/* Generate predefined persistent channels */
-
 	CHANNEL *new_chan;
 	const struct Conf_Channel *conf_chan;
 	const char *c;
@@ -160,6 +153,18 @@ Channel_InitPredefined( void )
 	}
 	if (channel_count)
 		array_free(&Conf_Channels);
+
+	/* Make sure the local &SERVER channel exists */
+	if (!Channel_Search("&SERVER")) {
+		new_chan = Channel_Create("&SERVER");
+		if (new_chan) {
+			Channel_SetModes(new_chan, "mnPt");
+			Channel_SetTopic(new_chan, Client_ThisServer(),
+					 "Server Messages");
+		} else
+			Log(LOG_ERR, "Failed to create \"&SERVER\" channel!");
+	} else
+		LogDebug("Required channel \"&SERVER\" already exists, ok.");
 } /* Channel_InitPredefined */
 
 
@@ -832,6 +837,10 @@ Can_Send_To_Channel(CHANNEL *Chan, CLIENT *From)
 	if (strchr(Channel_Modes(Chan), 'n') && !is_member)
 		return false;
 
+	if (strchr(Channel_Modes(Chan), 'M') && !Client_HasMode(From, 'R')
+	    && !Client_HasMode(From, 'o'))
+		return false;
+
 	if (is_op || has_voice)
 		return true;
 
diff --git a/src/ngircd/client.c b/src/ngircd/client.c
index 0d2d4147..4728c7a4 100644
--- a/src/ngircd/client.c
+++ b/src/ngircd/client.c
@@ -687,18 +687,35 @@ Client_Hostname(CLIENT *Client)
 
 /**
  * Get potentially cloaked hostname of a client.
+ *
  * If the client has not enabled cloaking, the real hostname is used.
+ * Please note that this function uses a global static buffer, so you can't
+ * nest invocations without overwriting earlier results!
+ *
  * @param Client Pointer to client structure
  * @return Pointer to client hostname
  */
 GLOBAL char *
 Client_HostnameCloaked(CLIENT *Client)
 {
+	static char Cloak_Buffer[CLIENT_HOST_LEN];
+
 	assert(Client != NULL);
-	if (Client_HasMode(Client, 'x'))
-		return Client_ID(Client->introducer);
-	else
+
+	if (!Client_HasMode(Client, 'x'))
 		return Client_Hostname(Client);
+
+	/* Do simple mapping to the server ID? */
+	if (!*Conf_CloakHostModeX)
+		return Client_ID(Client->introducer);
+
+	strlcpy(Cloak_Buffer, Client->host, CLIENT_HOST_LEN);
+	strlcat(Cloak_Buffer, Conf_CloakHostSalt, CLIENT_HOST_LEN);
+
+	snprintf(Cloak_Buffer, CLIENT_HOST_LEN, Conf_CloakHostModeX,
+		 Hash(Cloak_Buffer));
+
+	return Cloak_Buffer;
 } /* Client_HostnameCloaked */
 
 
@@ -792,10 +809,12 @@ Client_Mask( CLIENT *Client )
 
 /**
  * Return ID of a client with cloaked hostname: "client!user@server-name"
+ *
  * This client ID is used for IRC prefixes, for example.
  * Please note that this function uses a global static buffer, so you can't
  * nest invocations without overwriting earlier results!
  * If the client has not enabled cloaking, the real hostname is used.
+ *
  * @param Client Pointer to client structure
  * @return Pointer to global buffer containing the client ID
  */
@@ -803,7 +822,6 @@ GLOBAL char *
 Client_MaskCloaked(CLIENT *Client)
 {
 	static char Mask_Buffer[GETID_LEN];
-	char Cloak_Buffer[GETID_LEN];
 
 	assert (Client != NULL);
 
@@ -811,16 +829,8 @@ Client_MaskCloaked(CLIENT *Client)
 	if (!Client_HasMode(Client, 'x'))
 		return Client_Mask(Client);
 
-	if(*Conf_CloakHostModeX) {
-		strlcpy(Cloak_Buffer, Client->host, GETID_LEN);
-		strlcat(Cloak_Buffer, Conf_CloakHostSalt, GETID_LEN);
-		snprintf(Cloak_Buffer, GETID_LEN, Conf_CloakHostModeX, Hash(Cloak_Buffer));
-	} else {
-		strncpy(Cloak_Buffer, Client_ID(Client->introducer), GETID_LEN);
-	}
-
-	snprintf(Mask_Buffer, GETID_LEN, "%s!%s@%s",
-		Client->id, Client->user, Cloak_Buffer);
+	snprintf(Mask_Buffer, GETID_LEN, "%s!%s@%s", Client->id, Client->user,
+		 Client_HostnameCloaked(Client));
 
 	return Mask_Buffer;
 } /* Client_MaskCloaked */
diff --git a/src/ngircd/conf.c b/src/ngircd/conf.c
index 3966dc90..627e6d3f 100644
--- a/src/ngircd/conf.c
+++ b/src/ngircd/conf.c
@@ -346,7 +346,7 @@ Conf_Test( void )
 
 	puts("[LIMITS]");
 	printf("  ConnectRetry = %d\n", Conf_ConnectRetry);
-	printf("  MaxConnections = %ld\n", Conf_MaxConnections);
+	printf("  MaxConnections = %d\n", Conf_MaxConnections);
 	printf("  MaxConnectionsIP = %d\n", Conf_MaxConnectionsIP);
 	printf("  MaxJoins = %d\n", Conf_MaxJoins > 0 ? Conf_MaxJoins : -1);
 	printf("  MaxNickLength = %u\n", Conf_MaxNickLength - 1);
@@ -1432,7 +1432,7 @@ Handle_LIMITS(int Line, char *Var, char *Arg)
 		return;
 	}
 	if (strcasecmp(Var, "MaxConnections") == 0) {
-		Conf_MaxConnections = atol(Arg);
+		Conf_MaxConnections = atoi(Arg);
 		if (!Conf_MaxConnections && strcmp(Arg, "0"))
 			Config_Error_NaN(Line, Var);
 		return;
@@ -1911,6 +1911,13 @@ Validate_Config(bool Configtest, bool Rehash)
 	bool config_valid = true;
 	char *ptr;
 
+	/* Emit a warning when the config file is not a full path name */
+	if (NGIRCd_ConfFile[0] && NGIRCd_ConfFile[0] != '/') {
+		Config_Error(LOG_WARNING,
+			"Not specifying a full path name to \"%s\" can cause problems when rehashing the server!",
+			NGIRCd_ConfFile);
+	}
+
 	/* Validate configured server name, see RFC 2812 section 2.3.1 */
 	ptr = Conf_ServerName;
 	do {
diff --git a/src/ngircd/conf.h b/src/ngircd/conf.h
index 8e66c07c..7a4e38aa 100644
--- a/src/ngircd/conf.h
+++ b/src/ngircd/conf.h
@@ -206,7 +206,7 @@ GLOBAL bool Conf_ConnectIPv6;
 GLOBAL bool Conf_ConnectIPv4;
 
 /** Maximum number of simultaneous connections to this server */
-GLOBAL long Conf_MaxConnections;
+GLOBAL int Conf_MaxConnections;
 
 /** Maximum number of channels a user can join */
 GLOBAL int Conf_MaxJoins;
diff --git a/src/ngircd/conn-ssl.c b/src/ngircd/conn-ssl.c
index 5d44b30f..8f7b70af 100644
--- a/src/ngircd/conn-ssl.c
+++ b/src/ngircd/conn-ssl.c
@@ -625,6 +625,8 @@ ConnectAccept( CONNECTION *c, bool connect)
 #endif /* _GNUTLS */
 	Conn_OPTION_DEL(c, (CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ|CONN_SSL_CONNECT));
 	ConnSSL_LogCertInfo(c);
+
+	Conn_StartLogin(CONNECTION2ID(c));
 	return 1;
 }
 
diff --git a/src/ngircd/conn.c b/src/ngircd/conn.c
index e57aa242..6091ebe2 100644
--- a/src/ngircd/conn.c
+++ b/src/ngircd/conn.c
@@ -88,7 +88,7 @@
 
 static bool Handle_Write PARAMS(( CONN_ID Idx ));
 static bool Conn_Write PARAMS(( CONN_ID Idx, char *Data, size_t Len ));
-static int New_Connection PARAMS(( int Sock ));
+static int New_Connection PARAMS(( int Sock, bool IsSSL ));
 static CONN_ID Socket2Index PARAMS(( int Sock ));
 static void Read_Request PARAMS(( CONN_ID Idx ));
 static unsigned int Handle_Buffer PARAMS(( CONN_ID Idx ));
@@ -134,7 +134,7 @@ static void
 cb_listen(int sock, short irrelevant)
 {
 	(void) irrelevant;
-	(void) New_Connection(sock);
+	(void) New_Connection(sock, false);
 }
 
 
@@ -152,7 +152,7 @@ cb_listen_ssl(int sock, short irrelevant)
 	int fd;
 
 	(void) irrelevant;
-	fd = New_Connection(sock);
+	fd = New_Connection(sock, true);
 	if (fd < 0)
 		return;
 	io_event_setcb(My_Connections[fd].sock, cb_clientserver_ssl);
@@ -1362,17 +1362,18 @@ Count_Connections(ng_ipaddr_t *a)
  * Initialize new client connection on a listening socket.
  *
  * @param Sock	Listening socket descriptor.
+ * @param IsSSL	true if this socket expects SSL-encrypted data.
  * @returns	Accepted socket descriptor or -1 on error.
  */
 static int
-New_Connection(int Sock)
+New_Connection(int Sock, UNUSED bool IsSSL)
 {
 #ifdef TCPWRAP
 	struct request_info req;
 #endif
 	ng_ipaddr_t new_addr;
 	char ip_str[NG_INET_ADDRSTRLEN];
-	int new_sock, new_sock_len, identsock;
+	int new_sock, new_sock_len;
 	CLIENT *c;
 	long cnt;
 
@@ -1492,30 +1493,56 @@ New_Connection(int Sock)
 	Log(LOG_INFO, "Accepted connection %d from %s:%d on socket %d.",
 	    new_sock, My_Connections[new_sock].host,
 	    ng_ipaddr_getport(&new_addr), Sock);
+	Account_Connection();
+
+#ifdef SSL_SUPPORT
+	/* Delay connection initalization until SSL handshake is finished */
+	if (!IsSSL)
+#endif
+		Conn_StartLogin(new_sock);
+
+	return new_sock;
+} /* New_Connection */
+
+
+/**
+ * Finish connection initialization, start resolver subprocess.
+ *
+ * @param Idx Connection index.
+ */
+GLOBAL void
+Conn_StartLogin(CONN_ID Idx)
+{
+	int ident_sock = -1;
+
+	assert(Idx >= 0);
+
+	/* Nothing to do if DNS (and resolver subprocess) is disabled */
+	if (!Conf_DNS)
+		return;
 
-	identsock = new_sock;
 #ifdef IDENTAUTH
-	if (!Conf_Ident)
-		identsock = -1;
+	/* Should we make an IDENT request? */
+	if (Conf_Ident)
+		ident_sock = My_Connections[Idx].sock;
 #endif
-	if (Conf_DNS) {
-		if (Conf_NoticeAuth) {
+
+	if (Conf_NoticeAuth) {
+		/* Send "NOTICE AUTH" messages to the client */
 #ifdef IDENTAUTH
-			if (Conf_Ident)
-				(void)Conn_WriteStr(new_sock,
-					"NOTICE AUTH :*** Looking up your hostname and checking ident");
-			else
+		if (Conf_Ident)
+			(void)Conn_WriteStr(Idx,
+				"NOTICE AUTH :*** Looking up your hostname and checking ident");
+		else
 #endif
-				(void)Conn_WriteStr(new_sock,
-					"NOTICE AUTH :*** Looking up your hostname");
-		}
-		Resolve_Addr(&My_Connections[new_sock].proc_stat, &new_addr,
-			     identsock, cb_Read_Resolver_Result);
+			(void)Conn_WriteStr(Idx,
+				"NOTICE AUTH :*** Looking up your hostname");
+		(void)Handle_Write(Idx);
 	}
 
-	Account_Connection();
-	return new_sock;
-} /* New_Connection */
+	Resolve_Addr(&My_Connections[Idx].proc_stat, &My_Connections[Idx].addr,
+		     ident_sock, cb_Read_Resolver_Result);
+}
 
 
 /**
@@ -2257,7 +2284,8 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
 		Client_SetHostname(c, readbuf);
 		if (Conf_NoticeAuth)
 			(void)Conn_WriteStr(i,
-					"NOTICE AUTH :*** Found your hostname");
+					"NOTICE AUTH :*** Found your hostname: %s",
+					My_Connections[i].host);
 #ifdef IDENTAUTH
 		++identptr;
 		if (*identptr) {
@@ -2282,8 +2310,10 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
 			}
 			if (Conf_NoticeAuth) {
 				(void)Conn_WriteStr(i,
-					"NOTICE AUTH :*** Got %sident response",
-					*ptr ? "invalid " : "");
+					"NOTICE AUTH :*** Got %sident response%s%s",
+					*ptr ? "invalid " : "",
+					*ptr ? "" : ": ",
+					*ptr ? "" : identptr);
 			}
 		} else {
 			Log(LOG_INFO, "IDENT lookup for connection %d: no result.", i);
@@ -2292,6 +2322,10 @@ cb_Read_Resolver_Result( int r_fd, UNUSED short events )
 					"NOTICE AUTH :*** No ident response");
 		}
 #endif
+
+		if (Conf_NoticeAuth)
+			(void)Handle_Write(i);
+
 		Class_HandleServerBans(c);
 	}
 #ifdef DEBUG
diff --git a/src/ngircd/conn.h b/src/ngircd/conn.h
index 7dcc8d9d..e42a2ae6 100644
--- a/src/ngircd/conn.h
+++ b/src/ngircd/conn.h
@@ -42,7 +42,7 @@
 #define CONN_SSL_WANT_READ	128	/* SSL/TLS library needs to read protocol data */
 #define CONN_SSL_FLAGS_ALL	(CONN_SSL_CONNECT|CONN_SSL|CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ)
 #endif
-typedef long CONN_ID;
+typedef int CONN_ID;
 
 #include "client.h"
 #include "proc.h"
@@ -101,6 +101,8 @@ GLOBAL CONNECTION *My_Connections;
 GLOBAL CONN_ID Pool_Size;
 GLOBAL long WCounter;
 
+#define CONNECTION2ID(x) (long)(x - My_Connections)
+
 #endif /* CONN_MODULE */
 
 
@@ -112,6 +114,8 @@ GLOBAL void Conn_CloseAllSockets PARAMS((int ExceptOf));
 GLOBAL unsigned int Conn_InitListeners PARAMS(( void ));
 GLOBAL void Conn_ExitListeners PARAMS(( void ));
 
+GLOBAL void Conn_StartLogin PARAMS((CONN_ID Idx));
+
 GLOBAL void Conn_Handler PARAMS(( void ));
 
 GLOBAL bool Conn_WriteStr PARAMS(( CONN_ID Idx, const char *Format, ... ));
@@ -126,6 +130,7 @@ GLOBAL void Conn_SyncServerStruct PARAMS(( void ));
 GLOBAL CONN_ID Conn_GetFromProc PARAMS((int fd));
 GLOBAL CLIENT* Conn_GetClient PARAMS((CONN_ID i));
 GLOBAL PROC_STAT* Conn_GetProcStat PARAMS((CONN_ID i));
+
 #ifdef SSL_SUPPORT
 GLOBAL bool Conn_GetCipherInfo PARAMS((CONN_ID Idx, char *buf, size_t len));
 GLOBAL bool Conn_UsesSSL PARAMS((CONN_ID Idx));
diff --git a/src/ngircd/defines.h b/src/ngircd/defines.h
index cd0a1666..82837599 100644
--- a/src/ngircd/defines.h
+++ b/src/ngircd/defines.h
@@ -161,10 +161,10 @@
 #endif
 
 /** Supported user modes. */
-#define USERMODES "acCiorRswx"
+#define USERMODES "aBcCiorRswx"
 
 /** Supported channel modes. */
-#define CHANMODES "beiIklmnoOPrRstvz"
+#define CHANMODES "beiIklmMnoOPrRstvz"
 
 /** Away message for users connected to linked servers. */
 #define DEFAULT_AWAY_MSG "Away"
diff --git a/src/ngircd/io.c b/src/ngircd/io.c
index 9ffdfd6b..cce6ef53 100644
--- a/src/ngircd/io.c
+++ b/src/ngircd/io.c
@@ -86,6 +86,20 @@ static int io_masterfd;
 
 static int io_dispatch_kqueue(struct timeval *tv);
 static bool io_event_change_kqueue(int, short, const int action);
+
+#ifndef EV_SET
+/* Taken from /usr/include/sys/event.h of FreeBSD 8.1 and required by all
+ * platforms that have kqueue but lack EV_SET() -- for example FreeBSD 4. */
+#define EV_SET(kevp, a, b, c, d, e, f) do {	\
+	struct kevent *__kevp__ = (kevp);	\
+	__kevp__->ident = (a);			\
+	__kevp__->filter = (b);			\
+	__kevp__->flags = (c);			\
+	__kevp__->fflags = (d);			\
+	__kevp__->data = (e);			\
+	__kevp__->udata = (f);			\
+} while(0)
+#endif
 #endif
 
 #ifdef IO_USE_POLL
diff --git a/src/ngircd/irc-cap.c b/src/ngircd/irc-cap.c
index 2ea4c9af..af34c38c 100644
--- a/src/ngircd/irc-cap.c
+++ b/src/ngircd/irc-cap.c
@@ -275,8 +275,8 @@ Parse_CAP(int Capabilities, char *Args)
  * @param Capabilities Capability flags (bitmask).
  * @return Pointer to textual representation.
  */
-char
-*Get_CAP_String(int Capabilities)
+char *
+Get_CAP_String(int Capabilities)
 {
 	static char txt[COMMAND_LEN];
 
diff --git a/src/ngircd/irc-info.c b/src/ngircd/irc-info.c
index 89d2deef..fc04773a 100644
--- a/src/ngircd/irc-info.c
+++ b/src/ngircd/irc-info.c
@@ -848,6 +848,8 @@ IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
 	assert( Client != NULL );
 	assert( Chan != NULL );
 
+	IRC_SetPenalty(Client, 1);
+
 	is_member = Channel_IsMemberOf(Chan, Client);
 
 	/* Secret channel? */
@@ -866,9 +868,6 @@ IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
 
 		is_visible = strchr(client_modes, 'i') == NULL;
 		if (is_member || is_visible) {
-			if (IRC_CheckListTooBig(Client, count, MAX_RPL_WHO, "WHO"))
-				break;
-
 			strcpy(flags, who_flags_status(client_modes));
 			if (is_ircop)
 				strlcat(flags, "*", sizeof(flags));
@@ -883,6 +882,11 @@ IRC_WHO_Channel(CLIENT *Client, CHANNEL *Chan, bool OnlyOps)
 			count++;
 		}
 	}
+
+	/* If there are a lot of clients, augment penalty a bit */
+	if (count > MAX_RPL_WHO)
+		IRC_SetPenalty(Client, 1);
+
 	return IRC_WriteStrClient(Client, RPL_ENDOFWHO_MSG, Client_ID(Client),
 				  Channel_Name(Chan));
 }
@@ -911,6 +915,7 @@ IRC_WHO_Mask(CLIENT *Client, char *Mask, bool OnlyOps)
 	if (Mask)
 		ngt_LowerStr(Mask);
 
+	IRC_SetPenalty(Client, 3);
 	for (c = Client_First(); c != NULL; c = Client_Next(c)) {
 		if (Client_Type(c) != CLIENT_USER)
 			continue;
@@ -1014,13 +1019,11 @@ IRC_WHO(CLIENT *Client, REQUEST *Req)
 		chan = Channel_Search(Req->argv[0]);
 		if (chan) {
 			/* Members of a channel have been requested */
-			IRC_SetPenalty(Client, 1);
 			return IRC_WHO_Channel(Client, chan, only_ops);
 		}
 		if (strcmp(Req->argv[0], "0") != 0) {
 			/* A mask has been given. But please note this RFC
 			 * stupidity: "0" is same as no arguments ... */
-			IRC_SetPenalty(Client, 3);
 			return IRC_WHO_Mask(Client, Req->argv[0], only_ops);
 		}
 	}
@@ -1111,6 +1114,12 @@ IRC_WHOIS_SendReply(CLIENT *Client, CLIENT *from, CLIENT *c)
 				Client_ID(from), Client_ID(c)))
 		return DISCONNECTED;
 
+	/* IRC-Bot? */
+	if (Client_HasMode(c, 'B') &&
+	    !IRC_WriteStrClient(from, RPL_WHOISBOT_MSG,
+				Client_ID(from), Client_ID(c)))
+		return DISCONNECTED;
+
 	/* Connected using SSL? */
 	if (Conn_UsesSSL(Client_Conn(c)) &&
 	    !IRC_WriteStrClient(from, RPL_WHOISSSL_MSG, Client_ID(from),
@@ -1477,7 +1486,15 @@ Show_MOTD_Sendline(CLIENT *Client, const char *msg)
 static bool
 Show_MOTD_End(CLIENT *Client)
 {
-	return IRC_WriteStrClient( Client, RPL_ENDOFMOTD_MSG, Client_ID( Client ));
+	if (!IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG, Client_ID(Client)))
+		return DISCONNECTED;
+
+	if (*Conf_CloakHost)
+		return IRC_WriteStrClient(Client, RPL_HOSTHIDDEN_MSG,
+					  Client_ID(Client),
+					  Client_Hostname(Client));
+
+	return CONNECTED;
 }
 
 #ifdef SSL_SUPPORT
diff --git a/src/ngircd/irc-mode.c b/src/ngircd/irc-mode.c
index fa35cdd0..71557201 100644
--- a/src/ngircd/irc-mode.c
+++ b/src/ngircd/irc-mode.c
@@ -138,6 +138,7 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 {
 	char the_modes[COMMAND_LEN], x[2], *mode_ptr;
 	bool ok, set;
+	bool send_RPL_HOSTHIDDEN_MSG = false;
 	int mode_arg;
 	size_t len;
 
@@ -229,6 +230,14 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 							ERR_NOPRIVILEGES_MSG,
 							Client_ID(Origin));
 			break;
+		case 'B': /* Bot */
+			if (Client_HasMode(Client, 'r'))
+				ok = IRC_WriteStrClient(Origin,
+							ERR_RESTRICTED_MSG,
+							Client_ID(Origin));
+			else
+				x[0] = 'B';
+			break;
 		case 'c': /* Receive connect notices
 			   * (only settable by IRC operators!) */
 			if (!set || Client_Type(Client) == CLIENT_SERVER
@@ -256,6 +265,14 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 							ERR_RESTRICTED_MSG,
 							Client_ID(Origin));
 			break;
+		case 'R': /* Registered (not [un]settable by clients) */
+			if (Client_Type(Client) == CLIENT_SERVER)
+				x[0] = 'R';
+			else
+				ok = IRC_WriteStrClient(Origin,
+							ERR_NICKREGISTER_MSG,
+							Client_ID(Origin));
+			break;
 		case 'x': /* Cloak hostname */
 			if (Client_HasMode(Client, 'r'))
 				ok = IRC_WriteStrClient(Origin,
@@ -263,6 +280,7 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 							Client_ID(Origin));
 			else
 				x[0] = 'x';
+				send_RPL_HOSTHIDDEN_MSG = true;
 			break;
 		default:
 			if (Client_Type(Client) != CLIENT_SERVER) {
@@ -332,6 +350,10 @@ Client_Mode( CLIENT *Client, REQUEST *Req, CLIENT *Origin, CLIENT *Target )
 						  "MODE %s :%s",
 						  Client_ID(Target),
 						  the_modes);
+			if (send_RPL_HOSTHIDDEN_MSG)
+				IRC_WriteStrClient(Client, RPL_HOSTHIDDEN_MSG,
+						   Client_ID(Client),
+						   Client_HostnameCloaked(Client));
 		}
 		LogDebug("%s \"%s\": Mode change, now \"%s\".",
 			 Client_TypeText(Target), Client_Mask(Target),
@@ -500,6 +522,7 @@ Channel_Mode(CLIENT *Client, REQUEST *Req, CLIENT *Origin, CHANNEL *Channel)
 		switch (*mode_ptr) {
 		/* --- Channel modes --- */
 		case 'i': /* Invite only */
+		case 'M': /* Only identified nicks can write */
 		case 'm': /* Moderated */
 		case 'n': /* Only members can write */
 		case 'R': /* Registered users only */
diff --git a/src/ngircd/messages.h b/src/ngircd/messages.h
index d2e04dbf..4f3a397b 100644
--- a/src/ngircd/messages.h
+++ b/src/ngircd/messages.h
@@ -21,7 +21,7 @@
 #define RPL_YOURHOST_MSG		"002 %s :Your host is %s, running version ngircd-%s (%s/%s/%s)"
 #define RPL_CREATED_MSG			"003 %s :This server has been started %s"
 #define RPL_MYINFO_MSG			"004 %s %s ngircd-%s %s %s"
-#define RPL_ISUPPORT1_MSG		"005 %s RFC2812 IRCD=ngIRCd CASEMAPPING=ascii PREFIX=(ov)@+ CHANTYPES=#&+ CHANMODES=beI,k,l,imnOPRstz CHANLIMIT=#&+:%d :are supported on this server"
+#define RPL_ISUPPORT1_MSG		"005 %s RFC2812 IRCD=ngIRCd CASEMAPPING=ascii PREFIX=(ov)@+ CHANTYPES=#&+ CHANMODES=beI,k,l,imMnOPRstz CHANLIMIT=#&+:%d :are supported on this server"
 #define RPL_ISUPPORT2_MSG		"005 %s CHANNELLEN=%d NICKLEN=%d TOPICLEN=%d AWAYLEN=%d KICKLEN=%d MODES=%d MAXLIST=beI:%d EXCEPTS=e INVEX=I PENALTY :are supported on this server"
 
 #define RPL_TRACELINK_MSG		"200 %s Link %s-%s %s %s V%s %ld %d %d"
@@ -72,6 +72,7 @@
 #define RPL_NOTOPIC_MSG			"331 %s %s :No topic is set"
 #define RPL_TOPIC_MSG			"332 %s %s :%s"
 #define RPL_TOPICSETBY_MSG		"333 %s %s %s %u"
+#define RPL_WHOISBOT_MSG		"335 %s %s :is a IRC Bot"
 #define RPL_INVITING_MSG		"341 %s %s %s%s"
 #define RPL_INVITELIST_MSG		"346 %s %s %s"
 #define RPL_ENDOFINVITELIST_MSG		"347 %s %s :End of channel invite list"
@@ -95,6 +96,7 @@
 #define RPL_YOUREOPER_MSG		"381 %s :You are now an IRC Operator"
 #define RPL_YOURESERVICE_MSG		"383 %s :You are service %s"
 #define RPL_TIME_MSG			"391 %s %s :%s"
+#define RPL_HOSTHIDDEN_MSG		"396 %s %s :is your displayed hostname now"
 
 #define ERR_NOSUCHNICK_MSG		"401 %s %s :No such nick or channel name"
 #define ERR_NOSUCHSERVER_MSG		"402 %s %s :No such server"
@@ -138,6 +140,7 @@
 #define ERR_CHANOPRIVSNEEDED_MSG	"482 %s %s :You are not channel operator"
 #define ERR_CANTKILLSERVER_MSG		"483 %s :You can't kill a server!"
 #define ERR_RESTRICTED_MSG		"484 %s :Your connection is restricted"
+#define ERR_NICKREGISTER_MSG		"484 %s :Cannot modify user mode (+R) -- Use IRC services"
 #define ERR_NOOPERHOST_MSG		"491 %s :Not configured for your host"
 #define ERR_NOTONSAMECHANNEL_MSG	"493 %s :You must share a common channel with %s"
 
diff --git a/src/ngircd/ngircd.c b/src/ngircd/ngircd.c
index 8a93bcb0..e24cefa8 100644
--- a/src/ngircd/ngircd.c
+++ b/src/ngircd/ngircd.c
@@ -330,6 +330,7 @@ main(int argc, const char *argv[])
 		Channel_Exit();
 		Class_Exit();
 		Log_Exit();
+		Signals_Exit();
 	}
 	Pidfile_Delete();
 
diff --git a/src/ngircd/sighandlers.c b/src/ngircd/sighandlers.c
index efb41bcd..a219105f 100644
--- a/src/ngircd/sighandlers.c
+++ b/src/ngircd/sighandlers.c
@@ -334,6 +334,7 @@ Signals_Exit(void)
 #endif
 	close(signalpipe[1]);
 	close(signalpipe[0]);
+	signalpipe[0] = signalpipe[1] = 0;
 }
 
 /* -eof- */
diff --git a/src/tool/tool.c b/src/tool/tool.c
index df109188..eb6c131e 100644
--- a/src/tool/tool.c
+++ b/src/tool/tool.c
@@ -135,24 +135,20 @@ ngt_TrimLastChr( char *String, const char Chr)
  * Fill a String with random chars
  */
 GLOBAL char *
-ngt_RandomStr( char *String, const size_t len)
+ngt_RandomStr(char *String, const size_t len)
 {
-	assert(String != NULL);
+	static const char chars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!\"#$&'()*+,-./:;<=>?@[\\]^_`";
+	struct timeval t;
+	size_t i;
 
-	static const char chars[] = 
-		"0123456789ABCDEFGHIJKLMNO"
-		"PQRSTUVWXYZabcdefghijklmn"
-		"opqrstuvwxyz!\"#$&'()*+,-"
-		"./:;<=>?@[\\]^_`";
+	assert(String != NULL);
 
-	struct timeval t;
 	gettimeofday(&t, NULL);
 	srand((unsigned)(t.tv_usec * t.tv_sec));
 
-	for (size_t i = 0; i < len; ++i) {
+	for (i = 0; i < len; ++i) {
 		String[i] = chars[rand() % (sizeof(chars) - 1)];
 	}
-
 	String[len] = '\0';
 
 	return String;