Module: sip-router
Branch: master
Commit: fe6207c8413f2f14d026bfe6eb1032fd903126d1
URL:
http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=fe6207c…
Author: Andrei Pelinescu-Onciul <andrei(a)iptel.org>
Committer: Andrei Pelinescu-Onciul <andrei(a)iptel.org>
Date: Mon Feb 1 20:15:27 2010 +0100
core: mhomed: fix fd leak on error, ipv6, fd no
- on error the cached fds were set to 0, without closing them. Now
they are not overwritten anymore (opening another one would not
help).
- try ipv6 only if USE_IPV6 is defined
- 0 is a valid value for a fd => changed the initial values and
the comparisons to use -1 / <0
- if connect fails with EISCONN disable future caching of the
socket (return to the old open new socket, connect(), close()
mode). This way mhomed will work even in the unlikely case of
an OS where connect()-ing multiple times the same udp socket is
not allowed.
---
forward.c | 50 ++++++++++++++++++++++++++++++++++----------------
1 files changed, 34 insertions(+), 16 deletions(-)
diff --git a/forward.c b/forward.c
index c81e95a..2e6f05e 100644
--- a/forward.c
+++ b/forward.c
@@ -115,12 +115,15 @@
* multihomed hosts
*/
-static int sock_inet = 0;
-static int sock_inet6 = 0;
+static int mhomed_sock_cache_disabled = 0;
+static int sock_inet = -1;
+#ifdef USE_IPV6
+static int sock_inet6 = -1;
+#endif /* USE_IPV6 */
struct socket_info* get_out_socket(union sockaddr_union* to, int proto)
{
- int temp_sock;
+ int* temp_sock;
socklen_t len;
union sockaddr_union from;
struct socket_info* si;
@@ -130,57 +133,72 @@ struct socket_info* get_out_socket(union sockaddr_union* to, int
proto)
LOG(L_CRIT, "BUG: get_out_socket can only be called for UDP\n");
return 0;
}
-
+retry:
switch(to->s.sa_family){
case AF_INET : {
- if(sock_inet <= 0){
+ if(sock_inet < 0){
sock_inet = socket(AF_INET, SOCK_DGRAM, 0);
if (sock_inet==-1) {
LM_ERR("socket() failed: %s\n", strerror(errno));
return 0;
}
}
- temp_sock = sock_inet;
+ temp_sock = &sock_inet;
break;
}
+#ifdef USE_IPV6
case AF_INET6 : {
- if(sock_inet6 <= 0){
+ if(sock_inet6 < 0){
sock_inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock_inet6==-1) {
LM_ERR("socket() failed: %s\n", strerror(errno));
return 0;
}
}
- temp_sock = sock_inet6;
+ temp_sock = &sock_inet6;
break;
- }
+ }
+#endif /* USE_IPV6 */
default: {
- LM_ERR("Unknow protocol family \n");
+ LM_ERR("Unknown protocol family \n");
return 0;
}
}
- if (connect(temp_sock, &to->s, sockaddru_len(*to))==-1) {
+ if (connect(*temp_sock, &to->s, sockaddru_len(*to))==-1) {
+ if (errno==EISCONN && !mhomed_sock_cache_disabled){
+ /* no multiple connects support on the same socket */
+ mhomed_sock_cache_disabled=1;
+ sock_inet=-1;
+#ifdef USE_IPV6
+ sock_inet6=-1;
+#endif /* USE_IPV6 */
+ goto retry;
+ }
LOG(L_ERR, "ERROR: get_out_socket: connect failed: %s\n",
strerror(errno));
- sock_inet = 0;
- sock_inet6 = 0;
goto error;
}
len=sizeof(from);
- if (getsockname(temp_sock, &from.s, &len)==-1) {
+ if (getsockname(*temp_sock, &from.s, &len)==-1) {
LOG(L_ERR, "ERROR: get_out_socket: getsockname failed: %s\n",
strerror(errno));
- sock_inet = 0;
- sock_inet6 = 0;
goto error;
}
su2ip_addr(&ip, &from);
si=find_si(&ip, 0, proto);
if (si==0) goto error;
DBG("DEBUG: get_out_socket: socket determined: %p\n", si );
+ if (unlikely(mhomed_sock_cache_disabled)){
+ close(*temp_sock);
+ *temp_sock=-1;
+ }
return si;
error:
LOG(L_ERR, "ERROR: get_out_socket: no socket found\n");
+ if (unlikely(*temp_sock >=0 && mhomed_sock_cache_disabled)){
+ close(*temp_sock);
+ *temp_sock=-1;
+ }
return 0;
}