Mercurial > pidgin
comparison src/network.c @ 12909:8e3b85fe4a55
[gaim-migrate @ 15262]
Make UPnP truly asynchronous.
There are probably still a couple socket calls that should be made nonblocking, but I wanted to commit this before it became even bigger. This contains a number of probable leak fixes in the upnp stuff.
The UPnP stuff has been updated to use gaim_url_fetch_request() instead of the specific implementation.
To make this all work, I had to make gaim_network_listen() and gaim_network_listen_range() also asynchronous - seems to work just fine apart from the UPnP calls seeming to take longer than they should (I'm planning to look into this).
I also triggered a STUN and UPnP discovery on startup so that we hopefully have the information when we need it.
committer: Tailor Script <tailor@pidgin.im>
author | Daniel Atallah <daniel.atallah@gmail.com> |
---|---|
date | Tue, 17 Jan 2006 05:48:51 +0000 |
parents | d5b8f4dc1622 |
children | a85c8c11bfab |
comparison
equal
deleted
inserted
replaced
12908:4f2b96f23700 | 12909:8e3b85fe4a55 |
---|---|
30 #include "network.h" | 30 #include "network.h" |
31 #include "prefs.h" | 31 #include "prefs.h" |
32 #include "stun.h" | 32 #include "stun.h" |
33 #include "upnp.h" | 33 #include "upnp.h" |
34 | 34 |
35 typedef struct { | |
36 int listenfd; | |
37 int socket_type; | |
38 gboolean retry; | |
39 gboolean adding; | |
40 GaimNetworkListenCallback cb; | |
41 gpointer cb_data; | |
42 } ListenUPnPData; | |
35 | 43 |
36 const unsigned char * | 44 const unsigned char * |
37 gaim_network_ip_atoi(const char *ip) | 45 gaim_network_ip_atoi(const char *ip) |
38 { | 46 { |
39 static unsigned char ret[4]; | 47 static unsigned char ret[4]; |
141 | 149 |
142 const char * | 150 const char * |
143 gaim_network_get_my_ip(int fd) | 151 gaim_network_get_my_ip(int fd) |
144 { | 152 { |
145 const char *ip = NULL; | 153 const char *ip = NULL; |
146 GaimUPnPControlInfo* controlInfo = NULL; | |
147 GaimStunNatDiscovery *stun; | 154 GaimStunNatDiscovery *stun; |
148 | 155 |
149 /* Check if the user specified an IP manually */ | 156 /* Check if the user specified an IP manually */ |
150 if (!gaim_prefs_get_bool("/core/network/auto_ip")) { | 157 if (!gaim_prefs_get_bool("/core/network/auto_ip")) { |
151 ip = gaim_network_get_public_ip(); | 158 ip = gaim_network_get_public_ip(); |
156 if (ip == NULL || *ip == '\0') { | 163 if (ip == NULL || *ip == '\0') { |
157 /* Check if STUN discovery was already done */ | 164 /* Check if STUN discovery was already done */ |
158 stun = gaim_stun_discover(NULL); | 165 stun = gaim_stun_discover(NULL); |
159 if (stun != NULL && stun->status == GAIM_STUN_STATUS_DISCOVERED) | 166 if (stun != NULL && stun->status == GAIM_STUN_STATUS_DISCOVERED) |
160 return stun->publicip; | 167 return stun->publicip; |
161 } | 168 |
162 | 169 /* attempt to get the ip from a NAT device */ |
163 | 170 ip = gaim_upnp_get_public_ip(); |
164 /* attempt to get the ip from a NAT device */ | |
165 if ((controlInfo = gaim_upnp_discover()) != NULL) { | |
166 ip = gaim_upnp_get_public_ip(controlInfo); | |
167 | |
168 g_free(controlInfo->controlURL); | |
169 g_free(controlInfo->serviceType); | |
170 g_free(controlInfo); | |
171 | 171 |
172 if (ip != NULL) | 172 if (ip != NULL) |
173 return ip; | 173 return ip; |
174 } | 174 } |
175 | 175 |
176 /* Just fetch the IP of the local system */ | 176 /* Just fetch the IP of the local system */ |
177 return gaim_network_get_local_system_ip(fd); | 177 return gaim_network_get_local_system_ip(fd); |
178 } | 178 } |
179 | 179 |
180 | 180 |
181 static int | 181 static void |
182 gaim_network_do_listen(unsigned short port, int socket_type) | 182 gaim_network_set_upnp_port_mapping_cb(gboolean success, gpointer data) |
183 { | |
184 ListenUPnPData *ldata = data; | |
185 | |
186 if (!success) { | |
187 gaim_debug_warning("network", "Couldn't create UPnP mapping for...\n"); | |
188 if (ldata->retry) { | |
189 ldata->retry = FALSE; | |
190 ldata->adding = FALSE; | |
191 gaim_upnp_remove_port_mapping( | |
192 gaim_network_get_port_from_fd(ldata->listenfd), | |
193 (ldata->socket_type == SOCK_STREAM) ? "TCP" : "UDP", | |
194 gaim_network_set_upnp_port_mapping_cb, ldata); | |
195 return; | |
196 } | |
197 } else if (!ldata->adding) { | |
198 /* We've tried successfully to remove the port mapping. | |
199 * Try to add it again */ | |
200 ldata->adding = TRUE; | |
201 gaim_upnp_set_port_mapping( | |
202 gaim_network_get_port_from_fd(ldata->listenfd), | |
203 (ldata->socket_type == SOCK_STREAM) ? "TCP" : "UDP", | |
204 gaim_network_set_upnp_port_mapping_cb, ldata); | |
205 return; | |
206 } | |
207 | |
208 if (ldata->cb) | |
209 ldata->cb(ldata->listenfd, ldata->cb_data); | |
210 | |
211 g_free(ldata); | |
212 } | |
213 | |
214 | |
215 static gboolean | |
216 gaim_network_do_listen(unsigned short port, int socket_type, GaimNetworkListenCallback cb, gpointer cb_data) | |
183 { | 217 { |
184 int listenfd = -1; | 218 int listenfd = -1; |
185 const int on = 1; | 219 const int on = 1; |
186 GaimUPnPControlInfo* controlInfo = NULL; | |
187 #if HAVE_GETADDRINFO | 220 #if HAVE_GETADDRINFO |
188 int errnum; | 221 int errnum; |
189 struct addrinfo hints, *res, *next; | 222 struct addrinfo hints, *res, *next; |
190 char serv[6]; | 223 char serv[6]; |
224 ListenUPnPData *ld; | |
191 | 225 |
192 /* | 226 /* |
193 * Get a list of addresses on this machine. | 227 * Get a list of addresses on this machine. |
194 */ | 228 */ |
195 snprintf(serv, sizeof(serv), "%hu", port); | 229 snprintf(serv, sizeof(serv), "%hu", port); |
202 #ifndef _WIN32 | 236 #ifndef _WIN32 |
203 gaim_debug_warning("network", "getaddrinfo: %s\n", gai_strerror(errnum)); | 237 gaim_debug_warning("network", "getaddrinfo: %s\n", gai_strerror(errnum)); |
204 if (errnum == EAI_SYSTEM) | 238 if (errnum == EAI_SYSTEM) |
205 gaim_debug_warning("network", "getaddrinfo: system error: %s\n", strerror(errno)); | 239 gaim_debug_warning("network", "getaddrinfo: system error: %s\n", strerror(errno)); |
206 #else | 240 #else |
207 gaim_debug_warning("network", "getaddrinfo: Error Code = %d\n", errnum); | 241 gaim_debug_warning("network", "getaddrinfo: Error Code = %d\n", errnum); |
208 #endif | 242 #endif |
209 return -1; | 243 return FALSE; |
210 } | 244 } |
211 | 245 |
212 /* | 246 /* |
213 * Go through the list of addresses and attempt to listen on | 247 * Go through the list of addresses and attempt to listen on |
214 * one of them. | 248 * one of them. |
228 } | 262 } |
229 | 263 |
230 freeaddrinfo(res); | 264 freeaddrinfo(res); |
231 | 265 |
232 if (next == NULL) | 266 if (next == NULL) |
233 return -1; | 267 return FALSE; |
234 #else | 268 #else |
235 struct sockaddr_in sockin; | 269 struct sockaddr_in sockin; |
236 | 270 |
237 if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) { | 271 if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) { |
238 gaim_debug_warning("network", "socket: %s\n", strerror(errno)); | 272 gaim_debug_warning("network", "socket: %s\n", strerror(errno)); |
239 return -1; | 273 return FALSE; |
240 } | 274 } |
241 | 275 |
242 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) | 276 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) |
243 gaim_debug_warning("network", "setsockopt: %s\n", strerror(errno)); | 277 gaim_debug_warning("network", "setsockopt: %s\n", strerror(errno)); |
244 | 278 |
247 sockin.sin_port = htons(port); | 281 sockin.sin_port = htons(port); |
248 | 282 |
249 if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) { | 283 if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) { |
250 gaim_debug_warning("network", "bind: %s\n", strerror(errno)); | 284 gaim_debug_warning("network", "bind: %s\n", strerror(errno)); |
251 close(listenfd); | 285 close(listenfd); |
252 return -1; | 286 return FALSE; |
253 } | 287 } |
254 #endif | 288 #endif |
255 | 289 |
256 if (socket_type == SOCK_STREAM && listen(listenfd, 4) != 0) { | 290 if (socket_type == SOCK_STREAM && listen(listenfd, 4) != 0) { |
257 gaim_debug_warning("network", "listen: %s\n", strerror(errno)); | 291 gaim_debug_warning("network", "listen: %s\n", strerror(errno)); |
258 close(listenfd); | 292 close(listenfd); |
259 return -1; | 293 return FALSE; |
260 } | 294 } |
261 fcntl(listenfd, F_SETFL, O_NONBLOCK); | 295 fcntl(listenfd, F_SETFL, O_NONBLOCK); |
262 | 296 |
263 if ((controlInfo = gaim_upnp_discover()) != NULL) { | |
264 char *type_desc = (socket_type == SOCK_STREAM) ? "TCP" : "UDP"; | |
265 if (!gaim_upnp_set_port_mapping(controlInfo, | |
266 gaim_network_get_port_from_fd(listenfd), | |
267 type_desc)) { | |
268 gaim_upnp_remove_port_mapping(controlInfo, | |
269 gaim_network_get_port_from_fd(listenfd), | |
270 type_desc); | |
271 gaim_upnp_set_port_mapping(controlInfo, | |
272 gaim_network_get_port_from_fd(listenfd), | |
273 type_desc); | |
274 | |
275 } | |
276 g_free(controlInfo->serviceType); | |
277 g_free(controlInfo->controlURL); | |
278 g_free(controlInfo); | |
279 } | |
280 | |
281 gaim_debug_info("network", "Listening on port: %hu\n", gaim_network_get_port_from_fd(listenfd)); | 297 gaim_debug_info("network", "Listening on port: %hu\n", gaim_network_get_port_from_fd(listenfd)); |
282 return listenfd; | 298 |
283 } | 299 ld = g_new0(ListenUPnPData, 1); |
284 | 300 ld->listenfd = listenfd; |
285 int | 301 ld->adding = TRUE; |
286 gaim_network_listen(unsigned short port, int socket_type) | 302 ld->retry = TRUE; |
303 ld->cb = cb; | |
304 ld->cb_data = cb_data; | |
305 | |
306 gaim_upnp_set_port_mapping( | |
307 gaim_network_get_port_from_fd(listenfd), | |
308 (socket_type == SOCK_STREAM) ? "TCP" : "UDP", | |
309 gaim_network_set_upnp_port_mapping_cb, ld); | |
310 | |
311 return TRUE; | |
312 } | |
313 | |
314 gboolean | |
315 gaim_network_listen(unsigned short port, int socket_type, | |
316 GaimNetworkListenCallback cb, gpointer cb_data) | |
287 { | 317 { |
288 g_return_val_if_fail(port != 0, -1); | 318 g_return_val_if_fail(port != 0, -1); |
289 | 319 |
290 return gaim_network_do_listen(port, socket_type); | 320 return gaim_network_do_listen(port, socket_type, cb, cb_data); |
291 } | 321 } |
292 | 322 |
293 int | 323 gboolean |
294 gaim_network_listen_range(unsigned short start, unsigned short end, | 324 gaim_network_listen_range(unsigned short start, unsigned short end, |
295 int socket_type) | 325 int socket_type, GaimNetworkListenCallback cb, gpointer cb_data) |
296 { | 326 { |
297 int ret = -1; | 327 gboolean ret = FALSE; |
298 | 328 |
299 if (gaim_prefs_get_bool("/core/network/ports_range_use")) { | 329 if (gaim_prefs_get_bool("/core/network/ports_range_use")) { |
300 start = gaim_prefs_get_int("/core/network/ports_range_start"); | 330 start = gaim_prefs_get_int("/core/network/ports_range_start"); |
301 end = gaim_prefs_get_int("/core/network/ports_range_end"); | 331 end = gaim_prefs_get_int("/core/network/ports_range_end"); |
302 } else { | 332 } else { |
303 if (end < start) | 333 if (end < start) |
304 end = start; | 334 end = start; |
305 } | 335 } |
306 | 336 |
307 for (; start <= end; start++) { | 337 for (; start <= end; start++) { |
308 ret = gaim_network_do_listen(start, socket_type); | 338 ret = gaim_network_do_listen(start, socket_type, cb, cb_data); |
309 if (ret >= 0) | 339 if (ret) |
310 break; | 340 break; |
311 } | 341 } |
312 | 342 |
313 return ret; | 343 return ret; |
314 } | 344 } |
337 gaim_prefs_add_bool ("/core/network/auto_ip", TRUE); | 367 gaim_prefs_add_bool ("/core/network/auto_ip", TRUE); |
338 gaim_prefs_add_string("/core/network/public_ip", ""); | 368 gaim_prefs_add_string("/core/network/public_ip", ""); |
339 gaim_prefs_add_bool ("/core/network/ports_range_use", FALSE); | 369 gaim_prefs_add_bool ("/core/network/ports_range_use", FALSE); |
340 gaim_prefs_add_int ("/core/network/ports_range_start", 1024); | 370 gaim_prefs_add_int ("/core/network/ports_range_start", 1024); |
341 gaim_prefs_add_int ("/core/network/ports_range_end", 2048); | 371 gaim_prefs_add_int ("/core/network/ports_range_end", 2048); |
342 } | 372 |
373 gaim_upnp_discover(NULL, NULL); | |
374 } |