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 ***********************************************************
9 $Id: heartbeat.c,v 1.9 2006-12-21 14:08:50 jeroen Exp $
10 $Date: 2006-12-21 14:08:50 $
11 **********************************************************/
13 #include "heartbeat.h"
24 #if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
28 /**************************************
30 **************************************/
32 /* Get a socket and determine the new IP address */
33 SOCKET
heartbeat_socket(
34 uint32_t *address_changed
,
36 const char *sIPv4Interface
,
39 const char *sIPv4LocalResolve
)
44 char local_ipv4
[NI_MAXHOST
];
45 #if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
46 struct ifreq interface
;
48 struct addrinfo hints
, *res
, *ressave
;
50 D(dolog(LOG_DEBUG
, "heartbeat_socket() - Address is %s\n", *sIPv4Local
));
52 if (address_changed
) *address_changed
= 0;
54 /* Get ourselves a nice IPv4 socket */
55 sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
58 dolog(LOG_ERR
, "Couldn't open a socket for determining current IPv4 address\n");
62 #if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
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.
71 * Only allowed as root, but we need root rights anyways
72 * to (re)configure the tunnel
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)
81 dolog(LOG_ERR
, "Couldn't bind to device \"%s\"\n", sIPv4Interface
);
84 /* We return a -1, thus the app will keep beating */
89 /* Make compiler happy and 'use' the param */
90 sIPv4Interface
= sIPv4Interface
;
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
98 memset(&hints
, 0, sizeof(struct addrinfo
));
99 hints
.ai_family
= AF_INET
;
100 hints
.ai_socktype
= SOCK_DGRAM
;
102 /* Get the POP IPv4 into a sockaddr */
103 if (getaddrinfo(sIPv4POP
, HEARTBEAT_PORT
, &hints
, &res
) != 0)
105 dolog(LOG_ERR
, "Couldn't resolve POP ip %s\n", sIPv4POP
);
108 /* We return a -1, thus the app will keep beating */
116 if (connect(sockfd
, res
->ai_addr
, (unsigned int)res
->ai_addrlen
) == 0) break;
119 freeaddrinfo(ressave
);
122 dolog(LOG_ERR
, "Failed to connect() to remote side\n");
124 /* We return a -1, thus the app will keep beating */
128 /* Normal operation, find out our local IPv4 address */
129 if (sIPv4LocalResolve
== NULL
)
131 /* Figure out our local IP */
132 socklen
= sizeof(sa
);
133 if (getsockname(sockfd
, &sa
, &socklen
) == -1)
135 dolog(LOG_WARNING
, "Couldn't get local socketaddress\n");
140 if (getnameinfo((struct sockaddr
*)&sa
, sizeof(sa
),
141 local_ipv4
, sizeof(local_ipv4
),
143 NI_NUMERICHOST
) != 0)
145 dolog(LOG_WARNING
, "Couldn't get local IP\n");
153 * this causes us to be able to use normal write()
154 * and also gives us the ability to find out the local IP
156 memset(&hints
, 0, sizeof(struct addrinfo
));
157 hints
.ai_family
= AF_INET
;
158 hints
.ai_socktype
= SOCK_DGRAM
;
160 /* Get the POP IPv4 into a sockaddr */
161 if (getaddrinfo(sIPv4LocalResolve
, NULL
, &hints
, &res
) != 0)
163 dolog(LOG_ERR
, "Couldn't resolve POP IPv4 %s\n", sIPv4POP
);
164 /* We return a -1, thus the app will keep beating */
170 if (getnameinfo(res
->ai_addr
, (socklen_t
)res
->ai_addrlen
,
171 local_ipv4
, sizeof(local_ipv4
),
173 NI_NUMERICHOST
) == 0)
177 dolog(LOG_WARNING
, "Couldn't get local IP\n");
180 freeaddrinfo(ressave
);
183 /* Did the IPv4 address change? */
184 if (*sIPv4Local
== NULL
||
185 strcmp(*sIPv4Local
, local_ipv4
) != 0)
187 if (*sIPv4Local
) free(*sIPv4Local
);
188 *sIPv4Local
= strdup(local_ipv4
);
190 dolog(LOG_DEBUG
, "heartbeat_socket() - IPv4 : %s\n", *sIPv4Local
);
194 /* Run a script to change the address */
195 if (address_changed
) *address_changed
= 1;
198 D(else dolog(LOG_DEBUG
, "heartbeat_socket() - Address stays %s\n", *sIPv4Local
));
204 /* Send a heartbeat */
205 int heartbeat_send(SOCKET sockfd
, char *sIPv4Local
, char *sIPv6Local
, char *sPassword
, bool bBehindNAT
)
207 struct MD5Context md5
;
208 unsigned char *p
, our_digest
[20], *pn
= our_digest
, buf
[1000];
212 time_tee
= time(NULL
);
214 /* Create the string to send including our password */
215 snprintf((char *)buf
, sizeof(buf
), "HEARTBEAT TUNNEL %s %s %ld %s",
217 (bBehindNAT
? "sender" : sIPv4Local
),
218 (long int)time_tee
, sPassword
);
222 MD5Update(&md5
, buf
, (unsigned int)strlen((char *)buf
));
223 MD5Final(our_digest
, &md5
);
225 /* Overwrite it without password */
227 p
+= snprintf((char *)buf
, sizeof(buf
)-17, "HEARTBEAT TUNNEL %s %s %ld ",
229 (bBehindNAT
== 1 ? "sender" : sIPv4Local
),
232 /* append the digest */
233 for (i
= 0; i
< 16; i
++)
235 snprintf((char *)p
, 3, (const char *)"%02x", *pn
++);
240 /* Send the heartbeat */
241 send(sockfd
, (const char *)buf
, (unsigned int)strlen((const char *)buf
),0);
243 dolog(LOG_DEBUG
, "[HB] %s\n", buf
);
248 char *heartbeat_getlocalIP(struct TIC_Tunnel
*hTunnel
)
250 bool address_changed
= false;
251 char *ipv4_local
= NULL
;
253 SOCKET sockfd
= heartbeat_socket(&address_changed
, 0, "",
257 if (sockfd
>= 0) closesocket(sockfd
);
259 dolog(LOG_DEBUG
, "Local IPv4 address: %s\n", ipv4_local
);
265 * Other code can call this every once in a while
266 * and it will take care of everything ("everything?" "everything!")
268 void heartbeat_beat(struct TIC_Tunnel
*hTunnel
)
270 uint32_t address_changed
= 0;
273 D(dolog(LOG_DEBUG
, "heartbeat_beat() - Beating from %s\n", hTunnel
->sIPv4_Local
);)
275 sockfd
= heartbeat_socket(&address_changed
, 0, "",
276 &hTunnel
->sIPv4_Local
,
281 if (address_changed
== 1)
283 D(dolog(LOG_DEBUG
, "heartbeat_beat() - Address changed to %s\n", hTunnel
->sIPv4_Local
);)
284 aiccu_reconfig(hTunnel
);
286 heartbeat_send(sockfd
,
287 hTunnel
->sIPv4_Local
,
288 hTunnel
->sIPv6_Local
,