]>
Commit | Line | Data |
---|---|---|
d98f6a46 SS |
1 | /********************************************************** |
2 | SixXS - Automatic IPv6 Connectivity Configuration Utility | |
3 | *********************************************************** | |
4 | Copyright 2003-2005 SixXS - http://www.sixxs.net | |
5 | *********************************************************** | |
6 | common/heartbeat.c - Heartbeat Code | |
7 | *********************************************************** | |
8 | $Author: jeroen $ | |
9 | $Id: heartbeat.c,v 1.9 2006-12-21 14:08:50 jeroen Exp $ | |
10 | $Date: 2006-12-21 14:08:50 $ | |
11 | **********************************************************/ | |
12 | ||
13 | #include "heartbeat.h" | |
14 | #include "aiccu.h" | |
15 | ||
16 | #include <stdio.h> | |
17 | #include <errno.h> | |
18 | #include <stdlib.h> | |
19 | #include <stdarg.h> | |
20 | #include <string.h> | |
21 | #include <ctype.h> | |
22 | #include <time.h> | |
23 | #include <signal.h> | |
24 | #if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE) | |
25 | #include <net/if.h> | |
26 | #endif | |
27 | ||
28 | /************************************** | |
29 | Functions | |
30 | **************************************/ | |
31 | ||
32 | /* Get a socket and determine the new IP address */ | |
33 | SOCKET heartbeat_socket( | |
34 | uint32_t *address_changed, | |
35 | int bStaticTunnel, | |
36 | const char *sIPv4Interface, | |
37 | char **sIPv4Local, | |
38 | const char *sIPv4POP, | |
39 | const char *sIPv4LocalResolve) | |
40 | { | |
41 | SOCKET sockfd; | |
42 | struct sockaddr sa; | |
43 | socklen_t socklen; | |
44 | char local_ipv4[NI_MAXHOST]; | |
45 | #if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE) | |
46 | struct ifreq interface; | |
47 | #endif | |
48 | struct addrinfo hints, *res, *ressave; | |
49 | ||
50 | D(dolog(LOG_DEBUG, "heartbeat_socket() - Address is %s\n", *sIPv4Local)); | |
51 | ||
52 | if (address_changed) *address_changed = 0; | |
53 | ||
54 | /* Get ourselves a nice IPv4 socket */ | |
55 | sockfd = socket(AF_INET, SOCK_DGRAM, 0); | |
56 | if (sockfd < 0) | |
57 | { | |
58 | dolog(LOG_ERR, "Couldn't open a socket for determining current IPv4 address\n"); | |
59 | return -1; | |
60 | } | |
61 | ||
62 | #if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE) | |
63 | /* | |
64 | * We don't have to bind to a device if this | |
65 | * is a static tunnel, this allows running | |
66 | * the heartbeat client as non-root. | |
67 | */ | |
68 | if (!bStaticTunnel) | |
69 | { | |
70 | /* | |
71 | * Only allowed as root, but we need root rights anyways | |
72 | * to (re)configure the tunnel | |
73 | */ | |
74 | ||
75 | /* Bind to the underlying IPv4 device */ | |
76 | memset(&interface, 0, sizeof(interface)); | |
77 | strncpy(interface.ifr_ifrn.ifrn_name, sIPv4Interface, IFNAMSIZ); | |
78 | if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, | |
79 | (char *)&interface, sizeof(interface)) == -1) | |
80 | { | |
81 | dolog(LOG_ERR, "Couldn't bind to device \"%s\"\n", sIPv4Interface); | |
82 | close(sockfd); | |
83 | ||
84 | /* We return a -1, thus the app will keep beating */ | |
85 | return -1; | |
86 | } | |
87 | } | |
88 | #else | |
89 | /* Make compiler happy and 'use' the param */ | |
90 | sIPv4Interface = sIPv4Interface; | |
91 | #endif | |
92 | ||
93 | /* | |
94 | * connect to the remote port | |
95 | * this causes us to be able to use normal write() | |
96 | * and also gives us the ability to find out the local IP | |
97 | */ | |
98 | memset(&hints, 0, sizeof(struct addrinfo)); | |
99 | hints.ai_family = AF_INET; | |
100 | hints.ai_socktype = SOCK_DGRAM; | |
101 | ||
102 | /* Get the POP IPv4 into a sockaddr */ | |
103 | if (getaddrinfo(sIPv4POP, HEARTBEAT_PORT, &hints, &res) != 0) | |
104 | { | |
105 | dolog(LOG_ERR, "Couldn't resolve POP ip %s\n", sIPv4POP); | |
106 | closesocket(sockfd); | |
107 | ||
108 | /* We return a -1, thus the app will keep beating */ | |
109 | return -1; | |
110 | } | |
111 | ||
112 | ressave = res; | |
113 | ||
114 | while (res) | |
115 | { | |
116 | if (connect(sockfd, res->ai_addr, (unsigned int)res->ai_addrlen) == 0) break; | |
117 | res = res->ai_next; | |
118 | } | |
119 | freeaddrinfo(ressave); | |
120 | if (res == NULL) | |
121 | { | |
122 | dolog(LOG_ERR, "Failed to connect() to remote side\n"); | |
123 | closesocket(sockfd); | |
124 | /* We return a -1, thus the app will keep beating */ | |
125 | return -1; | |
126 | } | |
127 | ||
128 | /* Normal operation, find out our local IPv4 address */ | |
129 | if (sIPv4LocalResolve == NULL) | |
130 | { | |
131 | /* Figure out our local IP */ | |
132 | socklen = sizeof(sa); | |
133 | if (getsockname(sockfd, &sa, &socklen) == -1) | |
134 | { | |
135 | dolog(LOG_WARNING, "Couldn't get local socketaddress\n"); | |
136 | closesocket(sockfd); | |
137 | return -1; | |
138 | } | |
139 | ||
140 | if (getnameinfo((struct sockaddr *)&sa, sizeof(sa), | |
141 | local_ipv4, sizeof(local_ipv4), | |
142 | NULL, 0, | |
143 | NI_NUMERICHOST) != 0) | |
144 | { | |
145 | dolog(LOG_WARNING, "Couldn't get local IP\n"); | |
146 | closesocket(sockfd); | |
147 | return -1; | |
148 | } | |
149 | } | |
150 | else | |
151 | { | |
152 | /* | |
153 | * this causes us to be able to use normal write() | |
154 | * and also gives us the ability to find out the local IP | |
155 | */ | |
156 | memset(&hints, 0, sizeof(struct addrinfo)); | |
157 | hints.ai_family = AF_INET; | |
158 | hints.ai_socktype = SOCK_DGRAM; | |
159 | ||
160 | /* Get the POP IPv4 into a sockaddr */ | |
161 | if (getaddrinfo(sIPv4LocalResolve, NULL, &hints, &res) != 0) | |
162 | { | |
163 | dolog(LOG_ERR, "Couldn't resolve POP IPv4 %s\n", sIPv4POP); | |
164 | /* We return a -1, thus the app will keep beating */ | |
165 | return -1; | |
166 | } | |
167 | ressave = res; | |
168 | while (res) | |
169 | { | |
170 | if (getnameinfo(res->ai_addr, (socklen_t)res->ai_addrlen, | |
171 | local_ipv4, sizeof(local_ipv4), | |
172 | NULL, 0, | |
173 | NI_NUMERICHOST) == 0) | |
174 | { | |
175 | break; | |
176 | } | |
177 | dolog(LOG_WARNING, "Couldn't get local IP\n"); | |
178 | res = res->ai_next; | |
179 | } | |
180 | freeaddrinfo(ressave); | |
181 | } | |
182 | ||
183 | /* Did the IPv4 address change? */ | |
184 | if (*sIPv4Local == NULL || | |
185 | strcmp(*sIPv4Local, local_ipv4) != 0) | |
186 | { | |
187 | if (*sIPv4Local) free(*sIPv4Local); | |
188 | *sIPv4Local = strdup(local_ipv4); | |
189 | ||
190 | dolog(LOG_DEBUG, "heartbeat_socket() - IPv4 : %s\n", *sIPv4Local); | |
191 | ||
192 | if (!bStaticTunnel) | |
193 | { | |
194 | /* Run a script to change the address */ | |
195 | if (address_changed) *address_changed = 1; | |
196 | } | |
197 | } | |
198 | D(else dolog(LOG_DEBUG, "heartbeat_socket() - Address stays %s\n", *sIPv4Local)); | |
199 | ||
200 | /* Return it */ | |
201 | return sockfd; | |
202 | } | |
203 | ||
204 | /* Send a heartbeat */ | |
205 | int heartbeat_send(SOCKET sockfd, char *sIPv4Local, char *sIPv6Local, char *sPassword, bool bBehindNAT) | |
206 | { | |
207 | struct MD5Context md5; | |
208 | unsigned char *p, our_digest[20], *pn = our_digest, buf[1000]; | |
209 | time_t time_tee; | |
210 | int i; | |
211 | ||
212 | time_tee = time(NULL); | |
213 | ||
214 | /* Create the string to send including our password */ | |
215 | snprintf((char *)buf, sizeof(buf), "HEARTBEAT TUNNEL %s %s %ld %s", | |
216 | sIPv6Local, | |
217 | (bBehindNAT ? "sender" : sIPv4Local), | |
218 | (long int)time_tee, sPassword); | |
219 | ||
220 | /* Generate a MD5 */ | |
221 | MD5Init(&md5); | |
222 | MD5Update(&md5, buf, (unsigned int)strlen((char *)buf)); | |
223 | MD5Final(our_digest, &md5); | |
224 | ||
225 | /* Overwrite it without password */ | |
226 | p = buf; | |
227 | p += snprintf((char *)buf, sizeof(buf)-17, "HEARTBEAT TUNNEL %s %s %ld ", | |
228 | sIPv6Local, | |
229 | (bBehindNAT == 1 ? "sender" : sIPv4Local), | |
230 | (long int)time_tee); | |
231 | ||
232 | /* append the digest */ | |
233 | for (i = 0; i < 16; i++) | |
234 | { | |
235 | snprintf((char *)p, 3, (const char *)"%02x", *pn++); | |
236 | p+=2; | |
237 | } | |
238 | *p = '\0'; | |
239 | ||
240 | /* Send the heartbeat */ | |
241 | send(sockfd, (const char *)buf, (unsigned int)strlen((const char *)buf),0); | |
242 | ||
243 | dolog(LOG_DEBUG, "[HB] %s\n", buf); | |
244 | ||
245 | return 0; | |
246 | } | |
247 | ||
248 | char *heartbeat_getlocalIP(struct TIC_Tunnel *hTunnel) | |
249 | { | |
250 | bool address_changed = false; | |
251 | char *ipv4_local = NULL; | |
252 | ||
253 | SOCKET sockfd = heartbeat_socket(&address_changed, 0, "", | |
254 | &ipv4_local, | |
255 | hTunnel->sIPv4_POP, | |
256 | NULL); | |
257 | if (sockfd >= 0) closesocket(sockfd); | |
258 | ||
259 | dolog(LOG_DEBUG, "Local IPv4 address: %s\n", ipv4_local); | |
260 | ||
261 | return ipv4_local; | |
262 | } | |
263 | ||
264 | /* | |
265 | * Other code can call this every once in a while | |
266 | * and it will take care of everything ("everything?" "everything!") | |
267 | */ | |
268 | void heartbeat_beat(struct TIC_Tunnel *hTunnel) | |
269 | { | |
270 | uint32_t address_changed = 0; | |
271 | SOCKET sockfd = -1; | |
272 | ||
273 | D(dolog(LOG_DEBUG, "heartbeat_beat() - Beating from %s\n", hTunnel->sIPv4_Local);) | |
274 | ||
275 | sockfd = heartbeat_socket(&address_changed, 0, "", | |
276 | &hTunnel->sIPv4_Local, | |
277 | hTunnel->sIPv4_POP, | |
278 | NULL); | |
279 | if (sockfd >= 0) | |
280 | { | |
281 | if (address_changed == 1) | |
282 | { | |
283 | D(dolog(LOG_DEBUG, "heartbeat_beat() - Address changed to %s\n", hTunnel->sIPv4_Local);) | |
284 | aiccu_reconfig(hTunnel); | |
285 | } | |
286 | heartbeat_send(sockfd, | |
287 | hTunnel->sIPv4_Local, | |
288 | hTunnel->sIPv6_Local, | |
289 | hTunnel->sPassword, | |
290 | 1); | |
291 | closesocket(sockfd); | |
292 | sockfd = (SOCKET)-1; | |
293 | } | |
294 | } | |
295 |