libbsnmp: make binding of client UNIX socket optional and configurable

Before this change snmp_open(3) would always bind(2) client socket to a
random "/tmp/snmpXXXXXXXXXXXXXX" name.  However, this binding is not
required for SOCK_STREAM transport.  Also, any attempt to specify a
different name would fail, as open_client_local() would blindly rewrite
the name to the default.

Make this binding optional.  If application had initialized
snmp_client.local_path, then try to bind to the specified pathname,
otherwise perform the random name binding only if we are in the
SOCK_DGRAM mode.

While here change snmp_client.local_path size to SUNPATHLEN, so that any
legitimate local socket name can be used.  This requires library version
bump.

Note that this code has been broken by 81e0e7b9e3 for three years, thus
it is known not to be widely used.

Reviewed by:		harti
Differential Revision:	https://reviews.freebsd.org/D51070
This commit is contained in:
Gleb Smirnoff 2025-07-25 13:10:17 -07:00
parent b9fa99600a
commit 777123969d
7 changed files with 69 additions and 41 deletions

View file

@ -51,6 +51,9 @@
# xargs -n1 | sort | uniq -d;
# done
# 20250725: libbsnmp bumped to version 7
OLD_LIBS+=usr/lib/libbsnmp.so.6
# 20250725: Files which were briefly installed by WITH_MITKRB5 in 15.0.
OLD_FILES+=usr/include/kadm5/admin_internal.h
OLD_FILES+=usr/include/kadm5/admin_xdr.h

View file

@ -31,7 +31,7 @@
.\"
.\" $Begemot: bsnmp/lib/bsnmpclient.3,v 1.12 2005/10/04 08:46:50 brandt_h Exp $
.\"
.Dd March 31, 2020
.Dd June 24, 2025
.Dt BSNMPCLIENT 3
.Os
.Sh NAME
@ -155,7 +155,7 @@ struct snmp_client {
snmp_timeout_start_f timeout_start;
snmp_timeout_stop_f timeout_stop;
char local_path[sizeof(SNMP_LOCAL_PATH)];
char local_path[SUNPATHLEN];
};
.Ed
.Pp
@ -285,8 +285,19 @@ The function will be called with the return value of the corresponding
.Fn timeout_start
function.
.It Va local_path
If in local socket mode, the name of the clients socket.
Not needed by the application.
In local socket mode, optional path name the client socket shall be bound to
before connecting to the server.
For
.Dv SOCK_STREAM
local socket the named path is optional, and library will skip
.Xr bind 2
if path is not provided.
For
.Dv SOCK_DGRAM
local socket the named path is required, thus library will create a random
one in
.Pa /tmp
if path is not provided.
.El
.Pp
In the current implementation there is a global variable

View file

@ -977,7 +977,10 @@ remove_local(void)
static int
open_client_local(const char *path)
{
struct sockaddr_un sa;
struct sockaddr_un sa = {
.sun_family = AF_LOCAL,
.sun_len = sizeof(sa),
};
char *ptr;
int stype;
@ -1003,43 +1006,56 @@ open_client_local(const char *path)
return (-1);
}
snprintf(snmp_client.local_path, sizeof(snmp_client.local_path),
"%s", SNMP_LOCAL_PATH);
if (mktemp(snmp_client.local_path) == NULL) {
seterr(&snmp_client, "%s", strerror(errno));
(void)close(snmp_client.fd);
snmp_client.fd = -1;
return (-1);
/*
* A datagram socket requires a name to receive replies back. Would
* be cool to have an extension to unix(4) sockets similar to ip(4)
* IP_RECVDSTADDR/IP_SENDSRCADDR, so that a one-to-many datagram
* UNIX socket can send replies to its anonymous peers.
*/
if (snmp_client.trans == SNMP_TRANS_LOC_DGRAM &&
snmp_client.local_path[0] == '\0') {
(void)strlcpy(snmp_client.local_path, "/tmp/snmpXXXXXXXXXXXXXX",
sizeof(snmp_client.local_path));
if (mktemp(snmp_client.local_path) == NULL) {
seterr(&snmp_client, "mktemp(3): %s", strerror(errno));
goto fail;
}
}
sa.sun_family = AF_LOCAL;
sa.sun_len = sizeof(sa);
strcpy(sa.sun_path, snmp_client.local_path);
if (bind(snmp_client.fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
seterr(&snmp_client, "%s", strerror(errno));
(void)close(snmp_client.fd);
snmp_client.fd = -1;
(void)remove(snmp_client.local_path);
return (-1);
if (snmp_client.local_path[0] != '\0') {
if (strlcpy(sa.sun_path, snmp_client.local_path,
sizeof(sa.sun_path)) >=
sizeof(sa.sun_path)) {
seterr(&snmp_client, "%s",
"Local socket pathname too long");
goto fail;
}
if (bind(snmp_client.fd, (struct sockaddr *)&sa, sizeof(sa)) ==
-1) {
seterr(&snmp_client, "%s", strerror(errno));
goto fail;
}
atexit(remove_local);
}
atexit(remove_local);
sa.sun_family = AF_LOCAL;
sa.sun_len = offsetof(struct sockaddr_un, sun_path) +
strlen(snmp_client.chost);
strncpy(sa.sun_path, snmp_client.chost, sizeof(sa.sun_path) - 1);
sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
if (strlcpy(sa.sun_path, snmp_client.chost, sizeof(sa.sun_path)) >=
sizeof(sa.sun_path)) {
seterr(&snmp_client, "%s", "Server socket pathname too long");
goto fail;
}
if (connect(snmp_client.fd, (struct sockaddr *)&sa, sa.sun_len) == -1) {
seterr(&snmp_client, "%s", strerror(errno));
(void)close(snmp_client.fd);
snmp_client.fd = -1;
(void)remove(snmp_client.local_path);
return (-1);
goto fail;
}
return (0);
fail:
(void)close(snmp_client.fd);
snmp_client.fd = -1;
if (snmp_client.local_path[0] != '\0')
(void)remove(snmp_client.local_path);
return (-1);
}
/*

View file

@ -35,6 +35,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <stddef.h>
@ -42,8 +43,6 @@
#define SNMP_STRERROR_LEN 200
#define SNMP_DEFAULT_LOCAL "/var/run/snmpd.sock"
#define SNMP_LOCAL_PATH "/tmp/snmpXXXXXXXXXXXXXX"
/*
* transport methods
*/
@ -111,7 +110,7 @@ struct snmp_client {
snmp_timeout_start_f timeout_start;
snmp_timeout_stop_f timeout_stop;
char local_path[sizeof(SNMP_LOCAL_PATH)];
char local_path[SUNPATHLEN];
};
/* the global context */

View file

@ -7,7 +7,7 @@ CONTRIB= ${SRCTOP}/contrib/bsnmp/lib
.PATH: ${CONTRIB}
LIB= bsnmp
SHLIB_MAJOR= 6
SHLIB_MAJOR= 7
LD_FATAL_WARNINGS= no
CFLAGS+= -I${CONTRIB} -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY

View file

@ -461,7 +461,7 @@ OLD_FILES+=usr/include/bsnmp/snmpclient.h
OLD_FILES+=usr/include/bsnmp/snmpmod.h
OLD_FILES+=usr/lib/libbsnmp.a
OLD_FILES+=usr/lib/libbsnmp.so
OLD_LIBS+=usr/lib/libbsnmp.so.6
OLD_LIBS+=usr/lib/libbsnmp.so.7
OLD_FILES+=usr/lib/libbsnmp_p.a
OLD_FILES+=usr/lib/libbsnmptools.a
OLD_FILES+=usr/lib/libbsnmptools.so

View file

@ -881,12 +881,11 @@ parse_local_path(char *opt_arg)
{
assert(opt_arg != NULL);
if (sizeof(opt_arg) > sizeof(SNMP_LOCAL_PATH)) {
if (strlcpy(snmp_client.local_path, opt_arg,
sizeof(snmp_client.local_path)) >= sizeof(snmp_client.local_path)) {
warnx("Filename too long - %s", opt_arg);
return (-1);
}
strlcpy(snmp_client.local_path, opt_arg, sizeof(SNMP_LOCAL_PATH));
return (2);
}