1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Anthony Minessale II <anthm@freeswitch.org>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Anthony Minessale II <anthm@freeswitch.org>
27  * Michael Jerris <mike@jerris.com>
28  * Paul D. Tinsley <pdt at jackhammer.org>
29  *
30  *
31  * switch_core_port_allocator.c -- Main Core Library (port allocator)
32  *
33  */
34 
35 #include <switch.h>
36 #include "private/switch_core_pvt.h"
37 
38 struct switch_core_port_allocator {
39 	char *ip;
40 	switch_port_t start;
41 	switch_port_t end;
42 	switch_port_t next;
43 	int8_t *track;
44 	uint32_t track_len;
45 	uint32_t track_used;
46 	switch_port_flag_t flags;
47 	switch_mutex_t *mutex;
48 	switch_memory_pool_t *pool;
49 };
50 
switch_core_port_allocator_new(const char * ip,switch_port_t start,switch_port_t end,switch_port_flag_t flags,switch_core_port_allocator_t ** new_allocator)51 SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(const char *ip, switch_port_t start,
52 															   switch_port_t end, switch_port_flag_t flags, switch_core_port_allocator_t **new_allocator)
53 {
54 	switch_status_t status;
55 	switch_memory_pool_t *pool;
56 	switch_core_port_allocator_t *alloc;
57 	int even, odd;
58 
59 	if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) {
60 		return status;
61 	}
62 
63 	if (!(alloc = switch_core_alloc(pool, sizeof(*alloc)))) {
64 		switch_core_destroy_memory_pool(&pool);
65 		return SWITCH_STATUS_MEMERR;
66 	}
67 
68 	alloc->flags = flags;
69 	alloc->ip = switch_core_strdup(pool, ip);
70 	even = switch_test_flag(alloc, SPF_EVEN);
71 	odd = switch_test_flag(alloc, SPF_ODD);
72 
73 	alloc->flags |= runtime.port_alloc_flags;
74 
75 
76 	if (!(even && odd)) {
77 		if (even) {
78 			if ((start % 2) != 0) {
79 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Rounding odd start port %d to %d\n", start, start + 1);
80 				start++;
81 			}
82 			if ((end % 2) != 0) {
83 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Rounding odd end port %d to %d\n", end, end - 1);
84 				end--;
85 			}
86 		} else if (odd) {
87 			if ((start % 2) == 0) {
88 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Rounding even start port %d to %d\n", start, start + 1);
89 				start++;
90 			}
91 			if ((end % 2) == 0) {
92 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Rounding even end port %d to %d\n", end, end - 1);
93 				end--;
94 			}
95 		}
96 	}
97 
98 	alloc->track_len = (end - start) + 2;
99 
100 	if (!(even && odd)) {
101 		alloc->track_len /= 2;
102 	}
103 
104 	alloc->track = switch_core_alloc(pool, (alloc->track_len + 2) * sizeof(switch_byte_t));
105 
106 	alloc->start = start;
107 	alloc->next = start;
108 	alloc->end = end;
109 
110 
111 	switch_mutex_init(&alloc->mutex, SWITCH_MUTEX_NESTED, pool);
112 	alloc->pool = pool;
113 	*new_allocator = alloc;
114 
115 	return SWITCH_STATUS_SUCCESS;
116 }
117 
test_port(switch_core_port_allocator_t * alloc,int family,int type,switch_port_t port)118 static switch_bool_t test_port(switch_core_port_allocator_t *alloc, int family, int type, switch_port_t port)
119 {
120 	switch_memory_pool_t *pool = NULL;
121 	switch_sockaddr_t *local_addr = NULL;
122 	switch_socket_t *sock = NULL;
123 	switch_bool_t r = SWITCH_FALSE;
124 
125 	if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
126 		return SWITCH_FALSE;
127 	}
128 
129 	if (switch_sockaddr_info_get(&local_addr, alloc->ip, SWITCH_UNSPEC, port, 0, pool) == SWITCH_STATUS_SUCCESS) {
130 		if (switch_socket_create(&sock, family, type, 0, pool) == SWITCH_STATUS_SUCCESS) {
131 			if (switch_socket_bind(sock, local_addr) == SWITCH_STATUS_SUCCESS) {
132 				r = SWITCH_TRUE;
133 			}
134 			switch_socket_close(sock);
135 		}
136 	}
137 
138 	switch_core_destroy_memory_pool(&pool);
139 
140 	return r;
141 }
142 
switch_core_port_allocator_request_port(switch_core_port_allocator_t * alloc,switch_port_t * port_ptr)143 SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_request_port(switch_core_port_allocator_t *alloc, switch_port_t *port_ptr)
144 {
145 	switch_port_t port = 0;
146 	switch_status_t status = SWITCH_STATUS_FALSE;
147 	int even = switch_test_flag(alloc, SPF_EVEN);
148 	int odd = switch_test_flag(alloc, SPF_ODD);
149 
150 	switch_mutex_lock(alloc->mutex);
151 	srand((unsigned) ((unsigned) (intptr_t) port_ptr + (unsigned) (intptr_t) switch_thread_self() + switch_micro_time_now()));
152 
153 	while (alloc->track_used < alloc->track_len) {
154 		uint32_t index;
155 		uint32_t tries = 0;
156 
157 		/* randomly pick a port */
158 		index = rand() % alloc->track_len;
159 
160 		/* if it is used walk up the list to find a free one */
161 		while (alloc->track[index] && tries < alloc->track_len) {
162 			tries++;
163 			if (alloc->track[index] < 0) {
164 				alloc->track[index]++;
165 			}
166 			if (++index >= alloc->track_len) {
167 				index = 0;
168 			}
169 		}
170 
171 		if (tries < alloc->track_len) {
172 			switch_bool_t r = SWITCH_TRUE;
173 
174 			if ((even && odd)) {
175 				port = (switch_port_t) (index + alloc->start);
176 			} else {
177 				port = (switch_port_t) (index + (alloc->start / 2));
178 				port *= 2;
179 			}
180 
181 			if ((alloc->flags & SPF_ROBUST_UDP)) {
182 				r = test_port(alloc, AF_INET, SOCK_DGRAM, port);
183 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "UDP port robustness check for port %d %s\n", port, r ? "pass" : "fail");
184 			}
185 
186 			if ((alloc->flags & SPF_ROBUST_TCP)) {
187 				r = test_port(alloc, AF_INET, SOCK_STREAM, port);
188 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TCP port robustness check for port %d %s\n", port, r ? "pass" : "fail");
189 			}
190 
191 			if (r) {
192 				alloc->track[index] = 1;
193 				alloc->track_used++;
194 				status = SWITCH_STATUS_SUCCESS;
195 				goto end;
196 			} else {
197 				alloc->track[index] = -4;
198 			}
199 		}
200 	}
201 
202 
203   end:
204 
205 	switch_mutex_unlock(alloc->mutex);
206 
207 	if (status == SWITCH_STATUS_SUCCESS) {
208 		*port_ptr = port;
209 	} else {
210 		*port_ptr = 0;
211 	}
212 
213 
214 	return status;
215 
216 }
217 
switch_core_port_allocator_free_port(switch_core_port_allocator_t * alloc,switch_port_t port)218 SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_free_port(switch_core_port_allocator_t *alloc, switch_port_t port)
219 {
220 	switch_status_t status = SWITCH_STATUS_FALSE;
221 	int even = switch_test_flag(alloc, SPF_EVEN);
222 	int odd = switch_test_flag(alloc, SPF_ODD);
223 	int index;
224 
225 	if (port < alloc->start) {
226 		return SWITCH_STATUS_GENERR;
227 	}
228 
229 	index = port - alloc->start;
230 
231 	if (!(even && odd)) {
232 		index /= 2;
233 	}
234 
235 	switch_mutex_lock(alloc->mutex);
236 	if (alloc->track[index] > 0) {
237 		alloc->track[index] = -4;
238 		alloc->track_used--;
239 		status = SWITCH_STATUS_SUCCESS;
240 	}
241 	switch_mutex_unlock(alloc->mutex);
242 
243 	return status;
244 }
245 
switch_core_port_allocator_destroy(switch_core_port_allocator_t ** alloc)246 SWITCH_DECLARE(void) switch_core_port_allocator_destroy(switch_core_port_allocator_t **alloc)
247 {
248 	switch_memory_pool_t *pool = (*alloc)->pool;
249 	switch_core_destroy_memory_pool(&pool);
250 	*alloc = NULL;
251 }
252 
253 /* For Emacs:
254  * Local Variables:
255  * mode:c
256  * indent-tabs-mode:t
257  * tab-width:4
258  * c-basic-offset:4
259  * End:
260  * For VIM:
261  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
262  */
263