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 }