Branch: Tag:

2007-05-03

2007-05-03 15:59:30 by Martin Stjernholm <mast@lysator.liu.se>

Attempt to fix the long-standing problem with "Address already in use"
when the IPv6 ANY interfaces are bound after IPv4 ANY. My theory is
that the IPv4 ANY binding binds IPv6 too, but I haven't actually
tested it.

Rev: server/base_server/roxen.pike:1.961

6:   // Per Hedbor, Henrik Grubbström, Pontus Hagland, David Hedbor and others.   // ABS and suicide systems contributed freely by Francesco Chemolli    - constant cvs_version="$Id: roxen.pike,v 1.960 2007/04/26 15:03:55 mast Exp $"; + constant cvs_version="$Id: roxen.pike,v 1.961 2007/05/03 15:59:30 mast Exp $";      //! @appears roxen   //!
1048:   //! returned. Otherwise its call out identifier is returned, which can   //! be used with @[find_call_out] or @[remove_call_out].   { +  // FIXME: Make it possible to associate the background job with a +  // RoxenModule or Configuration, so that report_error etc can log in +  // a good place.   #ifdef DEBUG_BACKGROUND_RUN    report_debug ("background_run enqueue %s (%s) [%d jobs in queue]\n",    functionp (func) ?
1627:    }       static int retries; -  static void bind() +  static void bind (void|int ignore_eaddrinuse)    {    if (bound) return;    if (!port_obj) port_obj = Stdio.Port();
1645: Inside #if constant(System.EAFNOSUPPORT)
   error("Invalid address " + ip);    }   #endif /* System.EAFNOSUPPORT */ -  report_error(LOC_M(6, "Failed to bind %s (%s)")+"\n", -  get_url(), strerror(port_obj->errno())); - #if 0 -  werror (describe_backtrace (backtrace())); - #endif +    #if constant(System.EADDRINUSE) || constant(system.EADDRINUSE)    if (   #if constant(System.EADDRINUSE) -  (port_obj->errno() == System.EADDRINUSE) && +  (port_obj->errno() == System.EADDRINUSE)   #else /* !constant(System.EADDRINUSE) */ -  (port_obj->errno() == system.EADDRINUSE) && +  (port_obj->errno() == system.EADDRINUSE)   #endif /* constant(System.EADDRINUSE) */ -  (retries++ < 10)) { +  ) { +  if (!ignore_eaddrinuse && (retries++ < 10)) {    // We may get spurious failures on rebinding ports on some OS'es    // (eg Linux, WIN32). See [bug 3031]. -  +  report_error(LOC_M(6, "Failed to bind %s (%s)")+"\n", +  get_url(), strerror(port_obj->errno()));    report_notice(LOC_M(62, "Attempt %d. Retrying in 1 minute.")+"\n",    retries);    call_out(bind, 60);    } -  +  } +  else   #endif /* constant(System.EADDRINUSE) || constant(system.EADDRINUSE) */ -  +  { +  report_error(LOC_M(6, "Failed to bind %s (%s)")+"\n", +  get_url(), strerror(port_obj->errno())); + #if 0 +  werror (describe_backtrace (backtrace())); + #endif    } -  +  }       static array(int) get_ipv6_sequence(string partition)    {
1795:    retries = 0;    }    -  static void create( int pn, string i ) +  static void create( int pn, string i, void|int ignore_eaddrinuse )    //! Constructor. Bind to the port 'pn' ip 'i'    {    setup (pn, i); -  bind(); +  bind (ignore_eaddrinuse);    }       static string _sprintf( )
1847:    return; \    } while (0)    -  void certificates_changed(Variable|void ignored) +  void certificates_changed(Variable|void ignored, +  void|int ignore_eaddrinuse)    {    int old_cert_failure = cert_failure;   
2022:   #endif       if (!bound) { -  bind(); +  bind (ignore_eaddrinuse);    if (old_cert_failure && bound)    report_notice (LOC_M(64, "TLS port %s opened.\n"), get_url());    }
2060:    return 0;    }    -  static void bind() +  static void bind (void|int ignore_eaddrinuse)    {    // Don't bind if we don't have correct certs.    if (!ctx->certificates) return; -  ::bind(); +  ::bind (ignore_eaddrinuse);    }    -  void create(int pn, string i) +  void create(int pn, string i, void|int ignore_eaddrinuse)    {   #if constant(Crypto.Random.random_string)    ctx->random = Crypto.Random.random_string;
2079:       ::setup(pn, i);    -  certificates_changed(); +  certificates_changed (0, ignore_eaddrinuse);       // Install the change callbacks here to avoid duplicate calls    // above.
2436:    }       int failures; +  int opened_ipv4_any_port;       foreach(required_hosts, string required_host)    {
2453:       mixed err;    if (err = catch { -  m[ required_host ][ port ] = prot( port, required_host ); +  m[ required_host ][ port ] = +  prot( port, required_host, +  // Don't complain if binding IPv6 ANY fails with +  // EADDRINUSE after we've bound IPv4 ANY. At least on +  // Linux, it seems that IPv4 and IPv6 can share the +  // same interface, and in that case we're already done +  // if we've bound the IPv4 ANY. +  required_host == "::" && opened_ipv4_any_port);    }) {    failures++;    if (has_prefix(describe_error(err), "Invalid address") &&
2472:    continue;    }    +  if (!required_host) opened_ipv4_any_port = 1; +     if( !( m[ required_host ][ port ] ) )    {    m_delete( m[ required_host ], port );