Skip to content

Commit 6d24a2b

Browse files
committed
net_irc: Fix format of server PONG responses.
The server PONG response should include the hostname of the replying server. This was previously omitted, causing clients like Ambassador not to show any calculated lag time. The hostname is now present and clients should now display the lag correctly.
1 parent 93e4f79 commit 6d24a2b

File tree

2 files changed

+41
-9
lines changed

2 files changed

+41
-9
lines changed

nets/net_irc.c

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <signal.h>
2727
#include <fcntl.h>
2828
#include <unistd.h>
29+
#include <sys/time.h> /* use gettimeofday */
2930
#include <limits.h> /* use PATH_MAX */
3031

3132
#include "include/module.h"
@@ -146,6 +147,8 @@ struct irc_user {
146147
time_t lastactive; /* Time of last JOIN, PART, PRIVMSG, NOTICE, etc. */
147148
time_t lastping; /* Last ping sent */
148149
time_t lastpong; /* Last pong received */
150+
struct timeval lastping_us; /* Lag timer start (when we sent a PING) */
151+
int lag; /* Lag, in ms (how long it took to get a PONG) */
149152
bbs_mutex_t lock; /* User lock */
150153
char *awaymsg; /* Away message */
151154
unsigned int away:1; /* User is currently away (default is 0, i.e. user is here) */
@@ -3823,6 +3826,7 @@ static void handle_client(struct irc_user *user)
38233826

38243827
for (;;) {
38253828
char *s = buf;
3829+
38263830
/* XXX For some reason, using \r\n as the delimiter for bbs_readline breaks Ambassador.
38273831
* Doesn't seem like a bug in bbs_readline, though it is suspicious since
38283832
* the RFCs are very clear that CR LF is the delimiter.
@@ -3963,11 +3967,17 @@ static void handle_client(struct irc_user *user)
39633967
} else { /* Post-CAP/SASL */
39643968
char *current, *command = strsep(&s, " ");
39653969
if (!strcasecmp(command, "PONG")) {
3970+
struct timeval now_us, diff_us;
3971+
gettimeofday(&now_us, NULL);
39663972
bbs_mutex_lock(&user->lock);
3973+
timersub(&now_us, &user->lastping_us, &diff_us);
3974+
user->lag = (int) ((1000 * diff_us.tv_sec) + (diff_us.tv_usec / 1000));
39673975
user->lastpong = time(NULL);
39683976
bbs_mutex_unlock(&user->lock);
39693977
} else if (!strcasecmp(command, "PING")) { /* Usually servers ping clients, but clients can ping servers too */
3970-
send_reply(user, "PONG %s\r\n", S_IF(s)); /* Don't add another : because it's still in s, if present. */
3978+
/* The server PONG response slightly differs from the client PONG response.
3979+
* We include the hostname of the pinged server, before echoing back whatever was received. */
3980+
send_reply(user, "PONG %s %s\r\n", irc_hostname, S_IF(s)); /* Don't add another : because it's still in s, if present. */
39713981
} else if (!strcasecmp(command, "PASS")) {
39723982
REQUIRE_PARAMETER(user, s);
39733983
free_if(user->password);
@@ -4306,6 +4316,16 @@ static int ping_alertpipe[2] = { -1, -1 };
43064316
* TL;DR This IRC server uses N+1 threads, where N is the number of clients connected.
43074317
*/
43084318

4319+
/*! \todo RFC 1459 4.6.2 says a PING is sent... if no other activity detected from a connection.
4320+
* This means we don't actually need this separate thread to ping clients at all, handle_client can send PINGs if the poll timeout expires,
4321+
* with additional logic to disconnect if nothing further is heard afterwards. */
4322+
static void ping_client(struct irc_user *user, time_t now)
4323+
{
4324+
irc_other_thread_writef(user->node, "PING :%" TIME_T_FMT "\r\n", now);
4325+
user->lastping = now;
4326+
gettimeofday(&user->lastping_us, NULL);
4327+
}
4328+
43094329
/*! \brief Thread to periodically ping all clients and dump any that don't respond with a pong back in time */
43104330
static void *ping_thread(void *unused)
43114331
{
@@ -4340,8 +4360,7 @@ static void *ping_thread(void *unused)
43404360
bbs_debug(5, "Shutting down client on node %d\n", user->node->id);
43414361
bbs_socket_shutdown(user->node->fd); /* Make the client handler thread break */
43424362
} else {
4343-
irc_other_thread_writef(user->node, "PING :%" TIME_T_FMT "\r\n", now);
4344-
user->lastping = now;
4363+
ping_client(user, now);
43454364
clients++;
43464365
}
43474366
}
@@ -4370,11 +4389,13 @@ static int cli_irc_users(struct bbs_cli_args *a)
43704389
int i = 0;
43714390
time_t now = time(NULL);
43724391

4373-
bbs_dprintf(a->fdout, "%3s %2s %4s %-20s %4s %-15s %-20s %s\n", "#", "Op", "Node", "User", "Ping", "Modes", "Nick", "Hostmask");
4392+
bbs_dprintf(a->fdout, "%3s %2s %4s %-20s %4s %4s %-15s %-20s %s\n", "#", "Op", "Node", "User", "Ping", "Lag", "Modes", "Nick", "Hostmask");
43744393
RWLIST_RDLOCK(&users);
43754394
RWLIST_TRAVERSE(&users, user, entry) {
43764395
char hostmask[128];
43774396
char node_id[15];
4397+
char pingbuf[20];
4398+
char lagbuf[20]; /* min size possible */
43784399
time_t ping = user->lastpong ? now - user->lastpong : -1;
43794400
++i;
43804401
get_user_modes(modes, sizeof(modes), user);
@@ -4384,8 +4405,19 @@ static int cli_irc_users(struct bbs_cli_args *a)
43844405
strcpy(node_id, "-");
43854406
}
43864407
snprintf(hostmask, sizeof(hostmask), HOSTMASK_FMT, HOSTMASK_ARGS(user));
4387-
bbs_dprintf(a->fdout, "%3d %2s %4s %-20s %4" TIME_T_FMT " %-15s %-20s %s\n",
4388-
i, user->modes & USER_MODE_OPERATOR ? "*" : "", node_id, user->username, ping, modes, user->nickname, hostmask);
4408+
if (user->lastpong) {
4409+
int lag_sec = user->lag / 1000;
4410+
snprintf(lagbuf, sizeof(lagbuf), "%01d.%02d", lag_sec, (user->lag - (lag_sec * 1000)) / 10);
4411+
} else {
4412+
strcpy(lagbuf, "-");
4413+
}
4414+
if (ping != -1) {
4415+
snprintf(pingbuf, sizeof(pingbuf), "%" TIME_T_FMT, ping);
4416+
} else {
4417+
strcpy(pingbuf, "-");
4418+
}
4419+
bbs_dprintf(a->fdout, "%3d %2s %4s %-20s %4s %4s %-15s %-20s %s\n",
4420+
i, user->modes & USER_MODE_OPERATOR ? "*" : "", node_id, user->username, pingbuf, lagbuf, modes, user->nickname, hostmask);
43894421
}
43904422
RWLIST_UNLOCK(&users);
43914423
bbs_dprintf(a->fdout, "%d user%s online\n", i, ESS(i));

tests/test_irc.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,10 @@ static int run(void)
129129
SWRITE(client1, "PRIVMSG +private :Hello!\r\n");
130130
SWRITE(client2, "PRIVMSG +private :Hello!\r\n");
131131
SWRITE(client1, "PING :hello\r\n"); /* Something arbitrary to solicit a known response */
132-
CLIENT_EXPECT(client1, "PONG :hello\r\n"); /* This should be the first thing we read, the message client2 sent should not show up! */
132+
CLIENT_EXPECT(client1, "PONG " TEST_HOSTNAME " :hello\r\n"); /* This should be the first thing we read, the message client2 sent should not show up! */
133133
SWRITE(client1, "PRIVMSG +private :Hey!\r\n");
134134
SWRITE(client2, "PING :hello\r\n");
135-
CLIENT_EXPECT(client2, "PONG :hello\r\n"); /* Likewise */
135+
CLIENT_EXPECT(client2, "PONG " TEST_HOSTNAME " :hello\r\n"); /* Likewise */
136136

137137
/* These tests below are not very robust... they basically just ensure that something happens and the server doesn't crash. */
138138
SWRITE(client1, "LIST\r\n"); /* Get channel list */
@@ -144,7 +144,7 @@ static int run(void)
144144
* since we wouldn't get a ping while the tests are running anyways.
145145
* However, we can certainly test sending a PING from *OUR* side, as we should get a PONG reply in return. */
146146
SWRITE(client1, "PING :hello\r\n");
147-
CLIENT_EXPECT(client1, "PONG :hello");
147+
CLIENT_EXPECT(client1, "PONG " TEST_HOSTNAME " :hello");
148148

149149
/* Client 2 joins channel so there's a common channel to see the quit message following */
150150
SWRITE(client2, "JOIN #test1\r\n");

0 commit comments

Comments
 (0)