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 */