1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 
13 /*! \file */
14 
15 #include <config.h>
16 
17 #include <inttypes.h>
18 #include <stdbool.h>
19 
20 #include <isc/mem.h>
21 #include <isc/portset.h>
22 #include <isc/string.h>
23 #include <isc/types.h>
24 #include <isc/util.h>
25 
26 #define ISC_PORTSET_BUFSIZE (65536 / (sizeof(uint32_t) * 8))
27 
28 /*%
29  * Internal representation of portset.  It's an array of 32-bit integers, each
30  * bit corresponding to a single port in the ascending order.  For example,
31  * the second most significant bit of buf[0] corresponds to port 1.
32  */
33 struct isc_portset {
34 	unsigned int nports;	/*%< number of ports in the set */
35 	uint32_t buf[ISC_PORTSET_BUFSIZE];
36 };
37 
38 static inline bool
portset_isset(isc_portset_t * portset,in_port_t port)39 portset_isset(isc_portset_t *portset, in_port_t port) {
40 	return (portset->buf[port >> 5] & ((uint32_t)1 << (port & 31)));
41 }
42 
43 static inline void
portset_add(isc_portset_t * portset,in_port_t port)44 portset_add(isc_portset_t *portset, in_port_t port) {
45 	if (!portset_isset(portset, port)) {
46 		portset->nports++;
47 		portset->buf[port >> 5] |= ((uint32_t)1 << (port & 31));
48 	}
49 }
50 
51 static inline void
portset_remove(isc_portset_t * portset,in_port_t port)52 portset_remove(isc_portset_t *portset, in_port_t port) {
53 	if (portset_isset(portset, port)) {
54 		portset->nports--;
55 		portset->buf[port >> 5] &= ~((uint32_t)1 << (port & 31));
56 	}
57 }
58 
59 isc_result_t
isc_portset_create(isc_mem_t * mctx,isc_portset_t ** portsetp)60 isc_portset_create(isc_mem_t *mctx, isc_portset_t **portsetp) {
61 	isc_portset_t *portset;
62 
63 	REQUIRE(portsetp != NULL && *portsetp == NULL);
64 
65 	portset = isc_mem_get(mctx, sizeof(*portset));
66 	if (portset == NULL)
67 		return (ISC_R_NOMEMORY);
68 
69 	/* Make the set 'empty' by default */
70 	memset(portset, 0, sizeof(*portset));
71 	*portsetp = portset;
72 
73 	return (ISC_R_SUCCESS);
74 }
75 
76 void
isc_portset_destroy(isc_mem_t * mctx,isc_portset_t ** portsetp)77 isc_portset_destroy(isc_mem_t *mctx, isc_portset_t **portsetp) {
78 	isc_portset_t *portset;
79 
80 	REQUIRE(portsetp != NULL);
81 	portset = *portsetp;
82 
83 	isc_mem_put(mctx, portset, sizeof(*portset));
84 }
85 
86 bool
isc_portset_isset(isc_portset_t * portset,in_port_t port)87 isc_portset_isset(isc_portset_t *portset, in_port_t port) {
88 	REQUIRE(portset != NULL);
89 
90 	return (portset_isset(portset, port));
91 }
92 
93 unsigned int
isc_portset_nports(isc_portset_t * portset)94 isc_portset_nports(isc_portset_t *portset) {
95 	REQUIRE(portset != NULL);
96 
97 	return (portset->nports);
98 }
99 
100 void
isc_portset_add(isc_portset_t * portset,in_port_t port)101 isc_portset_add(isc_portset_t *portset, in_port_t port) {
102 	REQUIRE(portset != NULL);
103 
104 	portset_add(portset, port);
105 }
106 
107 void
isc_portset_remove(isc_portset_t * portset,in_port_t port)108 isc_portset_remove(isc_portset_t *portset, in_port_t port) {
109 	portset_remove(portset, port);
110 }
111 
112 void
isc_portset_addrange(isc_portset_t * portset,in_port_t port_lo,in_port_t port_hi)113 isc_portset_addrange(isc_portset_t *portset, in_port_t port_lo,
114 		     in_port_t port_hi)
115 {
116 	in_port_t p;
117 
118 	REQUIRE(portset != NULL);
119 	REQUIRE(port_lo <= port_hi);
120 
121 	p = port_lo;
122 	do {
123 		portset_add(portset, p);
124 	} while (p++ < port_hi);
125 }
126 
127 void
isc_portset_removerange(isc_portset_t * portset,in_port_t port_lo,in_port_t port_hi)128 isc_portset_removerange(isc_portset_t *portset, in_port_t port_lo,
129 			in_port_t port_hi)
130 {
131 	in_port_t p;
132 
133 	REQUIRE(portset != NULL);
134 	REQUIRE(port_lo <= port_hi);
135 
136 	p = port_lo;
137 	do {
138 		portset_remove(portset, p);
139 	} while (p++ < port_hi);
140 }
141