mirror of
https://github.com/opnsense/src.git
synced 2026-06-11 01:30:30 -04:00
sockstat: fix port parsing after libxo integration
parse_ports has been broken ever since7b35b4d, and it's a sufficiently complicated function that it really deserves some unit tests. Fix it, and add tests. Refactor the code a little bit to facilitate unit tests. Chiefly, split the tested functions out of main.c into sockstat.c . PR: 288731 Fixes:7b35b4d("sockstat: add libxo support") Sponsored by: ConnectWise PR: https://github.com/freebsd/freebsd-src/pull/1807 Reviewed by: rido
This commit is contained in:
parent
ec92e61f99
commit
d888317796
7 changed files with 328 additions and 54 deletions
|
|
@ -1205,6 +1205,8 @@
|
|||
..
|
||||
seq
|
||||
..
|
||||
sockstat
|
||||
..
|
||||
soelim
|
||||
..
|
||||
sort
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
.include <src.opts.mk>
|
||||
|
||||
PROG= sockstat
|
||||
SRCS= main.c
|
||||
SRCS= main.c sockstat.c
|
||||
|
||||
LIBADD= jail xo
|
||||
|
||||
|
|
@ -14,4 +14,7 @@ LIBADD+= cap_sysctl
|
|||
CFLAGS+= -DWITH_CASPER
|
||||
.endif
|
||||
|
||||
HAS_TESTS=
|
||||
SUBDIR.${MK_TESTS}+= tests
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@
|
|||
#include <arpa/inet.h>
|
||||
|
||||
#include <capsicum_helpers.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <jail.h>
|
||||
|
|
@ -74,6 +73,8 @@
|
|||
#include <casper/cap_pwd.h>
|
||||
#include <casper/cap_sysctl.h>
|
||||
|
||||
#include "sockstat.h"
|
||||
|
||||
#define SOCKSTAT_XO_VERSION "1"
|
||||
#define sstosin(ss) ((struct sockaddr_in *)(ss))
|
||||
#define sstosin6(ss) ((struct sockaddr_in6 *)(ss))
|
||||
|
|
@ -110,12 +111,6 @@ static size_t default_numprotos = nitems(default_protos);
|
|||
static int *protos; /* protocols to use */
|
||||
static size_t numprotos; /* allocated size of protos[] */
|
||||
|
||||
static int *ports;
|
||||
|
||||
#define INT_BIT (sizeof(int)*CHAR_BIT)
|
||||
#define SET_PORT(p) do { ports[p / INT_BIT] |= 1 << (p % INT_BIT); } while (0)
|
||||
#define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT)))
|
||||
|
||||
struct addr {
|
||||
union {
|
||||
struct sockaddr_storage address;
|
||||
|
|
@ -276,50 +271,6 @@ parse_protos(char *protospec)
|
|||
return (proto_index);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_ports(const char *portspec)
|
||||
{
|
||||
const char *p, *q;
|
||||
int port, end;
|
||||
|
||||
if (ports == NULL)
|
||||
if ((ports = calloc(65536 / INT_BIT, sizeof(int))) == NULL)
|
||||
xo_err(1, "calloc()");
|
||||
p = portspec;
|
||||
while (*p != '\0') {
|
||||
if (!isdigit(*p))
|
||||
xo_errx(1, "syntax error in port range");
|
||||
for (q = p; *q != '\0' && isdigit(*q); ++q)
|
||||
/* nothing */ ;
|
||||
for (port = 0; p < q; ++p)
|
||||
port = port * 10 + digittoint(*p);
|
||||
if (port < 0 || port > 65535)
|
||||
xo_errx(1, "invalid port number");
|
||||
SET_PORT(port);
|
||||
switch (*p) {
|
||||
case '-':
|
||||
++p;
|
||||
break;
|
||||
case ',':
|
||||
++p;
|
||||
/* fall through */
|
||||
case '\0':
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
for (q = p; *q != '\0' && isdigit(*q); ++q)
|
||||
/* nothing */ ;
|
||||
for (end = 0; p < q; ++p)
|
||||
end = end * 10 + digittoint(*p);
|
||||
if (end < port || end > 65535)
|
||||
xo_errx(1, "invalid port number");
|
||||
while (port++ < end)
|
||||
SET_PORT(port);
|
||||
if (*p == ',')
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sockaddr(struct sockaddr_storage *ss, int af, void *addr, int port)
|
||||
{
|
||||
|
|
@ -1767,7 +1718,7 @@ main(int argc, char *argv[])
|
|||
const char *pwdcmds[] = { "setpassent", "getpwuid" };
|
||||
const char *pwdfields[] = { "pw_name" };
|
||||
int protos_defined = -1;
|
||||
int o, i;
|
||||
int o, i, err;
|
||||
|
||||
argc = xo_parse_args(argc, argv);
|
||||
if (argc < 0)
|
||||
|
|
@ -1817,7 +1768,15 @@ main(int argc, char *argv[])
|
|||
opt_n = true;
|
||||
break;
|
||||
case 'p':
|
||||
parse_ports(optarg);
|
||||
err = parse_ports(optarg);
|
||||
switch (err) {
|
||||
case EINVAL:
|
||||
xo_errx(1, "syntax error in port range");
|
||||
break;
|
||||
case ERANGE:
|
||||
xo_errx(1, "invalid port number");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'P':
|
||||
protos_defined = parse_protos(optarg);
|
||||
|
|
|
|||
77
usr.bin/sockstat/sockstat.c
Normal file
77
usr.bin/sockstat/sockstat.c
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2025 ConnectWise
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <libxo/xo.h>
|
||||
|
||||
#include "sockstat.h"
|
||||
|
||||
int *ports;
|
||||
|
||||
int
|
||||
parse_ports(const char *portspec)
|
||||
{
|
||||
const char *p;
|
||||
|
||||
if (ports == NULL)
|
||||
if ((ports = calloc(65536 / INT_BIT, sizeof(int))) == NULL)
|
||||
xo_err(1, "calloc()");
|
||||
p = portspec;
|
||||
while (*p != '\0') {
|
||||
long port, end;
|
||||
char *endptr = NULL;
|
||||
|
||||
errno = 0;
|
||||
port = strtol(p, &endptr, 10);
|
||||
if (errno)
|
||||
return (errno);
|
||||
if (port < 0 || port > 65535)
|
||||
return (ERANGE);
|
||||
SET_PORT(port);
|
||||
switch (*endptr) {
|
||||
case '-':
|
||||
p = endptr + 1;
|
||||
end = strtol(p, &endptr, 10);
|
||||
break;
|
||||
case ',':
|
||||
p = endptr + 1;
|
||||
continue;
|
||||
default:
|
||||
p = endptr;
|
||||
continue;
|
||||
}
|
||||
if (errno)
|
||||
return (errno);
|
||||
if (end < port || end > 65535)
|
||||
return (ERANGE);
|
||||
while (port++ < end)
|
||||
SET_PORT(port);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
35
usr.bin/sockstat/sockstat.h
Normal file
35
usr.bin/sockstat/sockstat.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2025 ConnectWise
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define INT_BIT (sizeof(int)*CHAR_BIT)
|
||||
#define SET_PORT(p) do { ports[p / INT_BIT] |= 1 << (p % INT_BIT); } while (0)
|
||||
#define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT)))
|
||||
|
||||
extern int *ports;
|
||||
|
||||
int parse_ports(const char *portspec);
|
||||
8
usr.bin/sockstat/tests/Makefile
Normal file
8
usr.bin/sockstat/tests/Makefile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
ATF_TESTS_C+= sockstat_test
|
||||
SRCS.sockstat_test= sockstat_test.c ../sockstat.c
|
||||
|
||||
LIBADD= xo
|
||||
|
||||
PACKAGE= tests
|
||||
|
||||
.include <bsd.test.mk>
|
||||
190
usr.bin/sockstat/tests/sockstat_test.c
Normal file
190
usr.bin/sockstat/tests/sockstat_test.c
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2025 ConnectWise
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/errno.h>
|
||||
|
||||
#include <atf-c.h>
|
||||
|
||||
#include "../sockstat.h"
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(backwards_range);
|
||||
ATF_TC_BODY(backwards_range, tc)
|
||||
{
|
||||
const char portspec[] = "22-21";
|
||||
|
||||
ATF_CHECK_EQ(ERANGE, parse_ports(portspec));
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(erange_low);
|
||||
ATF_TC_BODY(erange_low, tc)
|
||||
{
|
||||
const char portspec[] = "-1";
|
||||
|
||||
ATF_CHECK_EQ(ERANGE, parse_ports(portspec));
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(erange_high);
|
||||
ATF_TC_BODY(erange_high, tc)
|
||||
{
|
||||
const char portspec[] = "65536";
|
||||
|
||||
ATF_CHECK_EQ(ERANGE, parse_ports(portspec));
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(erange_on_range_end);
|
||||
ATF_TC_BODY(erange_on_range_end, tc)
|
||||
{
|
||||
const char portspec[] = "22-65536";
|
||||
|
||||
ATF_CHECK_EQ(ERANGE, parse_ports(portspec));
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(multiple);
|
||||
ATF_TC_BODY(multiple, tc)
|
||||
{
|
||||
const char portspec[] = "80,443";
|
||||
|
||||
ATF_REQUIRE_EQ(0, parse_ports(portspec));
|
||||
|
||||
ATF_CHECK(!CHK_PORT(0));
|
||||
|
||||
ATF_CHECK(!CHK_PORT(79));
|
||||
ATF_CHECK(CHK_PORT(80));
|
||||
ATF_CHECK(!CHK_PORT(81));
|
||||
|
||||
ATF_CHECK(!CHK_PORT(442));
|
||||
ATF_CHECK(CHK_PORT(443));
|
||||
ATF_CHECK(!CHK_PORT(444));
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(multiple_plus_ranges);
|
||||
ATF_TC_BODY(multiple_plus_ranges, tc)
|
||||
{
|
||||
const char portspec[] = "80,443,500-501,510,520,40000-40002";
|
||||
|
||||
ATF_REQUIRE_EQ(0, parse_ports(portspec));
|
||||
|
||||
ATF_CHECK(!CHK_PORT(0));
|
||||
|
||||
ATF_CHECK(!CHK_PORT(79));
|
||||
ATF_CHECK(CHK_PORT(80));
|
||||
ATF_CHECK(!CHK_PORT(81));
|
||||
|
||||
ATF_CHECK(!CHK_PORT(442));
|
||||
ATF_CHECK(CHK_PORT(443));
|
||||
ATF_CHECK(!CHK_PORT(444));
|
||||
|
||||
ATF_CHECK(!CHK_PORT(499));
|
||||
ATF_CHECK(CHK_PORT(500));
|
||||
ATF_CHECK(CHK_PORT(501));
|
||||
ATF_CHECK(!CHK_PORT(502));
|
||||
|
||||
ATF_CHECK(!CHK_PORT(519));
|
||||
ATF_CHECK(CHK_PORT(520));
|
||||
ATF_CHECK(!CHK_PORT(521));
|
||||
|
||||
ATF_CHECK(!CHK_PORT(39999));
|
||||
ATF_CHECK(CHK_PORT(40000));
|
||||
ATF_CHECK(CHK_PORT(40001));
|
||||
ATF_CHECK(CHK_PORT(40002));
|
||||
ATF_CHECK(!CHK_PORT(40003));
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(nonnumeric);
|
||||
ATF_TC_BODY(nonnumeric, tc)
|
||||
{
|
||||
const char portspec[] = "foo";
|
||||
|
||||
ATF_CHECK_EQ(EINVAL, parse_ports(portspec));
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(null_range);
|
||||
ATF_TC_BODY(null_range, tc)
|
||||
{
|
||||
const char portspec[] = "22-22";
|
||||
|
||||
ATF_REQUIRE_EQ(0, parse_ports(portspec));
|
||||
|
||||
ATF_CHECK(!CHK_PORT(0));
|
||||
ATF_CHECK(CHK_PORT(22));
|
||||
ATF_CHECK(!CHK_PORT(23));
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(range);
|
||||
ATF_TC_BODY(range, tc)
|
||||
{
|
||||
const char portspec[] = "22-25";
|
||||
|
||||
ATF_REQUIRE_EQ(0, parse_ports(portspec));
|
||||
|
||||
ATF_CHECK(!CHK_PORT(0));
|
||||
ATF_CHECK(CHK_PORT(22));
|
||||
ATF_CHECK(CHK_PORT(23));
|
||||
ATF_CHECK(CHK_PORT(24));
|
||||
ATF_CHECK(CHK_PORT(25));
|
||||
ATF_CHECK(!CHK_PORT(26));
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(single);
|
||||
ATF_TC_BODY(single, tc)
|
||||
{
|
||||
const char portspec[] = "22";
|
||||
|
||||
ATF_REQUIRE_EQ(0, parse_ports(portspec));
|
||||
|
||||
ATF_CHECK(!CHK_PORT(0));
|
||||
ATF_CHECK(CHK_PORT(22));
|
||||
}
|
||||
|
||||
ATF_TC_WITHOUT_HEAD(zero);
|
||||
ATF_TC_BODY(zero, tc)
|
||||
{
|
||||
const char portspec[] = "0";
|
||||
|
||||
ATF_REQUIRE_EQ(0, parse_ports(portspec));
|
||||
|
||||
ATF_CHECK(CHK_PORT(0));
|
||||
}
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
ATF_TP_ADD_TC(tp, backwards_range);
|
||||
ATF_TP_ADD_TC(tp, erange_low);
|
||||
ATF_TP_ADD_TC(tp, erange_high);
|
||||
ATF_TP_ADD_TC(tp, erange_on_range_end);
|
||||
ATF_TP_ADD_TC(tp, multiple);
|
||||
ATF_TP_ADD_TC(tp, multiple_plus_ranges);
|
||||
ATF_TP_ADD_TC(tp, nonnumeric);
|
||||
ATF_TP_ADD_TC(tp, null_range);
|
||||
ATF_TP_ADD_TC(tp, range);
|
||||
ATF_TP_ADD_TC(tp, single);
|
||||
ATF_TP_ADD_TC(tp, zero);
|
||||
|
||||
return (atf_no_error());
|
||||
}
|
||||
Loading…
Reference in a new issue