8231
|
1 /**
|
|
2 * @file network.c Network Implementation
|
|
3 * @ingroup core
|
|
4 *
|
|
5 * gaim
|
|
6 *
|
|
7 * Gaim is the legal property of its developers, whose names are too numerous
|
|
8 * to list here. Please refer to the COPYRIGHT file distributed with this
|
|
9 * source distribution.
|
|
10 *
|
|
11 * This program is free software; you can redistribute it and/or modify
|
|
12 * it under the terms of the GNU General Public License as published by
|
|
13 * the Free Software Foundation; either version 2 of the License, or
|
|
14 * (at your option) any later version.
|
|
15 *
|
|
16 * This program is distributed in the hope that it will be useful,
|
|
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19 * GNU General Public License for more details.
|
|
20 *
|
|
21 * You should have received a copy of the GNU General Public License
|
|
22 * along with this program; if not, write to the Free Software
|
|
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
24 */
|
|
25
|
8245
|
26 #include "internal.h"
|
8231
|
27
|
|
28 #include "debug.h"
|
|
29 #include "account.h"
|
|
30 #include "network.h"
|
|
31 #include "prefs.h"
|
11411
|
32 #include "stun.h"
|
11195
|
33 #include "upnp.h"
|
8231
|
34
|
11391
|
35
|
8838
|
36 const unsigned char *
|
|
37 gaim_network_ip_atoi(const char *ip)
|
|
38 {
|
|
39 static unsigned char ret[4];
|
8981
|
40 gchar *delimiter = ".";
|
8838
|
41 gchar **split;
|
|
42 int i;
|
|
43
|
|
44 g_return_val_if_fail(ip != NULL, NULL);
|
|
45
|
8981
|
46 split = g_strsplit(ip, delimiter, 4);
|
8838
|
47 for (i = 0; split[i] != NULL; i++)
|
|
48 ret[i] = atoi(split[i]);
|
|
49 g_strfreev(split);
|
|
50
|
|
51 /* i should always be 4 */
|
|
52 if (i != 4)
|
|
53 return NULL;
|
|
54
|
|
55 return ret;
|
|
56 }
|
|
57
|
8231
|
58 void
|
8834
|
59 gaim_network_set_public_ip(const char *ip)
|
8231
|
60 {
|
|
61 g_return_if_fail(ip != NULL);
|
|
62
|
8838
|
63 /* XXX - Ensure the IP address is valid */
|
|
64
|
8231
|
65 gaim_prefs_set_string("/core/network/public_ip", ip);
|
|
66 }
|
|
67
|
|
68 const char *
|
8834
|
69 gaim_network_get_public_ip(void)
|
8231
|
70 {
|
|
71 const char *ip;
|
11411
|
72 struct stun_nattype *stun;
|
11391
|
73
|
8231
|
74 ip = gaim_prefs_get_string("/core/network/public_ip");
|
|
75
|
11411
|
76 if (ip == NULL || *ip == '\0') {
|
|
77 /* Check if STUN discovery was already done */
|
|
78 stun = gaim_stun_discover(NULL);
|
|
79 if(stun && stun->status>1)
|
|
80 return stun->publicip;
|
8231
|
81 return NULL;
|
11411
|
82 }
|
8231
|
83
|
|
84 return ip;
|
|
85 }
|
|
86
|
|
87 static const char *
|
|
88 gaim_network_get_local_ip_from_fd(int fd)
|
|
89 {
|
|
90 struct sockaddr_in addr;
|
|
91 socklen_t len;
|
|
92 static char ip[16];
|
|
93 const char *tmp;
|
|
94
|
8840
|
95 g_return_val_if_fail(fd >= 0, NULL);
|
8231
|
96
|
|
97 len = sizeof(addr);
|
|
98 if (getsockname(fd, (struct sockaddr *) &addr, &len) == -1) {
|
|
99 gaim_debug_warning("network", "getsockname: %s\n", strerror(errno));
|
|
100 return NULL;
|
|
101 }
|
|
102
|
|
103 tmp = inet_ntoa(addr.sin_addr);
|
|
104 strncpy(ip, tmp, sizeof(ip));
|
8838
|
105
|
8231
|
106 return ip;
|
|
107 }
|
|
108
|
|
109 const char *
|
|
110 gaim_network_get_local_system_ip(int fd)
|
|
111 {
|
|
112 struct hostent *host;
|
|
113 char localhost[129];
|
|
114 long unsigned add;
|
|
115 static char ip[46];
|
|
116 const char *tmp = NULL;
|
|
117
|
8840
|
118 if (fd >= 0)
|
8231
|
119 tmp = gaim_network_get_local_ip_from_fd(fd);
|
|
120
|
|
121 if (tmp)
|
|
122 return tmp;
|
|
123
|
|
124 if (gethostname(localhost, 128) < 0)
|
|
125 return NULL;
|
|
126
|
|
127 if ((host = gethostbyname(localhost)) == NULL)
|
|
128 return NULL;
|
|
129
|
|
130 memcpy(&add, host->h_addr_list[0], 4);
|
|
131 add = htonl(add);
|
|
132
|
|
133 g_snprintf(ip, 16, "%lu.%lu.%lu.%lu",
|
|
134 ((add >> 24) & 255),
|
|
135 ((add >> 16) & 255),
|
|
136 ((add >> 8) & 255),
|
|
137 add & 255);
|
|
138
|
|
139 return ip;
|
|
140 }
|
|
141
|
|
142 const char *
|
8838
|
143 gaim_network_get_my_ip(int fd)
|
8231
|
144 {
|
11215
|
145 const char *ip = NULL;
|
11391
|
146 GaimUPnPControlInfo* controlInfo = NULL;
|
11424
|
147 struct stun_nattype *stun;
|
8834
|
148
|
|
149 /* Check if the user specified an IP manually */
|
|
150 if (!gaim_prefs_get_bool("/core/network/auto_ip")) {
|
|
151 ip = gaim_network_get_public_ip();
|
|
152 if (ip != NULL)
|
|
153 return ip;
|
|
154 }
|
|
155
|
11424
|
156 if (ip == NULL || *ip == '\0') {
|
|
157 /* Check if STUN discovery was already done */
|
|
158 stun = gaim_stun_discover(NULL);
|
|
159 if(stun && stun->status>1)
|
|
160 return stun->publicip;
|
|
161 }
|
|
162
|
|
163
|
11195
|
164 /* attempt to get the ip from a NAT device */
|
11391
|
165 if ((controlInfo = gaim_upnp_discover()) != NULL) {
|
|
166 ip = gaim_upnp_get_public_ip(controlInfo);
|
|
167 g_free(controlInfo->controlURL);
|
|
168 g_free(controlInfo->serviceType);
|
|
169 g_free(controlInfo);
|
|
170 if (ip != NULL) {
|
11195
|
171 return ip;
|
11391
|
172 }
|
11195
|
173 }
|
|
174
|
8834
|
175 /* Just fetch the IP of the local system */
|
|
176 return gaim_network_get_local_system_ip(fd);
|
8231
|
177 }
|
|
178
|
11391
|
179
|
8834
|
180 static int
|
|
181 gaim_network_do_listen(unsigned short port)
|
8231
|
182 {
|
9452
|
183 int listenfd = -1;
|
8231
|
184 const int on = 1;
|
11391
|
185 GaimUPnPControlInfo* controlInfo = NULL;
|
9449
|
186 #if HAVE_GETADDRINFO
|
|
187 int errnum;
|
|
188 struct addrinfo hints, *res, *next;
|
9456
|
189 char serv[6];
|
8231
|
190
|
9449
|
191 /*
|
|
192 * Get a list of addresses on this machine.
|
|
193 */
|
|
194 snprintf(serv, sizeof(serv), "%hu", port);
|
8231
|
195 memset(&hints, 0, sizeof(struct addrinfo));
|
|
196 hints.ai_flags = AI_PASSIVE;
|
|
197 hints.ai_family = AF_UNSPEC;
|
|
198 hints.ai_socktype = SOCK_STREAM;
|
9449
|
199 errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res);
|
|
200 if (errnum != 0) {
|
11221
|
201 #ifndef _WIN32
|
9449
|
202 gaim_debug_warning("network", "getaddrinfo: %s\n", gai_strerror(errnum));
|
|
203 if (errnum == EAI_SYSTEM)
|
|
204 gaim_debug_warning("network", "getaddrinfo: system error: %s\n", strerror(errno));
|
11221
|
205 #else
|
|
206 gaim_debug_warning("network", "getaddrinfo: Error Code = %d\n", errnum);
|
|
207 #endif
|
8231
|
208 return -1;
|
|
209 }
|
9449
|
210
|
|
211 /*
|
|
212 * Go through the list of addresses and attempt to listen on
|
|
213 * one of them.
|
|
214 * XXX - Try IPv6 addresses first?
|
|
215 */
|
|
216 for (next = res; next != NULL; next = next->ai_next) {
|
9455
|
217 listenfd = socket(next->ai_family, next->ai_socktype, next->ai_protocol);
|
8231
|
218 if (listenfd < 0)
|
|
219 continue;
|
9449
|
220 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
|
|
221 gaim_debug_warning("network", "setsockopt: %s\n", strerror(errno));
|
9455
|
222 if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0)
|
8231
|
223 break; /* success */
|
|
224 close(listenfd);
|
9449
|
225 }
|
8231
|
226
|
9449
|
227 freeaddrinfo(res);
|
8231
|
228
|
9449
|
229 if (next == NULL)
|
|
230 return -1;
|
8231
|
231 #else
|
|
232 struct sockaddr_in sockin;
|
|
233
|
|
234 if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
|
235 gaim_debug_warning("network", "socket: %s\n", strerror(errno));
|
|
236 return -1;
|
|
237 }
|
|
238
|
9449
|
239 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
|
8231
|
240 gaim_debug_warning("network", "setsockopt: %s\n", strerror(errno));
|
|
241
|
|
242 memset(&sockin, 0, sizeof(struct sockaddr_in));
|
9449
|
243 sockin.sin_family = PF_INET;
|
8251
|
244 sockin.sin_port = htons(port);
|
8231
|
245
|
|
246 if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
|
|
247 gaim_debug_warning("network", "bind: %s\n", strerror(errno));
|
|
248 close(listenfd);
|
|
249 return -1;
|
|
250 }
|
|
251 #endif
|
|
252
|
|
253 if (listen(listenfd, 4) != 0) {
|
|
254 gaim_debug_warning("network", "listen: %s\n", strerror(errno));
|
|
255 close(listenfd);
|
|
256 return -1;
|
|
257 }
|
|
258 fcntl(listenfd, F_SETFL, O_NONBLOCK);
|
|
259
|
11391
|
260 if((controlInfo = gaim_upnp_discover()) != NULL) {
|
|
261 if(!gaim_upnp_set_port_mapping(controlInfo,
|
|
262 gaim_network_get_port_from_fd(listenfd),
|
|
263 "TCP")) {
|
|
264 gaim_upnp_remove_port_mapping(controlInfo,
|
|
265 gaim_network_get_port_from_fd(listenfd), "TCP");
|
|
266 gaim_upnp_set_port_mapping(controlInfo,
|
|
267 gaim_network_get_port_from_fd(listenfd), "TCP");
|
|
268
|
|
269 }
|
|
270 g_free(controlInfo->serviceType);
|
|
271 g_free(controlInfo->controlURL);
|
|
272 g_free(controlInfo);
|
11195
|
273 }
|
|
274
|
8231
|
275 gaim_debug_info("network", "Listening on port: %hu\n", gaim_network_get_port_from_fd(listenfd));
|
|
276 return listenfd;
|
|
277 }
|
|
278
|
8834
|
279 int
|
|
280 gaim_network_listen(unsigned short port)
|
8246
|
281 {
|
8250
|
282 g_return_val_if_fail(port != 0, -1);
|
|
283
|
8246
|
284 return gaim_network_do_listen(port);
|
|
285 }
|
|
286
|
8834
|
287 int
|
|
288 gaim_network_listen_range(unsigned short start, unsigned short end)
|
8231
|
289 {
|
8240
|
290 int ret = -1;
|
8231
|
291
|
8250
|
292 if (gaim_prefs_get_bool("/core/network/ports_range_use")) {
|
8239
|
293 start = gaim_prefs_get_int("/core/network/ports_range_start");
|
|
294 end = gaim_prefs_get_int("/core/network/ports_range_end");
|
8250
|
295 } else {
|
|
296 if (end < start)
|
|
297 end = start;
|
8239
|
298 }
|
8231
|
299
|
|
300 for (; start <= end; start++) {
|
|
301 ret = gaim_network_do_listen(start);
|
|
302 if (ret >= 0)
|
|
303 break;
|
|
304 }
|
|
305
|
|
306 return ret;
|
|
307 }
|
|
308
|
8834
|
309 unsigned short
|
|
310 gaim_network_get_port_from_fd(int fd)
|
8231
|
311 {
|
|
312 struct sockaddr_in addr;
|
|
313 socklen_t len;
|
|
314
|
9449
|
315 g_return_val_if_fail(fd >= 0, 0);
|
8231
|
316
|
|
317 len = sizeof(addr);
|
|
318 if (getsockname(fd, (struct sockaddr *) &addr, &len) == -1) {
|
|
319 gaim_debug_warning("network", "getsockname: %s\n", strerror(errno));
|
|
320 return 0;
|
|
321 }
|
|
322
|
|
323 return ntohs(addr.sin_port);
|
|
324 }
|
|
325
|
|
326 void
|
|
327 gaim_network_init(void)
|
|
328 {
|
|
329 gaim_prefs_add_none ("/core/network");
|
|
330 gaim_prefs_add_bool ("/core/network/auto_ip", TRUE);
|
|
331 gaim_prefs_add_string("/core/network/public_ip", "");
|
|
332 gaim_prefs_add_bool ("/core/network/ports_range_use", FALSE);
|
|
333 gaim_prefs_add_int ("/core/network/ports_range_start", 1024);
|
|
334 gaim_prefs_add_int ("/core/network/ports_range_end", 2048);
|
|
335 }
|