Add code to detect overflow in WHOX.

This should currently be impossible, but may be
possible with extremely long server/nick names and
additional fields.
In case of overflow, the string sent to to the user is
truncated and if it was the first overflow since the
module was loaded a notice is sent to snomask +d.
This commit is contained in:
Jilles Tjoelker 2008-12-20 17:17:27 +01:00
parent 83235e9ed5
commit bac250f689

View file

@ -423,6 +423,31 @@ do_who_on_channel(struct Client *source_p, struct Channel *chptr,
} }
} }
/*
* append_format
*
* inputs - pointer to buffer
* - size of buffer
* - pointer to position
* - format string
* - arguments for format
* output - NONE
* side effects - position incremented, possibly beyond size of buffer
* this allows detecting overflow
*/
static void
append_format(char *buf, size_t bufsize, size_t *pos, const char *fmt, ...)
{
size_t max, result;
va_list ap;
max = *pos >= bufsize ? 0 : bufsize - *pos;
va_start(ap, fmt);
result = rb_vsnprintf(buf + *pos, bufsize - *pos, fmt, ap);
va_end(ap);
*pos += result;
}
/* /*
* do_who * do_who
* *
@ -438,7 +463,8 @@ static void
do_who(struct Client *source_p, struct Client *target_p, struct membership *msptr, struct who_format *fmt) do_who(struct Client *source_p, struct Client *target_p, struct membership *msptr, struct who_format *fmt)
{ {
char status[16]; char status[16];
char str[512], *p, *end; char str[510 + 1]; /* linebuf.c will add \r\n */
size_t pos;
const char *q; const char *q;
rb_sprintf(status, "%c%s%s", rb_sprintf(status, "%c%s%s",
@ -454,33 +480,34 @@ do_who(struct Client *source_p, struct Client *target_p, struct membership *mspt
else else
{ {
str[0] = '\0'; str[0] = '\0';
p = str; pos = 0;
end = str + sizeof str; append_format(str, sizeof str, &pos, ":%s %d %s",
me.name, RPL_WHOSPCRPL, source_p->name);
if (fmt->fields & FIELD_QUERYTYPE) if (fmt->fields & FIELD_QUERYTYPE)
p += rb_snprintf(p, end - p, " %s", fmt->querytype); append_format(str, sizeof str, &pos, " %s", fmt->querytype);
if (fmt->fields & FIELD_CHANNEL) if (fmt->fields & FIELD_CHANNEL)
p += rb_snprintf(p, end - p, " %s", msptr ? msptr->chptr->chname : "*"); append_format(str, sizeof str, &pos, " %s", msptr ? msptr->chptr->chname : "*");
if (fmt->fields & FIELD_USER) if (fmt->fields & FIELD_USER)
p += rb_snprintf(p, end - p, " %s", target_p->username); append_format(str, sizeof str, &pos, " %s", target_p->username);
if (fmt->fields & FIELD_IP) if (fmt->fields & FIELD_IP)
{ {
if (show_ip(source_p, target_p) && !EmptyString(target_p->sockhost) && strcmp(target_p->sockhost, "0")) if (show_ip(source_p, target_p) && !EmptyString(target_p->sockhost) && strcmp(target_p->sockhost, "0"))
p += rb_snprintf(p, end - p, " %s", target_p->sockhost); append_format(str, sizeof str, &pos, " %s", target_p->sockhost);
else else
p += rb_snprintf(p, end - p, " %s", "255.255.255.255"); append_format(str, sizeof str, &pos, " %s", "255.255.255.255");
} }
if (fmt->fields & FIELD_HOST) if (fmt->fields & FIELD_HOST)
p += rb_snprintf(p, end - p, " %s", target_p->host); append_format(str, sizeof str, &pos, " %s", target_p->host);
if (fmt->fields & FIELD_SERVER) if (fmt->fields & FIELD_SERVER)
p += rb_snprintf(p, end - p, " %s", target_p->servptr->name); append_format(str, sizeof str, &pos, " %s", target_p->servptr->name);
if (fmt->fields & FIELD_NICK) if (fmt->fields & FIELD_NICK)
p += rb_snprintf(p, end - p, " %s", target_p->name); append_format(str, sizeof str, &pos, " %s", target_p->name);
if (fmt->fields & FIELD_FLAGS) if (fmt->fields & FIELD_FLAGS)
p += rb_snprintf(p, end - p, " %s", status); append_format(str, sizeof str, &pos, " %s", status);
if (fmt->fields & FIELD_HOP) if (fmt->fields & FIELD_HOP)
p += rb_snprintf(p, end - p, " %d", ConfigServerHide.flatten_links ? 0 : target_p->hopcount); append_format(str, sizeof str, &pos, " %d", ConfigServerHide.flatten_links ? 0 : target_p->hopcount);
if (fmt->fields & FIELD_IDLE) if (fmt->fields & FIELD_IDLE)
p += rb_snprintf(p, end - p, " %d", (int)(MyClient(target_p) ? rb_current_time() - target_p->localClient->last : 0)); append_format(str, sizeof str, &pos, " %d", (int)(MyClient(target_p) ? rb_current_time() - target_p->localClient->last : 0));
if (fmt->fields & FIELD_ACCOUNT) if (fmt->fields & FIELD_ACCOUNT)
{ {
/* display as in whois */ /* display as in whois */
@ -494,12 +521,22 @@ do_who(struct Client *source_p, struct Client *target_p, struct membership *mspt
} }
else else
q = "0"; q = "0";
p += rb_snprintf(p, end - p, " %s", q); append_format(str, sizeof str, &pos, " %s", q);
} }
if (fmt->fields & FIELD_OPLEVEL) if (fmt->fields & FIELD_OPLEVEL)
p += rb_snprintf(p, end - p, " %s", is_chanop(msptr) ? "999" : "n/a"); append_format(str, sizeof str, &pos, " %s", is_chanop(msptr) ? "999" : "n/a");
if (fmt->fields & FIELD_INFO) if (fmt->fields & FIELD_INFO)
p += rb_snprintf(p, end - p, " :%s", target_p->info); append_format(str, sizeof str, &pos, " :%s", target_p->info);
sendto_one_numeric(source_p, RPL_WHOSPCRPL, "%s", str + 1);
if (pos >= sizeof str)
{
static int warned = 0;
if (!warned)
sendto_realops_snomask(SNO_DEBUG, L_NETWIDE,
"WHOX overflow while sending information about %s to %s",
target_p->name, source_p->name);
warned = 1;
}
sendto_one(source_p, "%s", str);
} }
} }