Mercurial > pidgin
comparison libpurple/nat-pmp.c @ 15855:9b875f0ecb86
nat-pmp is now functional with a compatible router. I'm not enabling this code yet because it hasn't been tested with a router which doesn't support nat-pmp.
author | Evan Schoenberg <evan.s@dreskin.net> |
---|---|
date | Thu, 22 Mar 2007 12:51:33 +0000 |
parents | 32c366eeeb99 |
children | 9cfe41743c65 |
comparison
equal
deleted
inserted
replaced
15854:5ec2374087de | 15855:9b875f0ecb86 |
---|---|
50 #include <assert.h> | 50 #include <assert.h> |
51 #include <sys/types.h> | 51 #include <sys/types.h> |
52 #include <net/if.h> | 52 #include <net/if.h> |
53 | 53 |
54 #ifdef NET_RT_DUMP2 | 54 #ifdef NET_RT_DUMP2 |
55 | |
56 #define PMP_DEBUG | |
57 | |
55 /* | 58 /* |
56 * Thanks to R. Matthew Emerson for the fixes on this | 59 * Thanks to R. Matthew Emerson for the fixes on this |
57 */ | 60 */ |
58 | 61 |
62 #define PMP_MAP_OPCODE_UDP 1 | |
63 #define PMP_MAP_OPCODE_TCP 2 | |
64 | |
65 #define PMP_VERSION 0 | |
66 #define PMP_PORT 5351 | |
67 #define PMP_TIMEOUT 250000 // 250000 useconds | |
68 | |
59 /* alignment constraint for routing socket */ | 69 /* alignment constraint for routing socket */ |
60 #define ROUNDUP(a) \ | 70 #define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) |
61 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) | 71 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) |
62 #define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len)) | |
63 | 72 |
64 static void | 73 static void |
65 get_rtaddrs(int bitmask, struct sockaddr *sa, struct sockaddr *addrs[]) | 74 get_rtaddrs(int bitmask, struct sockaddr *sa, struct sockaddr *addrs[]) |
66 { | 75 { |
67 int i; | 76 int i; |
96 return 1; | 105 return 1; |
97 else | 106 else |
98 return 0; | 107 return 0; |
99 } | 108 } |
100 | 109 |
110 /*! | |
111 * The return sockaddr_in must be g_free()'d when no longer needed | |
112 */ | |
101 static struct sockaddr_in * | 113 static struct sockaddr_in * |
102 default_gw() | 114 default_gw() |
103 { | 115 { |
104 int mib[6]; | 116 int mib[6]; |
105 size_t needed; | 117 size_t needed; |
106 char *buf, *next, *lim; | 118 char *buf, *next, *lim; |
107 struct rt_msghdr2 *rtm; | 119 struct rt_msghdr2 *rtm; |
108 struct sockaddr *sa; | 120 struct sockaddr *sa; |
109 struct sockaddr *rti_info[RTAX_MAX]; | 121 struct sockaddr_in *sin = NULL; |
110 struct sockaddr_in *sin; | 122 gboolean found = FALSE; |
111 | 123 |
112 mib[0] = CTL_NET; | 124 mib[0] = CTL_NET; |
113 mib[1] = PF_ROUTE; | 125 mib[1] = PF_ROUTE; /* entire routing table or a subset of it */ |
114 mib[2] = 0; | 126 mib[2] = 0; /* protocol number - always 0 */ |
115 mib[3] = 0; | 127 mib[3] = 0; /* address family - 0 for all addres families */ |
116 mib[4] = NET_RT_DUMP2; | 128 mib[4] = NET_RT_DUMP2; |
117 mib[5] = 0; | 129 mib[5] = 0; |
118 | 130 |
131 /* Determine the buffer side needed to get the full routing table */ | |
119 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) | 132 if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) |
120 { | 133 { |
121 err(1, "sysctl: net.route.0.0.dump estimate"); | 134 purple_debug_warning("nat-pmp", "sysctl: net.route.0.0.dump estimate"); |
135 return NULL; | |
122 } | 136 } |
123 | 137 |
124 buf = malloc(needed); | 138 if (!(buf = malloc(needed))) |
125 | 139 { |
126 if (buf == 0) | 140 purple_debug_warning("nat-pmp", "malloc"); |
127 { | 141 return NULL; |
128 err(2, "malloc"); | |
129 } | 142 } |
130 | 143 |
144 /* Read the routing table into buf */ | |
131 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) | 145 if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) |
132 { | 146 { |
133 err(1, "sysctl: net.route.0.0.dump"); | 147 purple_debug_warning("nat-pmp", "sysctl: net.route.0.0.dump"); |
148 return NULL; | |
134 } | 149 } |
135 | 150 |
136 lim = buf + needed; | 151 lim = buf + needed; |
137 | 152 |
138 for (next = buf; next < lim; next += rtm->rtm_msglen) | 153 for (next = buf; next < lim; next += rtm->rtm_msglen) |
140 rtm = (struct rt_msghdr2 *)next; | 155 rtm = (struct rt_msghdr2 *)next; |
141 sa = (struct sockaddr *)(rtm + 1); | 156 sa = (struct sockaddr *)(rtm + 1); |
142 | 157 |
143 if (sa->sa_family == AF_INET) | 158 if (sa->sa_family == AF_INET) |
144 { | 159 { |
145 sin = (struct sockaddr_in *)sa; | 160 sin = (struct sockaddr_in*) sa; |
146 struct sockaddr addr, mask; | 161 |
147 | 162 if ((rtm->rtm_flags & RTF_GATEWAY) && sin->sin_addr.s_addr == INADDR_ANY) |
148 get_rtaddrs(rtm->rtm_addrs, sa, rti_info); | |
149 bzero(&addr, sizeof(addr)); | |
150 | |
151 if (rtm->rtm_addrs & RTA_DST) | |
152 bcopy(rti_info[RTAX_DST], &addr, rti_info[RTAX_DST]->sa_len); | |
153 | |
154 bzero(&mask, sizeof(mask)); | |
155 | |
156 if (rtm->rtm_addrs & RTA_NETMASK) | |
157 bcopy(rti_info[RTAX_NETMASK], &mask, rti_info[RTAX_NETMASK]->sa_len); | |
158 | |
159 if (is_default_route(&addr, &mask)) | |
160 { | 163 { |
161 sin = (struct sockaddr_in *)rti_info[RTAX_GATEWAY]; | 164 /* We found the default route. Now get the destination address and netmask. */ |
162 break; | 165 struct sockaddr *rti_info[RTAX_MAX]; |
166 struct sockaddr addr, mask; | |
167 | |
168 get_rtaddrs(rtm->rtm_addrs, sa, rti_info); | |
169 bzero(&addr, sizeof(addr)); | |
170 | |
171 if (rtm->rtm_addrs & RTA_DST) | |
172 bcopy(rti_info[RTAX_DST], &addr, rti_info[RTAX_DST]->sa_len); | |
173 | |
174 bzero(&mask, sizeof(mask)); | |
175 | |
176 if (rtm->rtm_addrs & RTA_NETMASK) | |
177 bcopy(rti_info[RTAX_NETMASK], &mask, rti_info[RTAX_NETMASK]->sa_len); | |
178 | |
179 if (rtm->rtm_addrs & RTA_GATEWAY && | |
180 is_default_route(&addr, &mask)) | |
181 { | |
182 if (rti_info[RTAX_GATEWAY]) { | |
183 struct sockaddr_in *rti_sin = (struct sockaddr_in *)rti_info[RTAX_GATEWAY]; | |
184 sin = g_new0(struct sockaddr_in, 1); | |
185 sin->sin_family = rti_sin->sin_family; | |
186 sin->sin_port = rti_sin->sin_port; | |
187 sin->sin_addr.s_addr = rti_sin->sin_addr.s_addr; | |
188 memcpy(sin, rti_info[RTAX_GATEWAY], sizeof(struct sockaddr_in)); | |
189 | |
190 purple_debug_info("nat-pmp", "found a default gateway"); | |
191 found = TRUE; | |
192 break; | |
193 } | |
194 } | |
163 } | 195 } |
164 } | 196 } |
165 | |
166 rtm = (struct rt_msghdr2 *)next; | |
167 } | 197 } |
168 | 198 |
169 free(buf); | 199 return (found ? sin : NULL); |
170 | 200 } |
171 return sin; | 201 |
172 } | 202 /*! |
173 | 203 * double_timeout(struct timeval *) will handle doubling a timeout for backoffs required by NAT-PMP |
174 //! double_timeout(struct timeval *) will handle doubling a timeout for backoffs required by NAT-PMP | 204 */ |
175 static void | 205 static void |
176 double_timeout(struct timeval *to) | 206 double_timeout(struct timeval *to) |
177 { | 207 { |
178 int second = 1000000; // number of useconds | 208 int second = 1000000; // number of useconds |
179 | 209 |
276 | 306 |
277 iterate: | 307 iterate: |
278 ++req_attempts; | 308 ++req_attempts; |
279 double_timeout(&req_timeout); | 309 double_timeout(&req_timeout); |
280 } | 310 } |
281 | 311 |
282 if (publicsockaddr == NULL) | 312 if (publicsockaddr == NULL) { |
283 return NULL; | 313 g_free(gateway); |
314 return NULL; | |
315 } | |
284 | 316 |
285 #ifdef PMP_DEBUG | 317 #ifdef PMP_DEBUG |
286 purple_debug_info("nat-pmp", "Response received from NAT-PMP device:\n"); | 318 purple_debug_info("nat-pmp", "Response received from NAT-PMP device:\n"); |
287 purple_debug_info("nat-pmp", "version: %d\n", resp.version); | 319 purple_debug_info("nat-pmp", "version: %d\n", resp.version); |
288 purple_debug_info("nat-pmp", "opcode: %d\n", resp.opcode); | 320 purple_debug_info("nat-pmp", "opcode: %d\n", resp.opcode); |
292 in.s_addr = resp.address; | 324 in.s_addr = resp.address; |
293 purple_debug_info("nat-pmp", "address: %s\n", inet_ntoa(in)); | 325 purple_debug_info("nat-pmp", "address: %s\n", inet_ntoa(in)); |
294 #endif | 326 #endif |
295 | 327 |
296 publicsockaddr->sin_addr.s_addr = resp.address; | 328 publicsockaddr->sin_addr.s_addr = resp.address; |
297 | 329 |
330 g_free(gateway); | |
331 | |
298 return inet_ntoa(publicsockaddr->sin_addr); | 332 return inet_ntoa(publicsockaddr->sin_addr); |
299 } | 333 } |
300 | 334 |
301 /*! | 335 /*! |
302 * will return NULL on error, or a pointer to the pmp_map_response_t type | 336 * will return NULL on error, or a pointer to the pmp_map_response_t type |
303 */ | 337 */ |
304 pmp_map_response_t * | 338 pmp_map_response_t * |
305 purple_pmp_create_map(uint8_t type, uint16_t privateport, uint16_t publicport, uint32_t lifetime) | 339 purple_pmp_create_map(PurplePmpType type, uint16_t privateport, uint16_t publicport, uint32_t lifetime) |
306 { | 340 { |
307 struct sockaddr_in *gateway = default_gw(); | 341 struct sockaddr_in *gateway = default_gw(); |
308 | 342 |
309 if (gateway == NULL) | 343 if (gateway == NULL) |
310 { | 344 { |
329 | 363 |
330 // Clean out both req and resp structures | 364 // Clean out both req and resp structures |
331 bzero(&req, sizeof(pmp_map_request_t)); | 365 bzero(&req, sizeof(pmp_map_request_t)); |
332 bzero(resp, sizeof(pmp_map_response_t)); | 366 bzero(resp, sizeof(pmp_map_response_t)); |
333 req.version = 0; | 367 req.version = 0; |
334 req.opcode = type; | 368 req.opcode = ((type == PURPLE_PMP_TYPE_UDP) ? PMP_MAP_OPCODE_UDP : PMP_MAP_OPCODE_TCP); |
335 req.privateport = htons(privateport); // What a difference byte ordering makes...d'oh! | 369 req.privateport = htons(privateport); // What a difference byte ordering makes...d'oh! |
336 req.publicport = htons(publicport); | 370 req.publicport = htons(publicport); |
337 req.lifetime = htonl(lifetime); | 371 req.lifetime = htonl(lifetime); |
338 | 372 |
339 // Attempt to contact NAT-PMP device 9 times as per: draft-cheshire-nat-pmp-02.txt | 373 // Attempt to contact NAT-PMP device 9 times as per: draft-cheshire-nat-pmp-02.txt |
391 purple_debug_info("nat-pmp", "privateport: %d\n", ntohs(resp->privateport)); | 425 purple_debug_info("nat-pmp", "privateport: %d\n", ntohs(resp->privateport)); |
392 purple_debug_info("nat-pmp", "publicport: %d\n", ntohs(resp->publicport)); | 426 purple_debug_info("nat-pmp", "publicport: %d\n", ntohs(resp->publicport)); |
393 purple_debug_info("nat-pmp", "lifetime: %d\n", ntohl(resp->lifetime)); | 427 purple_debug_info("nat-pmp", "lifetime: %d\n", ntohl(resp->lifetime)); |
394 #endif | 428 #endif |
395 | 429 |
430 g_free(gateway); | |
431 | |
396 return resp; | 432 return resp; |
397 } | 433 } |
398 | 434 |
399 /*! | 435 /*! |
400 * pmp_destroy_map(uint8_t,uint16_t) | 436 * pmp_destroy_map(uint8_t,uint16_t) |
401 * will return NULL on error, or a pointer to the pmp_map_response_t type | 437 * will return NULL on error, or a pointer to the pmp_map_response_t type |
402 */ | 438 */ |
403 pmp_map_response_t * | 439 pmp_map_response_t * |
404 purple_pmp_destroy_map(uint8_t type, uint16_t privateport) | 440 purple_pmp_destroy_map(PurplePmpType type, uint16_t privateport) |
405 { | 441 { |
406 pmp_map_response_t *response = NULL; | 442 pmp_map_response_t *response; |
407 | 443 |
408 if ((response = purple_pmp_create_map(type, privateport, 0, 0)) == NULL) | 444 response = purple_pmp_create_map(((type == PURPLE_PMP_TYPE_UDP) ? PMP_MAP_OPCODE_UDP : PMP_MAP_OPCODE_TCP), |
445 privateport, 0, 0); | |
446 if (!response) | |
409 { | 447 { |
410 purple_debug_info("nat-pmp", "Failed to properly destroy mapping for %d!\n", privateport); | 448 purple_debug_info("nat-pmp", "Failed to properly destroy mapping for %d!\n", privateport); |
411 return NULL; | 449 return NULL; |
412 } | 450 } |
413 else | 451 else |
421 { | 459 { |
422 return NULL; | 460 return NULL; |
423 } | 461 } |
424 | 462 |
425 pmp_map_response_t * | 463 pmp_map_response_t * |
426 purple_pmp_create_map(uint8_t type, uint16_t privateport, uint16_t publicport, uint32_t lifetime) | 464 purple_pmp_create_map(PurplePmpType type, uint16_t privateport, uint16_t publicport, uint32_t lifetime) |
427 { | 465 { |
428 return NULL; | 466 return NULL; |
429 } | 467 } |
430 | 468 |
431 pmp_map_response_t * | 469 pmp_map_response_t * |
432 purple_pmp_destroy_map(uint8_t type, uint16_t privateport) | 470 purple_pmp_destroy_map(PurplePmpType type, uint16_t privateport) |
433 { | 471 { |
434 return NULL; | 472 return NULL; |
435 } | 473 } |
436 #endif /* #ifndef NET_RT_DUMP2 */ | 474 #endif /* #ifndef NET_RT_DUMP2 */ |