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 133
 		LOG(L_CRIT, "BUG: get_out_socket can only be called for UDP\n");
131 134
 		return 0;
132 135
 	}
133
-
136
+retry:
134 137
 	switch(to->s.sa_family){
135 138
 	case AF_INET : {
136
-		if(sock_inet <= 0){
139
+		if(sock_inet < 0){
137 140
 			sock_inet = socket(AF_INET, SOCK_DGRAM, 0);
138 141
 			if (sock_inet==-1) {
139 142
 				LM_ERR("socket() failed: %s\n", strerror(errno));
140 143
 				return 0;
141 144
 			}
142 145
 		}
143
-		temp_sock = sock_inet;
146
+		temp_sock = &sock_inet;
144 147
 		break;
145 148
 	}
149
+#ifdef USE_IPV6
146 150
 	case AF_INET6 : {
147
-		if(sock_inet6 <= 0){
151
+		if(sock_inet6 < 0){
148 152
 			sock_inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
149 153
 			if (sock_inet6==-1) {
150 154
 				LM_ERR("socket() failed: %s\n", strerror(errno));
151 155
 				return 0;
152 156
 			}
153 157
 		}
154
-		temp_sock = sock_inet6;
158
+		temp_sock = &sock_inet6;
155 159
 		break;
156
- 	}
160
+	}
161
+#endif /* USE_IPV6 */
157 162
 	default: {
158
-		LM_ERR("Unknow protocol family \n");
163
+		LM_ERR("Unknown protocol family \n");
159 164
 		return 0;
160 165
 	}
161 166
 	}
162
-	if (connect(temp_sock, &to->s, sockaddru_len(*to))==-1) {
167
+	if (connect(*temp_sock, &to->s, sockaddru_len(*to))==-1) {
168
+		if (errno==EISCONN && !mhomed_sock_cache_disabled){
169
+			/*  no multiple connects support on the same socket */
170
+			mhomed_sock_cache_disabled=1;
171
+			sock_inet=-1;
172
+#ifdef USE_IPV6
173
+			sock_inet6=-1;
174
+#endif /* USE_IPV6 */
175
+			goto retry;
176
+		}
163 177
 		LOG(L_ERR, "ERROR: get_out_socket: connect failed: %s\n",
164 178
 				strerror(errno));
165
-		sock_inet = 0;
166
-		sock_inet6 = 0;
167 179
 		goto error;
168 180
 	}
169 181
 	len=sizeof(from);
170
-	if (getsockname(temp_sock, &from.s, &len)==-1) {
182
+	if (getsockname(*temp_sock, &from.s, &len)==-1) {
171 183
 		LOG(L_ERR, "ERROR: get_out_socket: getsockname failed: %s\n",
172 184
 				strerror(errno));
173
-		sock_inet = 0;
174
-		sock_inet6 = 0;
175 185
 		goto error;
176 186
 	}
177 187
 	su2ip_addr(&ip, &from);
178 188
 	si=find_si(&ip, 0, proto);
179 189
 	if (si==0) goto error;
180 190
 	DBG("DEBUG: get_out_socket: socket determined: %p\n", si );
191
+	if (unlikely(mhomed_sock_cache_disabled)){
192
+		close(*temp_sock);
193
+		*temp_sock=-1;
194
+	}
181 195
 	return si;
182 196
 error:
183 197
 	LOG(L_ERR, "ERROR: get_out_socket: no socket found\n");
198
+	if (unlikely(*temp_sock >=0 && mhomed_sock_cache_disabled)){
199
+		close(*temp_sock);
200
+		*temp_sock=-1;
201
+	}
184 202
 	return 0;
185 203
 }
186 204