1 /*
2  *      $Id: saddr.c 199 2008-04-09 16:13:07Z boote $
3  */
4 /************************************************************************
5 *									*
6 *			     Copyright (C)  2002			*
7 *				Internet2				*
8 *			     All Rights Reserved			*
9 *									*
10 ************************************************************************/
11 /*
12  *	File:		saddr.c
13  *
14  *	Author:		Jeff W. Boote
15  *			Internet2
16  *
17  *	Date:		Tue Nov 26 07:45:48 MST 2002
18  *
19  *	Description:
20  *
21  *	Generic socket functions used here to abstract away addr family
22  *	differences.
23  */
24 #include <assert.h>
25 #include <I2util/saddr.h>
26 
27 #include <string.h>
28 
29 /*
30  * These first functions are used to copy sockaddr structs in and out of
31  * a union designed to make the rest of the code more ISO C99 compliant.
32  * Specifically - type punning (aliasing) is much more limited than it
33  * used to be to allow for more optimization. (Of course it specifically
34  * de-optimizes my code since I now have to memcpy things instead of
35  * modifying them in place. YUCK!)
36  */
37 
38 /*
39  * Function:    I2SockAddrToSockUnion
40  *
41  * Description:
42  *
43  * In Args:
44  *
45  * Out Args:
46  *
47  * Scope:
48  * Returns:
49  * Side Effect:
50  */
51 I2SockUnion *
I2SockAddrToSockUnion(const struct sockaddr * sa,socklen_t sa_len,I2SockUnion * sau_mem)52 I2SockAddrToSockUnion(
53         const struct sockaddr   *sa,
54         socklen_t               sa_len,
55         I2SockUnion             *sau_mem
56         )
57 {
58     assert(sau_mem);
59     assert(sa);
60 
61     memset(sau_mem,0,sizeof(I2SockUnion));
62 
63     switch(sa->sa_family){
64 #ifdef	AF_INET6
65         case AF_INET6:
66             if(sa_len < sizeof(struct sockaddr_in6))
67                 return NULL;
68 
69             memcpy(&sau_mem->sin6,sa,sa_len);
70 
71             break;
72 #endif
73         case AF_INET:
74             if(sa_len < sizeof(struct sockaddr_in))
75                 return NULL;
76 
77             memcpy(&sau_mem->sin,sa,sa_len);
78 
79             break;
80 
81         default:
82             return NULL;
83     }
84 
85     return sau_mem;
86 }
87 
88 /*
89  * Function:    I2SockUnionToSockAddr
90  *
91  * Description:
92  *
93  * In Args:
94  *
95  * Out Args:
96  *
97  * Scope:
98  * Returns:
99  * Side Effect:
100  */
101 struct sockaddr *
I2SockUnionToSockAddr(const I2SockUnion * sau,socklen_t * sa_len_in_out,struct sockaddr * sa_mem)102 I2SockUnionToSockAddr(
103         const I2SockUnion   *sau,
104         socklen_t           *sa_len_in_out,
105         struct sockaddr     *sa_mem
106         )
107 {
108     assert(sau);
109     assert(sa_mem);
110 
111     switch(sau->sa.sa_family){
112 #ifdef	AF_INET6
113         case AF_INET6:
114             if(*sa_len_in_out < sizeof(struct sockaddr_in6))
115                 return NULL;
116             *sa_len_in_out = sizeof(struct sockaddr_in6);
117 
118 
119             break;
120 #endif
121         case AF_INET:
122             if(*sa_len_in_out < sizeof(struct sockaddr_in))
123                 return NULL;
124             *sa_len_in_out = sizeof(struct sockaddr_in);
125 
126             break;
127 
128         default:
129             return NULL;
130     }
131 
132     memcpy(sa_mem,&sau->sas,*sa_len_in_out);
133 
134     return sa_mem;
135 
136 }
137 
138 /*
139  * Function:	I2SockAddrEqual
140  *
141  * Description:
142  *
143  * In Args:
144  *
145  * Out Args:
146  *
147  * Scope:
148  * Returns:
149  * 	<0	: error/unsupported addr family
150  * 	0	: false
151  * 	>0	: true
152  * Side Effect:
153  */
154 int
I2SockAddrEqual(const struct sockaddr * sa1,socklen_t sa1_len,const struct sockaddr * sa2,socklen_t sa2_len,uint32_t chk_what)155 I2SockAddrEqual(
156 	const struct sockaddr	*sa1,
157 	socklen_t		sa1_len,
158 	const struct sockaddr	*sa2,
159 	socklen_t		sa2_len,
160 	uint32_t		chk_what
161 	)
162 {
163     I2SockUnion sau1_mem, sau2_mem;
164     I2SockUnion *sau1, *sau2;
165 
166 	/*
167 	 * If the lengths are not equal - or the families are not the
168 	 * same - check if the v6 address is really an encoded v4 address.
169 	 * If it is - then re-call ourselves with the v4 version directly.
170 	 */
171 	if((sa1_len != sa2_len) || (sa1->sa_family != sa2->sa_family)){
172 #ifdef	AF_INET6
173 		struct sockaddr_in  v4rec;
174 
175 		/*
176 		 * check if sa1 is a mapped addr.
177 		 */
178 
179                 /* copy sa1 into the union */
180                 if( !(sau1 = I2SockAddrToSockUnion(sa1,sa1_len,&sau1_mem))){
181                     return -1;
182                 }
183 
184 		if((sau1->sa.sa_family==AF_INET6) &&
185 				IN6_IS_ADDR_V4MAPPED(&sau1->sin6.sin6_addr)){
186 			memset(&v4rec,0,sizeof(v4rec));
187 #ifdef	HAVE_STRUCT_SOCKADDR_SA_LEN
188 			v4rec.sin_len = sizeof(v4rec);
189 #endif
190 			v4rec.sin_family = AF_INET;
191 			memcpy(&v4rec.sin_addr.s_addr,
192 					&sau1->sin6.sin6_addr.s6_addr[12],4);
193                         v4rec.sin_port = sau1->sin6.sin6_port;
194 			return I2SockAddrEqual((struct sockaddr*)&v4rec,
195 					sizeof(v4rec),
196 					sa2,sa2_len,chk_what);
197 		}
198 
199                 /* copy sa1 into the union */
200                 if( !(sau2 = I2SockAddrToSockUnion(sa2,sa2_len,&sau2_mem))){
201                     return -1;
202                 }
203 
204 		if((sau2->sa.sa_family==AF_INET6) &&
205 				IN6_IS_ADDR_V4MAPPED(&sau2->sin6.sin6_addr)){
206 			memset(&v4rec,0,sizeof(v4rec));
207 #ifdef	HAVE_STRUCT_SOCKADDR_SA_LEN
208 			v4rec.sin_len = sizeof(v4rec);
209 #endif
210 			v4rec.sin_family = AF_INET;
211 			memcpy(&v4rec.sin_addr.s_addr,
212 					&sau2->sin6.sin6_addr.s6_addr[12],4);
213                         v4rec.sin_port = sau2->sin6.sin6_port;
214 			return I2SockAddrEqual(sa1,sa1_len,
215 					(struct sockaddr*)&v4rec,
216 					sizeof(v4rec),chk_what);
217 		}
218 #endif
219 		return 0;
220 	}
221 
222         /* copy to union to enable punning */
223         if( !(sau1 = I2SockAddrToSockUnion(sa1,sa1_len,&sau1_mem)) ||
224                 !(sau2 = I2SockAddrToSockUnion(sa2,sa2_len,&sau2_mem))){
225             return -1;
226         }
227 
228 	switch(sau1->sa.sa_family){
229 #ifdef	AF_INET6
230 	case AF_INET6:
231 		if((chk_what & I2SADDR_ADDR) &&
232 			(memcmp(&sau1->sin6.sin6_addr,&sau2->sin6.sin6_addr,
233 				sizeof(struct in6_addr)) != 0)){
234 			return 0;
235 		}
236 
237 		if((chk_what & I2SADDR_PORT) &&
238 				(sau1->sin6.sin6_port != sau2->sin6.sin6_port)){
239 			return 0;
240 		}
241 
242 		/*
243 		 * TODO:Do I need to check scope? Won't for now...
244 		 */
245 		return 1;
246 
247 		break;
248 #endif
249 	case AF_INET:
250 		if((chk_what & I2SADDR_ADDR) &&
251 				(memcmp(&sau1->sin.sin_addr,&sau2->sin.sin_addr,
252 				sizeof(struct in_addr)) != 0)){
253 			return 0;
254 		}
255 
256 		if((chk_what & I2SADDR_PORT) &&
257 				(sau1->sin.sin_port != sau2->sin.sin_port)){
258 			return 0;
259 		}
260 
261 		return 1;
262 
263 		break;
264 
265 	default:
266 		return -1;
267 	}
268 
269 	return -1;
270 }
271 
272 /*
273  * Function:	I2SockAddrIsLoopback
274  *
275  * Description:
276  *
277  * In Args:
278  *
279  * Out Args:
280  *
281  * Scope:
282  * Returns:
283  * 	<0	: error/unsupported addr family
284  * 	0	: false
285  * 	>0	: true
286  * Side Effect:
287  */
288 int
I2SockAddrIsLoopback(const struct sockaddr * sa,socklen_t sa_len)289 I2SockAddrIsLoopback(
290 	const struct sockaddr	*sa,
291 	socklen_t		sa_len
292 	)
293 {
294     I2SockUnion sau_mem;
295     I2SockUnion *sau;
296 
297     if( !(sau = I2SockAddrToSockUnion(sa,sa_len,&sau_mem))){
298         return -1;
299     }
300 
301 
302     switch(sau->sa.sa_family){
303 #ifdef	AF_INET6
304         case AF_INET6:
305             if (IN6_IS_ADDR_V4MAPPED(&sau->sin6.sin6_addr)){
306                 struct sockaddr_in  v4rec;
307 
308                 memset(&v4rec,0,sizeof(v4rec));
309 #ifdef	HAVE_STRUCT_SOCKADDR_SA_LEN
310                 v4rec.sin_len = sizeof(v4rec);
311 #endif
312                 v4rec.sin_family = AF_INET;
313                 memcpy(&v4rec.sin_addr.s_addr,
314                         &sau->sin6.sin6_addr.s6_addr[12],4);
315                 v4rec.sin_port = sau->sin6.sin6_port;
316 
317                 return I2SockAddrIsLoopback(&sau->sa,sizeof(v4rec));
318             }
319 
320             return IN6_IS_ADDR_LOOPBACK(&sau->sin6.sin6_addr);
321 
322             break;
323 #endif
324         case AF_INET:
325             return (sau->sin.sin_addr.s_addr == htonl(INADDR_LOOPBACK));
326 
327             break;
328 
329         default:
330             return -1;
331     }
332 
333     return 0;
334 }
335