7.12.07

Binding Connections to Multiple Interfaces

SO_BINDTODEVICE
curl_setopt($conn_ch1, CURLOPT_INTERFACE, $AA_RANDOM_INTERFACE);



217 static CURLcode bindlocal(struct connectdata *conn,
218 curl_socket_t sockfd)
219 {
220 #ifdef ENABLE_IPV6
221 char ipv6_addr[16];
222 #endif
223 struct SessionHandle *data = conn->data;
224 struct sockaddr_in me;
225 struct sockaddr *sock = NULL; /* bind to this address */
226 socklen_t socksize; /* size of the data sock points to */
227 unsigned short port = data->set.localport; /* use this port number, 0 for
228 "random" */
229 /* how many port numbers to try to bind to, increasing one at a time */
230 int portnum = data->set.localportrange;
231
232 /*************************************************************
233 * Select device to bind socket to
234 *************************************************************/
235 if (data->set.device && (strlen(data->set.device)<255) ) {
236 struct Curl_dns_entry *h=NULL;
237 char myhost[256] = "";
238 in_addr_t in;
239 int rc;
240 bool was_iface = FALSE;
241 int in6 = -1;
242
243 /* First check if the given name is an IP address */
244 in=inet_addr(data->set.device);
245
246 if((in == CURL_INADDR_NONE) &&
247 Curl_if2ip(data->set.device, myhost, sizeof(myhost))) {
248 /*
249 * We now have the numerical IPv4-style x.y.z.w in the 'myhost' buffer
250 */
251 rc = Curl_resolv(conn, myhost, 0, &h);
252 if(rc == CURLRESOLV_PENDING)
253 (void)Curl_wait_for_resolv(conn, &h);
254
255 if(h) {
256 was_iface = TRUE;
257 Curl_resolv_unlock(data, h);
258 }
259 }
260
261 if(!was_iface) {
262 /*
263 * This was not an interface, resolve the name as a host name
264 * or IP number
265 */
266 rc = Curl_resolv(conn, data->set.device, 0, &h);
267 if(rc == CURLRESOLV_PENDING)
268 (void)Curl_wait_for_resolv(conn, &h);
269
270 if(h) {
271 if(in == CURL_INADDR_NONE)
272 /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
273 Curl_inet_ntop(h->addr->ai_addr->sa_family,
274 &((struct sockaddr_in*)h->addr->ai_addr)->sin_addr,
275 myhost, sizeof myhost);
276 else
277 /* we know data->set.device is shorter than the myhost array */
278 strcpy(myhost, data->set.device);
279 Curl_resolv_unlock(data, h);
280 }
281 }
282
283 if(! *myhost) {
284 /* need to fix this
285 h=Curl_gethost(data,
286 getmyhost(*myhost,sizeof(myhost)),
287 hostent_buf,
288 sizeof(hostent_buf));
289 */
290 failf(data, "Couldn't bind to '%s'", data->set.device);
291 return CURLE_HTTP_PORT_FAILED;
292 }
293
294 infof(data, "Bind local address to %s\n", myhost);
295
296 #ifdef SO_BINDTODEVICE
297 /* I am not sure any other OSs than Linux that provide this feature, and
298 * at the least I cannot test. --Ben
299 *
300 * This feature allows one to tightly bind the local socket to a
301 * particular interface. This will force even requests to other local
302 * interfaces to go out the external interface.
303 *
304 */
305 if (was_iface) {
306 /* Only bind to the interface when specified as interface, not just as a
307 * hostname or ip address.
308 */
309 if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
310 data->set.device, strlen(data->set.device)+1) != 0) {
311 /* printf("Failed to BINDTODEVICE, socket: %d device: %s error: %s\n",
312 sockfd, data->set.device, Curl_strerror(SOCKERRNO)); */
313 infof(data, "SO_BINDTODEVICE %s failed\n",
314 data->set.device);
315 /* This is typically "errno 1, error: Operation not permitted" if
316 you're not running as root or another suitable privileged user */
317 }
318 }
319 #endif
320
321 in=inet_addr(myhost);
322
323 #ifdef ENABLE_IPV6
324 in6 = Curl_inet_pton (AF_INET6, myhost, (void *)&ipv6_addr);
325 #endif
326 if (CURL_INADDR_NONE == in && -1 == in6) {
327 failf(data,"couldn't find my own IP address (%s)", myhost);
328 return CURLE_HTTP_PORT_FAILED;
329 } /* end of inet_addr */
330
331 if ( h ) {
332 Curl_addrinfo *addr = h->addr;
333 sock = addr->ai_addr;
334 socksize = addr->ai_addrlen;
335 }
336 else
337 return CURLE_HTTP_PORT_FAILED;
338
339 }
340 else if(port) {
341 /* if a local port number is requested but no local IP, extract the
342 address from the socket */
343 memset(&me, 0, sizeof(struct sockaddr));
344 me.sin_family = AF_INET;
345 me.sin_addr.s_addr = INADDR_ANY;
346
347 sock = (struct sockaddr *)&me;
348 socksize = sizeof(struct sockaddr);
349
350 }
351 else
352 /* no local kind of binding was requested */
353 return CURLE_OK;
354
355 do {
356
357 /* Set port number to bind to, 0 makes the system pick one */
358 if(sock->sa_family == AF_INET)
359 ((struct sockaddr_in *)sock)->sin_port = htons(port);
360 #ifdef ENABLE_IPV6
361 else
362 ((struct sockaddr_in6 *)sock)->sin6_port = htons(port);
363 #endif
364
365 if( bind(sockfd, sock, socksize) >= 0) {
366 /* we succeeded to bind */
367 struct Curl_sockaddr_storage add;
368 socklen_t size;
369
370 size = sizeof(add);
371 if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
372 failf(data, "getsockname() failed");
373 return CURLE_HTTP_PORT_FAILED;
374 }
375 /* We re-use/clobber the port variable here below */
376 if(((struct sockaddr *)&add)->sa_family == AF_INET)
377 port = ntohs(((struct sockaddr_in *)&add)->sin_port);
378 #ifdef ENABLE_IPV6
379 else
380 port = ntohs(((struct sockaddr_in6 *)&add)->sin6_port);
381 #endif
382 infof(data, "Local port: %d\n", port);
383 return CURLE_OK;
384 }
385 if(--portnum > 0) {
386 infof(data, "Bind to local port %d failed, trying next\n", port);
387 port++; /* try next port */
388 }
389 else
390 break;
391 } while(1);
392
393 data->state.os_errno = SOCKERRNO;
394 failf(data, "bind failure: %s",
395 Curl_strerror(conn, data->state.os_errno));
396 return CURLE_HTTP_PORT_FAILED;
397
398 }

1 comment: