Browse code

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.

Andrei Pelinescu-Onciul authored on 01/02/2010 19:15:27
Showing 1 changed files
... ...
@@ -115,12 +115,15 @@
115 115
  * multihomed hosts
116 116
  */
117 117
 
118
-static int sock_inet = 0;
119
-static int sock_inet6 = 0;
118
+static int mhomed_sock_cache_disabled = 0;
119
+static int sock_inet = -1;
120
+#ifdef USE_IPV6
121
+static int sock_inet6 = -1;
122
+#endif /* USE_IPV6 */
120 123
 
121 124
 struct socket_info* get_out_socket(union sockaddr_union* to, int proto)
122 125
 {
123
-	int temp_sock;
126
+	int* temp_sock;
124 127
 	socklen_t len;
125 128
 	union sockaddr_union from; 
126 129
 	struct socket_info* si;
... ...
@@ -130,57 +133,72 @@ struct socket_info* get_out_socket(union sockaddr_union* to, int proto)
130 130
 		LOG(L_CRIT, "BUG: get_out_socket can only be called for UDP\n");
131 131
 		return 0;
132 132
 	}
133
-
133
+retry:
134 134
 	switch(to->s.sa_family){
135 135
 	case AF_INET : {
136
-		if(sock_inet <= 0){
136
+		if(sock_inet < 0){
137 137
 			sock_inet = socket(AF_INET, SOCK_DGRAM, 0);
138 138
 			if (sock_inet==-1) {
139 139
 				LM_ERR("socket() failed: %s\n", strerror(errno));
140 140
 				return 0;
141 141
 			}
142 142
 		}
143
-		temp_sock = sock_inet;
143
+		temp_sock = &sock_inet;
144 144
 		break;
145 145
 	}
146
+#ifdef USE_IPV6
146 147
 	case AF_INET6 : {
147
-		if(sock_inet6 <= 0){
148
+		if(sock_inet6 < 0){
148 149
 			sock_inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
149 150
 			if (sock_inet6==-1) {
150 151
 				LM_ERR("socket() failed: %s\n", strerror(errno));
151 152
 				return 0;
152 153
 			}
153 154
 		}
154
-		temp_sock = sock_inet6;
155
+		temp_sock = &sock_inet6;
155 156
 		break;
156
- 	}
157
+	}
158
+#endif /* USE_IPV6 */
157 159
 	default: {
158
-		LM_ERR("Unknow protocol family \n");
160
+		LM_ERR("Unknown protocol family \n");
159 161
 		return 0;
160 162
 	}
161 163
 	}
162
-	if (connect(temp_sock, &to->s, sockaddru_len(*to))==-1) {
164
+	if (connect(*temp_sock, &to->s, sockaddru_len(*to))==-1) {
165
+		if (errno==EISCONN && !mhomed_sock_cache_disabled){
166
+			/*  no multiple connects support on the same socket */
167
+			mhomed_sock_cache_disabled=1;
168
+			sock_inet=-1;
169
+#ifdef USE_IPV6
170
+			sock_inet6=-1;
171
+#endif /* USE_IPV6 */
172
+			goto retry;
173
+		}
163 174
 		LOG(L_ERR, "ERROR: get_out_socket: connect failed: %s\n",
164 175
 				strerror(errno));
165
-		sock_inet = 0;
166
-		sock_inet6 = 0;
167 176
 		goto error;
168 177
 	}
169 178
 	len=sizeof(from);
170
-	if (getsockname(temp_sock, &from.s, &len)==-1) {
179
+	if (getsockname(*temp_sock, &from.s, &len)==-1) {
171 180
 		LOG(L_ERR, "ERROR: get_out_socket: getsockname failed: %s\n",
172 181
 				strerror(errno));
173
-		sock_inet = 0;
174
-		sock_inet6 = 0;
175 182
 		goto error;
176 183
 	}
177 184
 	su2ip_addr(&ip, &from);
178 185
 	si=find_si(&ip, 0, proto);
179 186
 	if (si==0) goto error;
180 187
 	DBG("DEBUG: get_out_socket: socket determined: %p\n", si );
188
+	if (unlikely(mhomed_sock_cache_disabled)){
189
+		close(*temp_sock);
190
+		*temp_sock=-1;
191
+	}
181 192
 	return si;
182 193
 error:
183 194
 	LOG(L_ERR, "ERROR: get_out_socket: no socket found\n");
195
+	if (unlikely(*temp_sock >=0 && mhomed_sock_cache_disabled)){
196
+		close(*temp_sock);
197
+		*temp_sock=-1;
198
+	}
184 199
 	return 0;
185 200
 }
186 201