1 /*
2  * This file is part of the Sofia-SIP package
3  *
4  * Copyright (C) 2009 Nokia Corporation.
5  *
6  * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24 
25 /**@CFILE s2_localinfo.c
26  *
27  * @brief s2_localinfo() stub returning well-known addresses for testing.
28  *
29  * @author Pekka Pessi <Pekka.Pessi@nokia.com>
30  */
31 
32 #include "config.h"
33 
34 #include <sofia-sip/hostdomain.h>
35 #include <sofia-sip/su_string.h>
36 #include <sofia-sip/su_alloc.h>
37 
38 #include <stdlib.h>
39 #include <assert.h>
40 
41 #define SU_LOCALINFO_TEST 1
42 
43 #define su_getlocalinfo s2_getlocalinfo
44 #define su_freelocalinfo s2_freelocalinfo
45 #define su_gli_strerror s2_gli_strerror
46 #define su_copylocalinfo s2_copylocalinfo
47 #define su_sockaddr_scope s2_sockaddr_scope
48 #define su_getlocalip s2_getlocalip
49 
50 #include "su_localinfo.c"
51 
52 static char const *default_ifaces[] = {
53   "lo0\0" "127.0.0.1\0" "::1\0",
54   NULL
55 };
56 
57 static char const **ifaces = default_ifaces;
58 
s2_localinfo_ifaces(char const ** replace_ifaces)59 void s2_localinfo_ifaces(char const **replace_ifaces)
60 {
61   ifaces = replace_ifaces;
62 }
63 
64 int
s2_getlocalinfo(su_localinfo_t const * hints,su_localinfo_t ** return_localinfo)65 s2_getlocalinfo(su_localinfo_t const *hints,
66 		su_localinfo_t **return_localinfo)
67 {
68   int error = 0, ip4 = 0, ip6 = 0, i;
69   su_localinfo_t *result = NULL, **rr;
70   su_localinfo_t hh[1] = {{ 0 }};
71 
72   assert(return_localinfo);
73 
74   *return_localinfo = NULL;
75 
76   if (hints) {
77     /* Copy hints so that it can be modified */
78     *hh = *hints;
79     if (hh->li_canonname)
80       hh->li_flags |= LI_CANONNAME;
81   }
82 
83   hints = hh;
84 
85   switch (hh->li_family) {
86 #if SU_HAVE_IN6
87   case AF_INET6:
88     if (hh->li_flags & LI_V4MAPPED)
89       ip6 = ip4 = 1, hh->li_family = 0;
90     else
91       ip6 = 1;
92     break;
93 #endif
94 
95   case AF_INET:
96     ip4 = 1;
97     break;
98 
99   case 0:
100     ip6 = ip4 = 1;
101     break;
102 
103   default:
104     return -1;
105   }
106 
107   for (i = 0; ifaces[i]; i++) {
108     char const *iface = ifaces[i], *address = iface;
109     su_sockaddr_t su[1];
110 
111     for (;(address += strlen(address) + 1)[0];) {
112       su_localinfo_t *li = NULL;
113       int scope = 0;
114 
115       memset(su, 0, sizeof su);
116 
117       if (0)
118 	;
119 #if SU_HAVE_IN6
120       else if (ip4 &&
121 	       (hints->li_flags & LI_V4MAPPED) != 0 &&
122 	       host_is_ip4_address(address)) {
123 	int32_t mapped;
124 	su->su_len = (sizeof su->su_sin6);
125 	su_inet_pton(su->su_family = AF_INET6,
126 		     address,
127 		     &mapped);
128 	((int32_t*)&su->su_sin6.sin6_addr)[2] = htonl(0xffff);
129 	((int32_t*)&su->su_sin6.sin6_addr)[3] = mapped;
130 	scope = li_scope4(mapped);
131       }
132       else if (ip6 && host_is_ip6_address(address)) {
133 	su->su_len = (sizeof su->su_sin6);
134 	su_inet_pton(su->su_family = AF_INET6,
135 		     address,
136 		     &su->su_sin6.sin6_addr);
137 	scope = li_scope6(&su->su_sin6.sin6_addr);
138       }
139 #endif
140       else if (ip4 && host_is_ip4_address(address)) {
141 	su->su_len = (sizeof su->su_sin);
142 	su_inet_pton(su->su_family = AF_INET,
143 		     address,
144 		     &su->su_sin.sin_addr);
145 	scope = li_scope4(su->su_sin.sin_addr.s_addr);
146       }
147       else
148 	continue;
149 
150       if (scope == 0)
151 	continue;
152       if (hints->li_scope && (hints->li_scope & scope) == 0)
153 	continue;
154       if (hints->li_index && hints->li_index != i + 1)
155 	continue;
156       if (hints->li_ifname && strcmp(hints->li_ifname, iface) != 0)
157 	continue;
158       if (hints->li_family && hints->li_family != su->su_family)
159 	continue;
160       if (hints->li_canonname && !su_casematch(address, hints->li_canonname))
161 	continue;
162 
163       li = calloc(1, (sizeof *li) + (sizeof *su) + strlen(iface) + 1);
164       li->li_family = su->su_family;
165       li->li_scope = scope;
166       li->li_index = i + 1;
167       li->li_addrlen = su_sockaddr_size(su);
168       li->li_addr = memcpy((li + 1), su, (sizeof *su));
169       if (hints->li_flags & LI_IFNAME)
170 	li->li_ifname = strcpy((char *)li->li_addr + li->li_addrlen, iface);
171 
172       if ((hints->li_flags & LI_CANONNAME) || hints->li_canonname) {
173 	li->li_flags |= LI_NUMERIC;
174 	li->li_canonname = su_strdup(NULL, address);
175       }
176 
177 #define LI_MAPPED(li) \
178   ((li)->li_family == AF_INET6 &&					\
179    (IN6_IS_ADDR_V4MAPPED(&(li)->li_addr->su_sin6.sin6_addr) ||		\
180     IN6_IS_ADDR_V4COMPAT(&(li)->li_addr->su_sin6.sin6_addr)))
181 
182       /* Insert according to scope, mappedness and family */
183       for (rr = &result; *rr; rr = &(*rr)->li_next) {
184 	if ((*rr)->li_scope < li->li_scope)
185 	  break;
186 #if SU_HAVE_IN6
187 	if (LI_MAPPED(*rr) > LI_MAPPED(li))
188 	  break;
189 #endif
190 	if ((*rr)->li_family < li->li_family)
191 	  break;
192       }
193       li->li_next = *rr;
194       *rr = li;
195     }
196   }
197 
198   *return_localinfo = result;
199 
200   if (result == NULL)
201     error = ELI_NOADDRESS;
202 
203   return error;
204 }
205 
206