Skip to content

Commit

Permalink
socket.c: Throttle node connections per IP address.
Browse files Browse the repository at this point in the history
Previously, the only limit on node connections was
the maximum node limit, and a single host could
easily reach this by opening that many connections.
Now, a separate limit restricts the number of connections
from any particular IP address, ensuring that a single
host can't easily monopolize all of the nodes.

LBBS-60 #close
  • Loading branch information
InterLinked1 committed Sep 20, 2024
1 parent 4454e1d commit e2960f5
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 1 deletion.
37 changes: 36 additions & 1 deletion bbs/node.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ static RWLIST_HEAD_STATIC(nodes, bbs_node);
#define DEFAULT_GUEST_ASK_INFO 1

static unsigned int maxnodes;
static unsigned int maxnodes_perip;
static unsigned int minuptimedisplayed = 0;
static int allow_guest = DEFAULT_ALLOW_GUEST;
static int guest_ask_info = DEFAULT_GUEST_ASK_INFO; /* 0 = don't ask, 1 = ask if not using TDD, 2 = always ask */
Expand All @@ -83,6 +84,7 @@ static int load_config(void)

/* Set some basic defaults, whether there's a config or not */
maxnodes = DEFAULT_MAX_NODES;
maxnodes_perip = DEFAULT_MAX_NODES / 2;
allow_guest = DEFAULT_ALLOW_GUEST;
guest_ask_info = DEFAULT_GUEST_ASK_INFO;
defaultbps = 0;
Expand All @@ -108,6 +110,7 @@ static int load_config(void)
bbs_config_val_set_uint(cfg, "bbs", "minuptimedisplayed", &minuptimedisplayed);
bbs_config_val_set_str(cfg, "bbs", "exitmsg", bbs_exitmsg, sizeof(bbs_exitmsg));
bbs_config_val_set_uint(cfg, "nodes", "maxnodes", &maxnodes);
bbs_config_val_set_uint(cfg, "nodes", "maxnodesperip", &maxnodes_perip);
bbs_config_val_set_uint(cfg, "nodes", "defaultbps", &defaultbps);
bbs_config_val_set_uint(cfg, "nodes", "defaultrows", &default_rows);
bbs_config_val_set_uint(cfg, "nodes", "defaultcols", &default_cols);
Expand Down Expand Up @@ -171,6 +174,31 @@ unsigned int bbs_node_mod_count(void *mod)
return count;
}

unsigned int bbs_node_ip_count(struct sockaddr_in *sinaddr)
{
struct bbs_node *node;
unsigned int count = 0;

/* XXX This function is implemented this way so that if/when we store
* the sockaddr_in for IP addresses rather than char*,
* we don't have to make any API changes. For the moment,
* yes, this is less efficient since we do more conversions. */
char addrstr[64];
if (bbs_get_remote_ip(sinaddr, addrstr, sizeof(addrstr))) {
return 0;
}

RWLIST_RDLOCK(&nodes);
RWLIST_TRAVERSE(&nodes, node, entry) {
if (!strcmp(addrstr, node->ip)) {
count++;
}
}
RWLIST_UNLOCK(&nodes);

return count;
}

unsigned int bbs_max_nodenum(void)
{
struct bbs_node *node;
Expand Down Expand Up @@ -200,6 +228,11 @@ unsigned int bbs_maxnodes(void)
return maxnodes;
}

unsigned int bbs_maxnodes_per_ip(void)
{
return maxnodes_perip;
}

const char *bbs_hostname(void)
{
return bbs_hostname_buf;
Expand Down Expand Up @@ -585,9 +618,11 @@ static void node_shutdown(struct bbs_node *node, int unique)
node->spy = 0;
bbs_node_pty_unlock(node);
}
} else {
bbs_debug(8, "Node %u has no PTY thread to clean up\n", node->id);
}

if (node->fd) {
if (node->fd != -1) {
bbs_socket_close(&node->fd);
}

Expand Down
8 changes: 8 additions & 0 deletions bbs/socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,14 @@ static void *tcp_multilistener(void *unused)
}

bbs_soft_assert(l->name != NULL);

/* Throttle connections from this IP address if needed */
if (bbs_node_ip_count(&sinaddr) >= bbs_maxnodes_per_ip()) {
bbs_notice("Rejecting new %s connection from %s (exceeds maximum connections from this IP address)\n", l->name, new_ip);
close(sfd);
continue;
}

bbs_debug(1, "Accepting new %s connection from %s\n", l->name, new_ip);

/* Note that l->name is const memory allocated as part of l.
Expand Down
1 change: 1 addition & 0 deletions configs/nodes.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ exitmsg=${COLOR_GREEN}Thank you for visiting today, please call again soon!${CRL

[nodes]
maxnodes=64 ; Maximum number of nodes. Once capacity is reached, new connections will be denied. Default is 64.
;maxnodes_perip=32 ; Maximum number of nodes allowed to connect from each IP address. Default is maxnodes / 2.
;defaultbps=300 ; Throttle node output to a specified speed on connection (bps = bits per second).
; Useful if you want to emulate a lower speed system, but at the cost of less efficient CPU utilization.
; Default is 0 (unthrottled), and input is never throttled.
Expand Down
13 changes: 13 additions & 0 deletions include/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ unsigned int bbs_node_count(void);
*/
unsigned int bbs_node_mod_count(void *mod);

/*!
* \brief Get number of allocated nodes connected from a certain IP address
* \param sinaddr
* \retval Number of current nodes connected from specified IP address
*/
unsigned int bbs_node_ip_count(struct sockaddr_in *sinaddr);

/*!
* \brief Get the highest-numbered node's number
* \retval 0 if no nodes, positive node number otherwise
Expand All @@ -160,6 +167,12 @@ unsigned int bbs_idle_ms(void);
*/
unsigned int bbs_maxnodes(void);

/*!
* \brief Get the maximum number of nodes allowed to connect per IP address
* \retval non-negative max nodes allowed
*/
unsigned int bbs_maxnodes_per_ip(void);

/*! \brief Get configured BBS hostname */
const char *bbs_hostname(void);

Expand Down

0 comments on commit e2960f5

Please sign in to comment.