Make rate limiting work per-listening-socket. Log better messages than
before for this, requiring a new function (get_ipaddr()). canohost.c receives a $FreeBSD$ line. Suggested by: Niels Provos <niels@OpenBSD.org>
This commit is contained in:
parent
b535a9bf12
commit
7e03cf33e9
@ -11,6 +11,7 @@
|
||||
*
|
||||
* Functions for returning the canonical host name of the remote site.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
@ -201,7 +202,7 @@ get_remote_ipaddr()
|
||||
/* Get the IP address in ascii. */
|
||||
if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop),
|
||||
NULL, 0, NI_NUMERICHOST) != 0)
|
||||
fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed");
|
||||
fatal("get_remote_ipaddr: getnameinfo NI_NUMERICHOST failed");
|
||||
|
||||
canonical_host_ip = xstrdup(ntop);
|
||||
|
||||
@ -209,6 +210,35 @@ get_remote_ipaddr()
|
||||
return canonical_host_ip;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the IP-address of the local host as a string. The returned
|
||||
* string must be freed.
|
||||
*/
|
||||
|
||||
const char *
|
||||
get_ipaddr(int socket)
|
||||
{
|
||||
static char *canonical_host_ip = NULL;
|
||||
struct sockaddr_storage from;
|
||||
socklen_t fromlen;
|
||||
char ntop[NI_MAXHOST];
|
||||
|
||||
/* Get IP address of server. */
|
||||
fromlen = sizeof(from);
|
||||
memset(&from, 0, sizeof(from));
|
||||
if (getsockname(socket, (struct sockaddr *)&from, &fromlen) < 0) {
|
||||
debug("getsockname failed: %.100s", strerror(errno));
|
||||
fatal_cleanup();
|
||||
}
|
||||
/* Get the IP address in ascii. */
|
||||
if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop),
|
||||
NULL, 0, NI_NUMERICHOST) != 0)
|
||||
fatal("get_local_ipaddr: getnameinfo NI_NUMERICHOST failed");
|
||||
|
||||
/* Return ip address string. */
|
||||
return xstrdup(ntop);
|
||||
}
|
||||
|
||||
/* Returns the local/remote port for the socket. */
|
||||
|
||||
int
|
||||
|
@ -363,6 +363,11 @@ char *get_remote_hostname(int socket);
|
||||
*/
|
||||
const char *get_canonical_hostname(void);
|
||||
|
||||
/*
|
||||
* Returns the local IP address as an ascii string.
|
||||
*/
|
||||
const char *get_ipaddr(int socket);
|
||||
|
||||
/*
|
||||
* Returns the remote IP address as an ascii string. The value need not be
|
||||
* freed by the caller.
|
||||
|
@ -143,17 +143,17 @@ unsigned char *session_id2 = NULL;
|
||||
int session_id2_len = 0;
|
||||
|
||||
/* These are used to implement connections_per_period. */
|
||||
struct magic_connection {
|
||||
struct ratelim_connection {
|
||||
struct timeval connections_begin;
|
||||
unsigned int connections_this_period;
|
||||
} *magic_connections;
|
||||
/* Magic number, too! TODO: this doesn't have to be static. */
|
||||
const size_t MAGIC_CONNECTIONS_SIZE = 1;
|
||||
} *ratelim_connections;
|
||||
|
||||
static __inline int
|
||||
magic_hash(struct sockaddr *sa) {
|
||||
|
||||
return 0;
|
||||
static void
|
||||
ratelim_init(void) {
|
||||
ratelim_connections = calloc(num_listen_socks,
|
||||
sizeof(struct ratelim_connection));
|
||||
if (ratelim_connections == NULL)
|
||||
fatal("calloc: %s", strerror(errno));
|
||||
}
|
||||
|
||||
static __inline struct timeval
|
||||
@ -440,6 +440,7 @@ main(int ac, char **av)
|
||||
int opt, sock_in = 0, sock_out = 0, newsock, i, fdsetsz, on = 1;
|
||||
pid_t pid;
|
||||
socklen_t fromlen;
|
||||
int ratelim_exceeded = 0;
|
||||
int silent = 0;
|
||||
fd_set *fdset;
|
||||
struct sockaddr_storage from;
|
||||
@ -450,7 +451,6 @@ main(int ac, char **av)
|
||||
struct addrinfo *ai;
|
||||
char ntop[NI_MAXHOST], strport[NI_MAXSERV];
|
||||
int listen_sock, maxfd;
|
||||
int connections_per_period_exceeded = 0;
|
||||
|
||||
/* Save argv[0]. */
|
||||
saved_argv = av;
|
||||
@ -786,11 +786,7 @@ main(int ac, char **av)
|
||||
fdsetsz = howmany(maxfd, NFDBITS) * sizeof(fd_mask);
|
||||
fdset = (fd_set *)xmalloc(fdsetsz);
|
||||
|
||||
/* Initialize the magic_connections table. It's magical! */
|
||||
magic_connections = calloc(MAGIC_CONNECTIONS_SIZE,
|
||||
sizeof(struct magic_connection));
|
||||
if (magic_connections == NULL)
|
||||
fatal("calloc: %s", strerror(errno));
|
||||
ratelim_init();
|
||||
|
||||
/*
|
||||
* Stay listening for connections until the system crashes or
|
||||
@ -825,22 +821,23 @@ main(int ac, char **av)
|
||||
}
|
||||
if (options.connections_per_period != 0) {
|
||||
struct timeval diff, connections_end;
|
||||
struct magic_connection *mc;
|
||||
struct ratelim_connection *rc;
|
||||
|
||||
(void)gettimeofday(&connections_end, NULL);
|
||||
mc = &magic_connections[magic_hash((struct sockaddr *)0)];
|
||||
diff = timevaldiff(&mc->connections_begin, &connections_end);
|
||||
rc = &ratelim_connections[i];
|
||||
diff = timevaldiff(&rc->connections_begin,
|
||||
&connections_end);
|
||||
if (diff.tv_sec >= options.connections_period) {
|
||||
/*
|
||||
* Slide the window forward only after completely
|
||||
* leaving it.
|
||||
* Slide the window forward only after
|
||||
* completely leaving it.
|
||||
*/
|
||||
mc->connections_begin = connections_end;
|
||||
mc->connections_this_period = 1;
|
||||
rc->connections_begin = connections_end;
|
||||
rc->connections_this_period = 1;
|
||||
} else {
|
||||
if (++mc->connections_this_period >
|
||||
if (++rc->connections_this_period >
|
||||
options.connections_per_period)
|
||||
connections_per_period_exceeded = 1;
|
||||
ratelim_exceeded = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -861,12 +858,18 @@ main(int ac, char **av)
|
||||
sock_out = newsock;
|
||||
pid = getpid();
|
||||
break;
|
||||
} else if (connections_per_period_exceeded) {
|
||||
log("Connection rate limit of %u/%us has been exceeded; "
|
||||
"dropping connection from %s.",
|
||||
options.connections_per_period, options.connections_period,
|
||||
ntop);
|
||||
connections_per_period_exceeded = 0;
|
||||
} else if (ratelim_exceeded) {
|
||||
const char *myaddr;
|
||||
|
||||
myaddr = get_ipaddr(newsock);
|
||||
log("rate limit (%u/%u) on %s port %d "
|
||||
"exceeded by %s",
|
||||
options.connections_per_period,
|
||||
options.connections_period, myaddr,
|
||||
get_sock_port(newsock, 1), ntop);
|
||||
free((void *)myaddr);
|
||||
ratelim_exceeded = 0;
|
||||
continue;
|
||||
} else {
|
||||
/*
|
||||
* Normal production daemon. Fork, and have
|
||||
|
Loading…
Reference in New Issue
Block a user