1219820Sjeff/*
2219820Sjeff  This software is available to you under a choice of one of two
3219820Sjeff  licenses.  You may choose to be licensed under the terms of the GNU
4219820Sjeff  General Public License (GPL) Version 2, available at
5219820Sjeff  <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD
6219820Sjeff  license, available in the LICENSE.TXT file accompanying this
7219820Sjeff  software.  These details are also available at
8219820Sjeff  <http://openib.org/license.html>.
9219820Sjeff
10219820Sjeff  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
11219820Sjeff  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12219820Sjeff  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
13219820Sjeff  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
14219820Sjeff  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
15219820Sjeff  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16219820Sjeff  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
17219820Sjeff  SOFTWARE.
18219820Sjeff
19219820Sjeff  Copyright (c) 2004 Topspin Communications.  All rights reserved.
20219820Sjeff  Copyright (c) 2005-2006 Mellanox Technologies Ltd.  All rights reserved.
21219820Sjeff
22219820Sjeff  $Id$
23219820Sjeff*/
24219820Sjeff
25219820Sjeff/*
26219820Sjeff * system includes
27219820Sjeff */
28219820Sjeff#if HAVE_CONFIG_H
29219820Sjeff#  include <config.h>
30219820Sjeff#endif /* HAVE_CONFIG_H */
31219820Sjeff
32219820Sjeff#include <unistd.h>
33219820Sjeff#include <errno.h>
34219820Sjeff#include <stdio.h>
35219820Sjeff#include <stdlib.h>
36219820Sjeff#include <string.h>
37219820Sjeff#include <fnmatch.h>
38219820Sjeff#include <sys/socket.h>
39219820Sjeff#include <netinet/in.h>
40219820Sjeff#include <arpa/inet.h>
41219820Sjeff
42219820Sjeff/*
43219820Sjeff * SDP specific includes
44219820Sjeff */
45219820Sjeff#include "libsdp.h"
46219820Sjeff
47219820Sjeff/* --------------------------------------------------------------------- */
48219820Sjeff/* library static and global variables                                   */
49219820Sjeff/* --------------------------------------------------------------------- */
50219820Sjeffextern char *program_invocation_name, *program_invocation_short_name;
51219820Sjeff
52219820Sjeffstatic void
53219820Sjeffget_rule_str(
54219820Sjeff	struct use_family_rule *rule,
55219820Sjeff	char *buf,
56219820Sjeff	size_t len )
57219820Sjeff{
58219820Sjeff	char addr_buf[MAX_ADDR_STR_LEN];
59219820Sjeff	char ports_buf[16];
60219820Sjeff	char *target = __sdp_get_family_str( rule->target_family );
61219820Sjeff	char *prog = rule->prog_name_expr;
62219820Sjeff
63219820Sjeff	/* TODO: handle IPv6 in rule */
64219820Sjeff	if ( rule->match_by_addr ) {
65219820Sjeff		if ( rule->prefixlen != 32 )
66219820Sjeff			sprintf( addr_buf, "%s/%d", inet_ntoa( rule->ipv4 ),
67219820Sjeff						rule->prefixlen );
68219820Sjeff		else
69219820Sjeff			sprintf( addr_buf, "%s", inet_ntoa( rule->ipv4 ) );
70219820Sjeff	} else {
71219820Sjeff		strcpy( addr_buf, "*" );
72219820Sjeff	}
73219820Sjeff
74219820Sjeff	if ( rule->match_by_port )
75219820Sjeff		if ( rule->eport > rule->sport )
76219820Sjeff			sprintf( ports_buf, "%d", rule->sport );
77219820Sjeff		else
78219820Sjeff			sprintf( ports_buf, "%d-%d", rule->sport, rule->eport );
79219820Sjeff	else
80219820Sjeff		sprintf( ports_buf, "*" );
81219820Sjeff
82219820Sjeff	snprintf( buf, len, "use %s %s %s:%s", target, prog, addr_buf, ports_buf );
83219820Sjeff}
84219820Sjeff
85219820Sjeff/* return 0 if the addresses match */
86219820Sjeffstatic inline int
87219820Sjeffmatch_ipv4_addr(
88219820Sjeff	struct use_family_rule *rule,
89219820Sjeff	const struct sockaddr_in *sin )
90219820Sjeff{
91219820Sjeff	return ( rule->ipv4.s_addr !=
92219820Sjeff				( sin->sin_addr.
93219820Sjeff				  s_addr & htonl( SDP_NETMASK( rule->prefixlen ) ) ) );
94219820Sjeff}
95219820Sjeff
96219820Sjeffstatic int
97219820Sjeffmatch_ip_addr_and_port(
98219820Sjeff	struct use_family_rule *rule,
99219820Sjeff	const struct sockaddr *addr_in,
100219820Sjeff	const socklen_t addrlen )
101219820Sjeff{
102219820Sjeff	const struct sockaddr_in *sin = ( const struct sockaddr_in * )addr_in;
103219820Sjeff	const struct sockaddr_in6 *sin6 = ( const struct sockaddr_in6 * )addr_in;
104219820Sjeff	struct sockaddr_in tmp_sin;
105219820Sjeff	unsigned short port;
106219820Sjeff	int match = 1;
107219820Sjeff	char addr_buf[MAX_ADDR_STR_LEN];
108219820Sjeff	const char *addr_str;
109219820Sjeff	char rule_str[512];
110219820Sjeff
111219820Sjeff	if ( __sdp_log_get_level(  ) <= 3 ) {
112219820Sjeff		if ( sin6->sin6_family == AF_INET6 ) {
113219820Sjeff			addr_str =
114219820Sjeff				inet_ntop( AF_INET6, ( void * )&( sin6->sin6_addr ), addr_buf,
115219820Sjeff							  MAX_ADDR_STR_LEN );
116219820Sjeff			port = ntohs( sin6->sin6_port );
117219820Sjeff		} else {
118219820Sjeff			addr_str =
119219820Sjeff				inet_ntop( AF_INET, ( void * )&( sin->sin_addr ), addr_buf,
120219820Sjeff							  MAX_ADDR_STR_LEN );
121219820Sjeff			port = ntohs( sin->sin_port );
122219820Sjeff		}
123219820Sjeff		if ( addr_str == NULL )
124219820Sjeff			addr_str = "INVALID_ADDR";
125219820Sjeff
126219820Sjeff		get_rule_str( rule, rule_str, sizeof( rule_str ) );
127219820Sjeff
128219820Sjeff		__sdp_log( 3, "MATCH: matching %s:%d to %s => \n", addr_str, port,
129219820Sjeff					  rule_str );
130219820Sjeff	}
131219820Sjeff
132219820Sjeff	/* We currently only support IPv4 and IPv4 embedded in IPv6 */
133219820Sjeff	if ( rule->match_by_port ) {
134219820Sjeff		if ( sin6->sin6_family == AF_INET6 )
135219820Sjeff			port = ntohs( sin6->sin6_port );
136219820Sjeff		else
137219820Sjeff			port = ntohs( sin->sin_port );
138219820Sjeff
139219820Sjeff		if ( ( port < rule->sport ) || ( port > rule->eport ) ) {
140219820Sjeff			__sdp_log( 3, "NEGATIVE by port range\n" );
141219820Sjeff			match = 0;
142219820Sjeff		}
143219820Sjeff	}
144219820Sjeff
145219820Sjeff	if ( match && rule->match_by_addr ) {
146219820Sjeff		if ( __sdp_sockaddr_to_sdp( addr_in, addrlen, &tmp_sin, NULL ) ||
147219820Sjeff			  match_ipv4_addr( rule, &tmp_sin ) ) {
148219820Sjeff			__sdp_log( 3, "NEGATIVE by address\n" );
149219820Sjeff			match = 0;
150219820Sjeff		}
151219820Sjeff	}
152219820Sjeff
153219820Sjeff	if ( match )
154219820Sjeff		__sdp_log( 3, "POSITIVE\n" );
155219820Sjeff
156219820Sjeff	return match;
157219820Sjeff}
158219820Sjeff
159219820Sjeff/* return 1 on match */
160219820Sjeffstatic int
161219820Sjeffmatch_program_name(
162219820Sjeff	struct use_family_rule *rule )
163219820Sjeff{
164219820Sjeff	return !fnmatch( rule->prog_name_expr, program_invocation_short_name, 0 );
165219820Sjeff}
166219820Sjeff
167219820Sjeffstatic use_family_t
168219820Sjeffget_family_by_first_matching_rule(
169219820Sjeff	const struct sockaddr *sin,
170219820Sjeff	const socklen_t addrlen,
171219820Sjeff	struct use_family_rule *rules )
172219820Sjeff{
173219820Sjeff	struct use_family_rule *rule;
174219820Sjeff
175219820Sjeff	for ( rule = rules; rule != NULL; rule = rule->next ) {
176219820Sjeff		/* skip if not our program */
177219820Sjeff		if ( !match_program_name( rule ) )
178219820Sjeff			continue;
179219820Sjeff
180219820Sjeff		/* first rule wins */
181219820Sjeff		if ( match_ip_addr_and_port( rule, sin, addrlen ) )
182219820Sjeff			return ( rule->target_family );
183219820Sjeff	}
184219820Sjeff
185219820Sjeff	return ( USE_BOTH );
186219820Sjeff}
187219820Sjeff
188219820Sjeff/* return the result of the first matching rule found */
189219820Sjeffuse_family_t
190219820Sjeff__sdp_match_listen(
191219820Sjeff	const struct sockaddr * sin,
192219820Sjeff	const socklen_t addrlen )
193219820Sjeff{
194219820Sjeff	use_family_t target_family;
195219820Sjeff
196219820Sjeff	/* if we do not have any rules we use sdp */
197219820Sjeff	if ( __sdp_config_empty(  ) )
198219820Sjeff		target_family = USE_SDP;
199219820Sjeff	else
200219820Sjeff		target_family =
201219820Sjeff			get_family_by_first_matching_rule( sin, addrlen,
202219820Sjeff														  __sdp_servers_family_rules_head );
203219820Sjeff
204219820Sjeff	__sdp_log( 4, "MATCH LISTEN: => %s\n",
205219820Sjeff				  __sdp_get_family_str( target_family ) );
206219820Sjeff
207219820Sjeff	return ( target_family );
208219820Sjeff}
209219820Sjeff
210219820Sjeffuse_family_t
211219820Sjeff__sdp_match_connect(
212219820Sjeff	const struct sockaddr * sin,
213219820Sjeff	const socklen_t addrlen )
214219820Sjeff{
215219820Sjeff	use_family_t target_family;
216219820Sjeff
217219820Sjeff	/* if we do not have any rules we use sdp */
218219820Sjeff	if ( __sdp_config_empty(  ) )
219219820Sjeff		target_family = USE_SDP;
220219820Sjeff	else
221219820Sjeff		target_family =
222219820Sjeff			get_family_by_first_matching_rule( sin, addrlen,
223219820Sjeff														  __sdp_clients_family_rules_head );
224219820Sjeff
225219820Sjeff	__sdp_log( 4, "MATCH CONNECT: => %s\n",
226219820Sjeff				  __sdp_get_family_str( target_family ) );
227219820Sjeff
228219820Sjeff	return ( target_family );
229219820Sjeff}
230219820Sjeff
231219820Sjeff/* given a set of rules see if there is a global match for current program */
232219820Sjeffstatic use_family_t
233219820Sjeffmatch_by_all_rules_program(
234219820Sjeff	struct use_family_rule *rules )
235219820Sjeff{
236219820Sjeff	int any_sdp = 0;
237219820Sjeff	int any_tcp = 0;
238219820Sjeff	use_family_t target_family = USE_BOTH;
239219820Sjeff	struct use_family_rule *rule;
240219820Sjeff
241219820Sjeff	for ( rule = rules; ( rule != NULL ) && ( target_family == USE_BOTH );
242219820Sjeff			rule = rule->next ) {
243219820Sjeff		/* skip if not our program */
244219820Sjeff		if ( !match_program_name( rule ) )
245219820Sjeff			continue;
246219820Sjeff
247219820Sjeff		/*
248219820Sjeff		 * to declare a dont care we either have a dont care address and port
249219820Sjeff		 * or the previous non global rules use the same target family as the
250219820Sjeff		 * global rule
251219820Sjeff		 */
252219820Sjeff		if ( rule->match_by_addr || rule->match_by_port ) {
253219820Sjeff			/* not a glocal match rule - just track the target family */
254219820Sjeff			if ( rule->target_family == USE_SDP )
255219820Sjeff				any_sdp++;
256219820Sjeff			else if ( rule->target_family == USE_TCP )
257219820Sjeff				any_tcp++;
258219820Sjeff		} else {
259219820Sjeff			/* a global match so we can declare a match by program */
260219820Sjeff			if ( ( rule->target_family == USE_SDP ) && ( any_tcp == 0 ) )
261219820Sjeff				target_family = USE_SDP;
262219820Sjeff			else if ( ( rule->target_family == USE_TCP ) && ( any_sdp == 0 ) )
263219820Sjeff				target_family = USE_TCP;
264219820Sjeff		}
265219820Sjeff	}
266219820Sjeff	return ( target_family );
267219820Sjeff}
268219820Sjeff
269219820Sjeff/* return tcp or sdp if the port and role are dont cares */
270219820Sjeffuse_family_t
271219820Sjeff__sdp_match_by_program(
272219820Sjeff	 )
273219820Sjeff{
274219820Sjeff	use_family_t server_target_family;
275219820Sjeff	use_family_t client_target_family;
276219820Sjeff	use_family_t target_family = USE_BOTH;
277219820Sjeff
278219820Sjeff	if ( __sdp_config_empty(  ) ) {
279219820Sjeff		target_family = USE_SDP;
280219820Sjeff	} else {
281219820Sjeff		/* need to try both server and client rules */
282219820Sjeff		server_target_family =
283219820Sjeff			match_by_all_rules_program( __sdp_servers_family_rules_head );
284219820Sjeff		client_target_family =
285219820Sjeff			match_by_all_rules_program( __sdp_clients_family_rules_head );
286219820Sjeff
287219820Sjeff		/* only if both agree */
288219820Sjeff		if ( server_target_family == client_target_family )
289219820Sjeff			target_family = server_target_family;
290219820Sjeff	}
291219820Sjeff
292219820Sjeff	__sdp_log( 4, "MATCH PROGRAM: => %s\n",
293219820Sjeff				  __sdp_get_family_str( target_family ) );
294219820Sjeff
295219820Sjeff	return ( target_family );
296219820Sjeff}
297