The victim's connection closes without any explanation. Output may be
lost. This is because kill_cmd() kills by calling io_shutdown(),
which shuts down the socket and drains the I/O queues.
How this makes the victim's thread terminate is a bit subtle: shutting
down the socket makes it ready. If the victim's thread is waiting for
I/O, it wakes up. Since all further reads return EOF, and all further
writes fail, the command terminates quickly (short of inifinite loop
bugs), then the command loop, and finally the thread.
To make kill behave more nicely, change kill_cmd() to work exactly
like server shutdown: send a flash message to the victim, set his EOF
indicator, abort the command, forbid sleeping on I/O, wake up the
victim's thread. Just as reliable, but doesn't lose output.
If the victim's client fails to close his connection, the victim's
thread may still linger in state PS_SHUTDOWN for up to
login_grace_time (default 120s). An attacker could try to use that to
make the server run out of file descriptors or memory, but simply
connecting achieves the same effect more cheaply.
return RET_FAIL;
}
logerror("%s killed country #%d", praddr(player), player->cnum);
- io_shutdown(other->iop, IO_READ | IO_WRITE);
- pr_id(player, C_EXIT, "closed socket of offending job\n");
+ pr_flash(other, "Disconnected by %s\n", praddr(player));
+ io_set_eof(other->iop);
+ other->aborted = 1;
+ other->may_sleep = PLAYER_SLEEP_NEVER;
+ empth_wakeup(other->proc);
+ pr_id(player, C_EXIT, "terminated %s's connection\n", praddr(other));
return RET_OK;
}