Smaller nitpicks:

On Wed, Nov 30, 2022 at 09:57:18PM +0100, Gert Doering wrote:
> From: Vladislav Grishenko <themi...@yandex-team.ru>
[...]
> diff --git a/src/openvpn/init.c b/src/openvpn/init.c
> index c2154b8d..3a70748e 100644
> --- a/src/openvpn/init.c
> +++ b/src/openvpn/init.c
> @@ -350,7 +350,12 @@ management_callback_remote_cmd(void *arg, const char **p)
>          }
>          else if (!strcmp(p[1], "MOD") && p[2] && p[3])
>          {
> -            if (strlen(p[2]) < RH_HOST_LEN && strlen(p[3]) < RH_PORT_LEN)
> +            if (ce->remote_srv && ce->proto == PROTO_AUTO)
> +            {
> +                /* can't mutate --remote-srv into --remote without protocol 
> */
> +                ret = false;
> +            }
> +            else if (strlen(p[2]) < RH_HOST_LEN && strlen(p[3]) < 
> RH_PORT_LEN)
>              {
>                  struct remote_host_store *rhs = c->options.rh_store;
>                  if (!rhs)
> @@ -363,6 +368,7 @@ management_callback_remote_cmd(void *arg, const char **p)
>  
>                  ce->remote = rhs->host;
>                  ce->remote_port = rhs->port;
> +                ce->remote_srv = false;
>                  flags = CE_MAN_QUERY_REMOTE_MOD;
>                  ret = true;
>              }
> @@ -462,6 +468,23 @@ clear_remote_addrlist(struct link_socket_addr *lsa, bool 
> free)
>      lsa->current_remote = NULL;
>  }
>  
> +/*
> + * Clear the remote service list
> + */
> +static void
> +clear_remote_servlist(struct link_socket_addr *lsa, bool free)
> +{
> +    if (lsa->service_list && free)
> +    {
> +        freeservinfo(lsa->service_list);
> +    }
> +    lsa->service_list = NULL;
> +    lsa->current_service = NULL;
> +
> +    /* clear addrinfo objects as well */
> +    clear_remote_addrlist(lsa, free);
> +}
> +
>  /*
>   * Increment to next connection entry
>   */
> @@ -491,6 +514,24 @@ next_connection_entry(struct context *c)
>                  c->c1.link_socket_addr.current_remote =
>                      c->c1.link_socket_addr.current_remote->ai_next;
>              }
> +            /* Check if there is another resolved service to try for
> +             * the current connection unless persist-remote-ip was
> +             * requested and current service already has an address */
> +            else if (c->c1.link_socket_addr.current_service
> +                     && c->c1.link_socket_addr.current_service->next
> +                     && !(c->options.persist_remote_ip
> +                          && c->c1.link_socket_addr.remote_list))
> +            {
> +                c->c1.link_socket_addr.current_service =
> +                    c->c1.link_socket_addr.current_service->next;
> +
> +                /* Clear addrinfo object of the previous service */
> +                if (c->c1.link_socket_addr.remote_list)
> +                {
> +                    clear_remote_addrlist(&c->c1.link_socket_addr,
> +                                          !c->options.resolve_in_advance);
> +                }
> +            }
>              else
>              {
>                  c->options.advance_next_remote = false;
> @@ -500,20 +541,24 @@ next_connection_entry(struct context *c)
>                   */
>                  if (!c->options.persist_remote_ip)
>                  {
> -                    /* Connection entry addrinfo objects might have been
> +                    /* Connection entry addr/servinfo objects might have been
>                       * resolved earlier but the entry itself might have been
> -                     * skipped by management on the previous loop.
> -                     * If so, clear the addrinfo objects as close_instance 
> does
> +                     * skipped on the previous loop either by management or
> +                     * due inappropriate service protocol.
> +                     * Clear the addr/servinfo objects as close_instance 
> does.
>                       */
> -                    if (c->c1.link_socket_addr.remote_list)
> +                    if (c->c1.link_socket_addr.remote_list
> +                        || c->c1.link_socket_addr.service_list)
>                      {
> -                        clear_remote_addrlist(&c->c1.link_socket_addr,
> +                        clear_remote_servlist(&c->c1.link_socket_addr,
>                                                
> !c->options.resolve_in_advance);
>                      }
>  
>                      /* close_instance should have cleared the addrinfo 
> objects */
>                      ASSERT(c->c1.link_socket_addr.current_remote == NULL);
>                      ASSERT(c->c1.link_socket_addr.remote_list == NULL);
> +                    ASSERT(c->c1.link_socket_addr.current_service == NULL);
> +                    ASSERT(c->c1.link_socket_addr.service_list == NULL);
>                  }
>                  else
>                  {
> @@ -549,6 +594,12 @@ next_connection_entry(struct context *c)
>          }
>  
>          c->options.ce = *ce;
> +        if (ce_defined && c->c1.link_socket_addr.current_service)
> +        {
> +            /* map in current service */
> +            struct servinfo *si = c->c1.link_socket_addr.current_service;
> +            ce_defined = options_mutate_ce_servinfo(&c->options, si);
> +        }
>  #ifdef ENABLE_MANAGEMENT
>          if (ce_defined && management && 
> management_query_remote_enabled(management))
>          {
> @@ -3699,10 +3750,13 @@ do_close_link_socket(struct context *c)
>                 ( c->sig->source != SIG_SOURCE_HARD
>                   && ((c->c1.link_socket_addr.current_remote
>                        && c->c1.link_socket_addr.current_remote->ai_next)
> +                     || (c->c1.link_socket_addr.current_service
> +                         && c->c1.link_socket_addr.current_service->next)
>                       || c->options.no_advance))
>                 )))
>      {
> -        clear_remote_addrlist(&c->c1.link_socket_addr, 
> !c->options.resolve_in_advance);
> +        clear_remote_servlist(&c->c1.link_socket_addr,
> +                              !c->options.resolve_in_advance);
>      }
>  
>      /* Clear the remote actual address when persist_remote_ip is not in use 
> */
> @@ -4234,6 +4288,17 @@ init_instance(struct context *c, const struct env_set 
> *env, const unsigned int f
>      /* map in current connection entry */
>      next_connection_entry(c);
>  
> +    /* map in current remote service */
> +    if (c->options.ce.remote_srv)
> +    {
> +        do_resolve_service(c);
> +        if (IS_SIG(c))
> +        {
> +            goto sig;
> +        }
> +        update_options_ce_post(&c->options);
> +    }
> +
>      /* link_socket_mode allows CM_CHILD_TCP
>       * instances to inherit acceptable fds
>       * from a top-level parent */
[...]
> diff --git a/src/openvpn/options.c b/src/openvpn/options.c
> index b7b34c9c..897f1db4 100644
> --- a/src/openvpn/options.c
> +++ b/src/openvpn/options.c
> @@ -128,7 +128,9 @@ static const char usage_message[] =
>      "Tunnel Options:\n"
>      "--local host    : Local host name or ip address. Implies --bind.\n"
>      "--remote host [port] : Remote host name or ip address.\n"
> -    "--remote-random : If multiple --remote options specified, choose one 
> randomly.\n"
> +    "--remote-srv domain [service] : Perform DNS SRV remote host 
> discovery.\n"

Missing [proto]

> +    "--remote-random : If multiple --remote or --remote-srv options 
> specified,\n"
> +    "                  choose one randomly.\n"
>      "--remote-random-hostname : Add a random string to remote DNS name.\n"
>      "--mode m        : Major mode, m = 'p2p' (default, point-to-point) or 
> 'server'.\n"
>      "--proto p       : Use protocol p for communicating with peer.\n"
> @@ -161,7 +163,7 @@ static const char usage_message[] =
>      "                  up is a file containing username/password on 2 lines, 
> or\n"
>      "                  'stdin' to prompt for console.\n"
>      "--socks-proxy-retry : Retry indefinitely on Socks proxy errors.\n"
> -    "--resolv-retry n: If hostname resolve fails for --remote, retry\n"
> +    "--resolv-retry n: If hostname resolve fails for --remote or 
> --remote-srv, retry\n"
>      "                  resolve for n seconds before failing (disabled by 
> default).\n"
>      "                  Set n=\"infinite\" to retry indefinitely.\n"
>      "--float         : Allow remote to change its IP address/port, such as 
> through\n"
> @@ -1695,6 +1697,7 @@ show_connection_entry(const struct connection_entry *o)
>      SHOW_STR(local_port);
>      SHOW_STR(remote);
>      SHOW_STR(remote_port);
> +    SHOW_BOOL(remote_srv);
>      SHOW_BOOL(remote_float);
>      SHOW_BOOL(bind_defined);
>      SHOW_BOOL(bind_local);
> @@ -2246,6 +2249,56 @@ connection_entry_load_re(struct connection_entry *ce, 
> const struct remote_entry
>      {
>          ce->af = re->af;
>      }
> +    ce->remote_srv = re->remote_srv;
> +}
> +
> +/* Part of options_postprocess_verify_ce that can be used
> + * in runtime after connection entry proto/hostname change.
> + */
> +static bool
> +options_postprocess_verify_ce_proto(const struct options *options,
> +                                    const struct connection_entry *ce)
> +{
> +    int msglevel = M_WARN|M_NOPREFIX|M_OPTERR;
> +
> +    /*
> +     * Sanity check on --local, --remote, and --ifconfig
> +     */
> +
> +    if (proto_is_net(ce->proto)
> +        && string_defined_equal(ce->local, ce->remote)
> +        && string_defined_equal(ce->local_port, ce->remote_port))
> +    {
> +        msg(msglevel, "--remote and --local addresses are the same");
> +        return false;
> +    }
> +
> +    if (string_defined_equal(ce->remote, options->ifconfig_local)
> +        || string_defined_equal(ce->remote, 
> options->ifconfig_remote_netmask))
> +    {
> +        msg(msglevel, "--local and --remote addresses must be distinct from 
> --ifconfig addresses");
> +        return false;
> +    }
> +
> +    /*
> +     * Check that protocol options make sense.
> +     */
> +
> +#ifdef ENABLE_FRAGMENT
> +    if (!proto_is_udp(ce->proto) && ce->fragment)
> +    {
> +        msg(msglevel, "--fragment can only be used with --proto udp");
> +        return false;
> +    }
> +#endif
> +
> +    if ((ce->http_proxy_options) && ce->proto != PROTO_TCP_CLIENT)
> +    {
> +        msg(msglevel, "--http-proxy MUST be used in TCP Client mode (i.e. 
> --proto tcp-client)");
> +        return false;
> +    }
> +
> +    return true;
>  }
>  
>  static void
> @@ -2322,6 +2375,11 @@ options_postprocess_verify_ce(const struct options 
> *options,
>              "--proto tcp-server or --proto tcp-client");
>      }
>  
> +    if (ce->proto == PROTO_AUTO && !ce->remote_srv)
> +    {
> +        msg(M_USAGE, "--proto auto can be only used with --remote-srv");
> +    }
> +
>      if (options->lladdr && dev != DEV_TYPE_TAP)
>      {
>          msg(M_USAGE, "--lladdr can only be used in --dev tap mode");
> @@ -2335,7 +2393,9 @@ options_postprocess_verify_ce(const struct options 
> *options,
>          msg(M_USAGE, "only one of --tun-mtu or --link-mtu may be defined");
>      }
>  
> -    if (!proto_is_udp(ce->proto) && options->mtu_test)
> +    /* Defer validation for --remote-srv with auto protocol */
> +    if (!proto_is_udp(ce->proto) && options->mtu_test
> +        && !(ce->remote_srv && ce->proto == PROTO_AUTO))
>      {
>          msg(M_USAGE, "--mtu-test only makes sense with --proto udp");

You disable this test here, but you don't add this in any of the
later checks. So it seems this test is just completely removed when
using remote-srv?

>      }
> @@ -2347,6 +2407,11 @@ options_postprocess_verify_ce(const struct options 
> *options,
>       * Sanity check on --local, --remote, and --ifconfig
>       */
>  
> +    if (ce->remote_srv && options->ip_remote_hint)
> +    {
> +        msg(M_USAGE, "--ip-remote-hint can't be used with --remote-srv");
> +    }
> +
>      if (proto_is_net(ce->proto)
>          && string_defined_equal(ce->local, ce->remote)
>          && string_defined_equal(ce->local_port, ce->remote_port))
> @@ -2470,7 +2535,9 @@ options_postprocess_verify_ce(const struct options 
> *options,
>       */
>  
>  #ifdef ENABLE_FRAGMENT
> -    if (!proto_is_udp(ce->proto) && ce->fragment)
> +    /* Defer validation for --remote-srv with auto protocol */
> +    if (!proto_is_udp(ce->proto) && ce->fragment
> +        && !(ce->remote_srv && ce->proto == PROTO_AUTO))
>      {
>          msg(M_USAGE, "--fragment can only be used with --proto udp");
>      }
> @@ -2481,11 +2548,11 @@ options_postprocess_verify_ce(const struct options 
> *options,
>          msg(M_USAGE, "--remote MUST be used in TCP Client mode");
>      }
>  
> -    if ((ce->http_proxy_options) && ce->proto != PROTO_TCP_CLIENT)
> +    /* Defer validation for --remote-srv with auto protocol */
> +    if ((ce->http_proxy_options) && ce->proto != PROTO_TCP_CLIENT
> +        && !(ce->remote_srv && ce->proto == PROTO_AUTO))
>      {
> -        msg(M_USAGE,
> -            "--http-proxy MUST be used in TCP Client mode (i.e. --proto "
> -            "tcp-client)");
> +        msg(M_USAGE, "--http-proxy MUST be used in TCP Client mode (i.e. 
> --proto tcp-client)");
>      }
>  
>      if ((ce->http_proxy_options) && !ce->http_proxy_options->server)
> @@ -2552,6 +2619,10 @@ options_postprocess_verify_ce(const struct options 
> *options,
>          {
>              msg(M_USAGE, "--remote cannot be used with --mode server");
>          }
> +        if (ce->remote_srv)
> +        {
> +            msg(M_USAGE, "--remote-srv cannot be used with --mode server");
> +        }
>          if (!ce->bind_local)
>          {
>              msg(M_USAGE, "--nobind cannot be used with --mode server");
> @@ -3067,26 +3138,14 @@ options_postprocess_verify_ce(const struct options 
> *options,
>      uninit_options(&defaults);
>  }
>  
> -static void
> -options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce)
> +/* Part of options_postprocess_mutate_ce that can be used
> + * in runtime after connection entry proto/hostname change.
> + */
> +static bool
> +options_postprocess_mutate_ce_proto(struct options *o,
> +                                    struct connection_entry *ce)
>  {
> -    const int dev = dev_type_enum(o->dev, o->dev_type);
> -
> -    if (o->server_defined || o->server_bridge_defined || 
> o->server_bridge_proxy_dhcp)
> -    {
> -        if (ce->proto == PROTO_TCP)
> -        {
> -            ce->proto = PROTO_TCP_SERVER;
> -        }
> -    }
> -
> -    if (o->client)
> -    {
> -        if (ce->proto == PROTO_TCP)
> -        {
> -            ce->proto = PROTO_TCP_CLIENT;
> -        }
> -    }
> +    bool result = true;
>  
>      /* an option is present that requires local bind to enabled */
>      bool need_bind = ce->local || ce->local_port_defined || ce->bind_defined;
> @@ -3110,7 +3169,61 @@ options_postprocess_mutate_ce(struct options *o, 
> struct connection_entry *ce)
>      /* if protocol forcing is enabled, disable all protocols
>       * except for the forced one
>       */
> -    if (o->proto_force >= 0 && o->proto_force != ce->proto)
> +    if (o->proto_force >= 0 && o->proto_force != ce->proto
> +        && !(ce->remote_srv && ce->proto == PROTO_AUTO))
> +    {
> +        result = false;
> +    }
> +
> +    /* our socks code is not fully IPv6 enabled yet (TCP works, UDP not)
> +     * so fall back to IPv4-only (trac #1221)
> +     */
> +    if (ce->socks_proxy_server && proto_is_udp(ce->proto) && ce->af != 
> AF_INET)
> +    {
> +        if (ce->af == AF_INET6)
> +        {
> +            msg(M_INFO, "WARNING: '--proto udp6' is not compatible with "
> +                "'--socks-proxy' today. Forcing IPv4 mode.");
> +        }
> +        else
> +        {
> +            msg(M_INFO, "NOTICE: dual-stack mode for '--proto udp' does not "
> +                "work correctly with '--socks-proxy' today. Forcing IPv4.");
> +        }
> +        ce->af = AF_INET;
> +    }
> +
> +    if (!proto_is_udp(ce->proto) && ce->explicit_exit_notification)
> +    {
> +        msg(M_WARN, "NOTICE: --explicit-exit-notify ignored for --proto 
> tcp");
> +        ce->explicit_exit_notification = 0;
> +    }
> +
> +    return result;
> +}
> +
> +static void
> +options_postprocess_mutate_ce(struct options *o, struct connection_entry *ce)
> +{
> +    const int dev = dev_type_enum(o->dev, o->dev_type);
> +
> +    if (o->server_defined || o->server_bridge_defined || 
> o->server_bridge_proxy_dhcp)
> +    {
> +        if (ce->proto == PROTO_TCP)
> +        {
> +            ce->proto = PROTO_TCP_SERVER;
> +        }
> +    }
> +
> +    if (o->client)
> +    {
> +        if (ce->proto == PROTO_TCP)
> +        {
> +            ce->proto = PROTO_TCP_CLIENT;
> +        }
> +    }
> +
> +    if (!options_postprocess_mutate_ce_proto(o, ce))
>      {
>          ce->flags |= CE_DISABLED;
>      }
> @@ -3214,12 +3327,32 @@ options_postprocess_mutate_ce(struct options *o, 
> struct connection_entry *ce)
>          connection_entry_preload_key(&ce->tls_crypt_v2_file,
>                                       &ce->tls_crypt_v2_file_inline, &o->gc);
>      }
> +}
>  
> -    if (!proto_is_udp(ce->proto) && ce->explicit_exit_notification)
> +/*
> + * Merges servinfo's hostname, servname and proto into current connection
> + * entry if possible and doesn't conflict with the options.
> + * Used by runtime DNS SRV discovery.
> + */
> +bool
> +options_mutate_ce_servinfo(struct options *o, struct servinfo *si)
> +{
> +    struct connection_entry ce = o->ce;
> +
> +    ASSERT(ce.remote_srv);
> +
> +    ce.remote = si->hostname;
> +    ce.remote_port = si->servname;
> +    ce.proto = si->proto;
> +
> +    if (options_postprocess_mutate_ce_proto(o, &ce)
> +        && options_postprocess_verify_ce_proto(o, &ce))
>      {
> -        msg(M_WARN, "NOTICE: --explicit-exit-notify ignored for --proto 
> tcp");
> -        ce->explicit_exit_notification = 0;
> +        o->ce = ce;
> +        return true;
>      }
> +
> +    return false;
>  }
>  
>  #ifdef _WIN32
> @@ -6062,7 +6195,8 @@ add_option(struct options *options,
>                                 OPT_P_CONNECTION, option_types_found, es);
>              if (!sub.ce.remote)
>              {
> -                msg(msglevel, "Each 'connection' block must contain exactly 
> one 'remote' directive");
> +                msg(msglevel, "Each 'connection' block must contain exactly 
> one 'remote' "
> +                    "or 'remote-srv' directive");
>                  uninit_options(&sub);
>                  goto err;
>              }
> @@ -6140,6 +6274,7 @@ add_option(struct options *options,
>      {
>          struct remote_entry re;
>          re.remote = re.remote_port = NULL;
> +        re.remote_srv = false;
>          re.proto = -1;
>          re.af = 0;
>  
> @@ -6174,9 +6309,66 @@ add_option(struct options *options,
>          }
>          else if (permission_mask & OPT_P_CONNECTION)
>          {
> +            if (options->ce.remote && options->ce.remote_srv)
> +            {
> +                msg(msglevel, "Each 'connection' block must contain exactly 
> one 'remote' "
> +                    "or 'remote-srv' directive");
> +                goto err;
> +            }
> +            connection_entry_load_re(&options->ce, &re);
> +        }
> +    }
> +#if !defined(TARGET_OPENBSD)
> +    else if (streq(p[0], "remote-srv") && p[1] && !p[4])
> +    {
> +        struct remote_entry re;
> +        re.remote = NULL;
> +        re.remote_port = OPENVPN_SERVICE;
> +        re.remote_srv = true;
> +        re.proto = PROTO_AUTO;
> +        re.af = 0;
> +
> +        VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_CONNECTION);
> +        re.remote = p[1];
> +        if (p[2])
> +        {
> +            re.remote_port = p[2];
> +            if (p[3])
> +            {
> +                const int proto = ascii2proto(p[3]);
> +                const sa_family_t af = ascii2af(p[3]);
> +                if (proto < 0)
> +                {
> +                    msg(msglevel,
> +                        "remote-srv: bad protocol associated with domain %s: 
> "
> +                        "'%s'", p[1], p[3]);
> +                    goto err;
> +                }
> +                re.proto = proto;
> +                re.af = af;
> +            }
> +        }
> +        if (permission_mask & OPT_P_GENERAL)
> +        {
> +            struct remote_entry *e = alloc_remote_entry(options, msglevel);
> +            if (!e)
> +            {
> +                goto err;
> +            }
> +            *e = re;
> +        }
> +        else if (permission_mask & OPT_P_CONNECTION)
> +        {
> +            if (options->ce.remote && !options->ce.remote_srv)
> +            {
> +                msg(msglevel, "Each 'connection' block must contain exactly 
> one 'remote' "
> +                    "or 'remote-srv' directive");
> +                goto err;
> +            }
>              connection_entry_load_re(&options->ce, &re);
>          }
>      }
> +#endif /* if !defined(TARGET_OPENBSD) */
>      else if (streq(p[0], "resolv-retry") && p[1] && !p[2])
>      {
>          VERIFY_PERMISSION(OPT_P_GENERAL);
> @@ -6691,7 +6883,7 @@ add_option(struct options *options,
>          int proto_force;
>          VERIFY_PERMISSION(OPT_P_GENERAL);
>          proto_force = ascii2proto(p[1]);
> -        if (proto_force < 0)
> +        if (proto_force < 0 || proto_force == PROTO_AUTO)
>          {
>              msg(msglevel, "Bad --proto-force protocol: '%s'", p[1]);
>              goto err;
[...]
> diff --git a/src/openvpn/socket.c b/src/openvpn/socket.c
> index 4a982561..aa057f61 100644
> --- a/src/openvpn/socket.c
> +++ b/src/openvpn/socket.c
> @@ -271,6 +271,37 @@ get_cached_dns_entry(struct cached_dns_entry *dns_cache,
>      return -1;
>  }
>  
> +/*
> + * get_cached_srv_entry return 0 on success and -1

"returns"

> + * otherwise. (like getservinfo)
> + */
> +static int
> +get_cached_srv_entry(struct cached_dns_entry *dns_cache,
> +                     const char *hostname,
> +                     const char *servname,
> +                     int ai_family,
> +                     int resolve_flags,
> +                     struct servinfo **si)
> +{
> +    struct cached_dns_entry *ph;
> +    int flags;
> +
> +    /* Only use flags that are relevant for the structure */
> +    flags = resolve_flags & GETADDR_CACHE_SERVICE_MASK;
> +    flags |= GETADDR_SERVICE;
> +
> +    for (ph = dns_cache; ph; ph = ph->next)
> +    {
> +        if (streqnull(ph->hostname, hostname)
> +            && streqnull(ph->servname, servname)
> +            && ph->flags == flags)
> +        {
> +            *si = ph->si;
> +            return 0;
> +        }
> +    }
> +    return -1;
> +}
>  
>  static int
>  do_preresolve_host(struct context *c,
> @@ -326,6 +357,77 @@ do_preresolve_host(struct context *c,
>      return status;
>  }
>  
> +static int
> +do_preresolve_service(struct context *c,
> +                      const char *hostname,
> +                      const char *servname,
> +                      const int af,
> +                      const int flags,
> +                      bool preresolve_host)
> +{
> +    struct servinfo *si;
> +    int status;
> +
> +    if (get_cached_srv_entry(c->c1.dns_cache,
> +                             hostname,
> +                             servname,
> +                             af,
> +                             flags,
> +                             &si) == 0)
> +    {
> +        /* entry already cached, return success */
> +        return 0;
> +    }
> +
> +    status = openvpn_getservinfo(flags, hostname, servname,
> +                                 c->options.resolve_retry_seconds, NULL,
> +                                 af, &si);
> +    if (status == 0)
> +    {
> +        struct cached_dns_entry *ph;
> +
> +        ALLOC_OBJ_CLEAR_GC(ph, struct cached_dns_entry, &c->gc);
> +        ph->si = si;
> +        ph->hostname = hostname;
> +        ph->servname = servname;
> +        ph->flags = flags & GETADDR_CACHE_SERVICE_MASK;
> +
> +        if (!c->c1.dns_cache)
> +        {
> +            c->c1.dns_cache = ph;
> +        }
> +        else
> +        {
> +            struct cached_dns_entry *prev = c->c1.dns_cache;
> +            while (prev->next)
> +            {
> +                prev = prev->next;
> +            }
> +            prev->next = ph;
> +        }
> +
> +        gc_addspecial(si, &gc_freeservinfo_callback, &c->gc);
> +
> +        /* preresolve service targets unless disabled */
> +        if (preresolve_host)
> +        {
> +            while (si)
> +            {
> +                int host_flags = flags & ~GETADDR_PROTO_MASK;
> +                if (proto_is_dgram(si->proto))
> +                {
> +                    host_flags |= GETADDR_DATAGRAM;
> +                }
> +                /* ignore errors */
> +                do_preresolve_host(c, si->hostname, si->servname,
> +                                   af, host_flags);
> +                si = si->next;
> +            }
> +        }
> +    }
> +    return status;
> +}
> +
>  void
>  do_preresolve(struct context *c)
>  {
> @@ -364,8 +466,31 @@ do_preresolve(struct context *c)
>              remote = ce->remote;
>          }
>  
> +        /* Preresolve remote services */
> +        if (ce->remote_srv)
> +        {
> +            int service_flags = flags|GETADDR_SERVICE;
> +
> +            if (proto_is_dgram(ce->proto) || ce->proto == PROTO_AUTO)
> +            {
> +                service_flags |= GETADDR_DATAGRAM;
> +            }
> +            if (proto_is_stream(ce->proto) || ce->proto == PROTO_AUTO)
> +            {
> +                service_flags |= GETADDR_STREAM;
> +            }
> +
> +            /* HTTP remote hostnames does not need to be resolved */
> +            status = do_preresolve_service(c, remote, ce->remote_port,
> +                                           ce->af, service_flags,
> +                                           !ce->http_proxy_options);
> +            if (status != 0)
> +            {
> +                goto err;
> +            }
> +        }
>          /* HTTP remote hostname does not need to be resolved */
> -        if (!ce->http_proxy_options)
> +        else if (!ce->http_proxy_options)
>          {
>              status = do_preresolve_host(c, remote, ce->remote_port,
>                                          ce->af, flags);
> @@ -423,6 +548,638 @@ err:
>      throw_signal_soft(SIGHUP, "Preresolving failed");
>  }
>  
> +/**
> + * Allocates new service structure on heap and stores
> + * its initial values.
> + *
> + * @param hostname - The peer host name or ip address
> + * @param port     - The peer TCP/UDP port
> + * @param proto    - Protocol for communicating with the peer
> + *
> + * @return A pointer to servinfo structure or NULL in case of
> + *     allocation error.
> + */
> +static struct servinfo *
> +alloc_servinfo(const char *hostname, uint16_t port, int proto)
> +{
> +    size_t namesize = hostname ? strlen(hostname) + 1 : 0;
> +
> +    char servname[sizeof("65535")];
> +    openvpn_snprintf(servname, sizeof(servname), "%u", port);
> +    size_t servsize = strlen(servname) + 1;
> +
> +    struct servinfo *si = calloc(1, sizeof(*si) + namesize + servsize);
> +    if (si)
> +    {
> +        if (hostname)
> +        {
> +            si->hostname = (char *)(si + 1);
> +            memcpy((char *)si->hostname, hostname, namesize);
> +        }
> +        si->servname = (char *)(si + 1) + namesize;
> +        memcpy((char *)si->servname, servname, servsize);
> +        si->proto = proto;
> +    }
> +    return si;
> +}
> +
> +#ifdef _WIN32
> +/**
> + * Queries DNS SRV records for specified DNS domain.
> + *
> + * @param domain - The DNS domain name
> + * @param proto  - TCP/UDP protocol for communicating with the peer
> + * @param next   - The servinfo structure list to be chained to
> + *                 the resulting servinfo list
> + * @param res    - The pointer to the resulting servinfo list
> + *
> + * @return 0 on success, a EAI-* status code otherwise
> + */
> +static int
> +query_servinfo(const char *domain, int proto,
> +               struct servinfo *next, struct servinfo **res)
> +{
> +    PDNS_RECORD pDnsRecord;
> +    int status;
> +
> +    ASSERT(res);
> +
> +    DNS_STATUS DnsStatus = DnsQuery(domain, DNS_TYPE_SRV, DNS_QUERY_STANDARD,
> +                                    NULL, &pDnsRecord, NULL);
> +    dmsg(D_SOCKET_DEBUG, "DNSQUERY type=%d domain=%s result=%d",
> +         DNS_TYPE_SRV, domain, DnsStatus);
> +    switch (DnsStatus)
> +    {
> +        case ERROR_SUCCESS:
> +            break;
> +
> +        case DNS_ERROR_RCODE_NAME_ERROR:
> +        case DNS_INFO_NO_RECORDS:
> +            return EAI_NONAME; /* HOST_NOT_FOUND */
> +
> +        case DNS_ERROR_NO_DNS_SERVERS:
> +        case DNS_ERROR_RCODE_FORMAT_ERROR:
> +        case DNS_ERROR_RCODE_NOT_IMPLEMENTED:
> +        case DNS_ERROR_RCODE_REFUSED:
> +            return EAI_FAIL; /* NO_RECOVERY */
> +
> +        case ERROR_TIMEOUT:
> +        case DNS_ERROR_RCODE_SERVER_FAILURE:
> +        case DNS_ERROR_TRY_AGAIN_LATER:
> +            return EAI_AGAIN; /* TRY_AGAIN */
> +
> +        default:
> +            return EAI_FAIL;
> +    }
> +
> +    struct servinfo *list = NULL, *first = NULL;
> +    for (PDNS_RECORD rr = pDnsRecord; rr; rr = rr->pNext)
> +    {
> +        if (rr->wType == DNS_TYPE_SRV)
> +        {
> +            PDNS_SRV_DATA rdata = &rr->Data.Srv;
> +
> +            if (rr->wDataLength >= sizeof(DNS_SRV_DATA)
> +                && *rdata->pNameTarget)
> +            {
> +                struct servinfo *si = alloc_servinfo(rdata->pNameTarget,
> +                                                     rdata->wPort,
> +                                                     proto);
> +                if (!si)
> +                {
> +                    freeservinfo(list);
> +                    status = EAI_MEMORY;
> +                    goto done;
> +                }
> +                si->prio = rdata->wPriority;
> +                si->weight = rdata->wWeight;
> +                si->next = list, list = si;
> +                if (!first)
> +                {
> +                    first = si;
> +                }
> +            }
> +        }
> +    }
> +    if (list)
> +    {
> +        first->next = next;
> +        *res = list;
> +        status = 0;
> +    }
> +    else
> +    {
> +        status = EAI_FAIL;
> +    }
> +
> +done:
> +    DnsRecordListFree(pDnsRecord, DnsFreeRecordList);
> +    return status;
> +}
> +
> +#else  /* ifdef _WIN32 */
> +/**
> + * Queries DNS SRV records for specified DNS domain.
> + *
> + * @param domain - The DNS domain name
> + * @param proto  - TCP/UDP protocol for communicating with the peer
> + * @param next   - The servinfo structure list to be chained to
> + *                 the resulting servinfo list
> + * @param res    - The pointer to the resulting servinfo list
> + *
> + * @return 0 on success, a EAI-* status code otherwise
> + */
> +
> +static int
> +query_servinfo(const char *domain, int proto,
> +               struct servinfo *next, struct servinfo **res)
> +{
> +#if !defined(TARGET_OPENBSD)
> +    unsigned char answer[65535];
> +    int status;
> +
> +    int n = res_query(domain, ns_c_in, ns_t_srv, answer, sizeof(answer));
> +    dmsg(D_SOCKET_DEBUG, "RES_QUERY class=%d type=%d domain=%s result=%d",
> +         ns_c_in, ns_t_srv, domain, n);
> +    if (n < 0)
> +    {
> +        switch (h_errno)
> +        {
> +            case HOST_NOT_FOUND:
> +            case NO_ADDRESS:
> +#if NO_ADDRESS != NO_DATA
> +            case NO_DATA:
> +#endif
> +                return EAI_NONAME;
> +
> +            case NO_RECOVERY:
> +                return EAI_FAIL;
> +
> +            case TRY_AGAIN:
> +                return EAI_AGAIN;
> +        }
> +        return EAI_SYSTEM;
> +    }
> +
> +    ns_msg msg;
> +    if (ns_initparse(answer, n, &msg) < 0)
> +    {
> +        return EAI_FAIL;
> +    }
> +
> +    struct servinfo *list = NULL, *first = NULL;
> +    for (int i = 0; i < ns_msg_count(msg, ns_s_an); i++)
> +    {
> +        ns_rr rr;
> +
> +        if (ns_parserr(&msg, ns_s_an, i, &rr) == 0
> +            && ns_rr_type(rr) == ns_t_srv)
> +        {
> +            const unsigned char *rdata = ns_rr_rdata(rr);
> +            char name[NS_MAXDNAME];
> +
> +            if (ns_rr_rdlen(rr) > 6
> +                && dn_expand(ns_msg_base(msg), ns_msg_end(msg),
> +                             rdata + 6, name, sizeof(name)) > 0 && *name)
> +            {
> +                struct servinfo *si = alloc_servinfo(name,
> +                                                     ns_get16(rdata + 4),
> +                                                     proto);
> +                if (!si)
> +                {
> +                    freeservinfo(list);
> +                    status = EAI_MEMORY;
> +                    goto done;
> +                }
> +                si->prio = ns_get16(rdata);
> +                si->weight = ns_get16(rdata + 2);
> +                si->next = list, list = si;
> +                if (!first)
> +                {
> +                    first = si;
> +                }
> +            }
> +        }
> +    }
> +    if (list)
> +    {
> +        first->next = next;
> +        *res = list;
> +        status = 0;
> +    }
> +    else
> +    {
> +        status = EAI_FAIL;
> +    }
> +
> +done:
> +    return status;
> +#else   /* defined(TARGET_OPENBSD) */
> +    /* OpenBSD's native resolver library has a better API - getrrsetbyname() 
> -
> +     * but this is not implemented yet
> +     */
> +    return EAI_FAIL;
> +#endif  /* defined(TARGET_OPENBSD) */
> +}
> +#endif /* ifdef _WIN32 */
> +
> +/**
> + * Servinfo qsort compare function for the server selection
> + * mechanism, defined in RFC 2782.
> + *
> + * @param a - Pointer to first servinfo structure
> + * @param b - Pointer to second servinfo structure
> + *
> + * @return
> + * @li 0, if equal
> + * @li 1, if "a" should be ordered after "b"
> + * @li -1, if "a" should be ordered before "b"
> + */
> +static int
> +servinfo_cmp(const void *a, const void *b)
> +{
> +    const struct servinfo *ae = *(struct servinfo **)a;
> +    const struct servinfo *be = *(struct servinfo **)b;
> +
> +    /* lowest-numbered priority first */
> +    if (ae->prio != be->prio)
> +    {
> +        return ae->prio < be->prio ? -1 : 1;
> +    }
> +
> +    /* zero-weighted first */
> +    if ((ae->weight == 0 && be->weight)
> +        || (ae->weight && be->weight == 0))
> +    {
> +        return ae->weight < be->weight ? -1 : 1;
> +    }
> +
> +    /* else keep received order, can't be equal */
> +    return ae->order > be->order ? -1 : 1;
> +}
> +
> +/**
> + * Sorts service list according the server selection mechanism,
> + * defined in RFC 2782.
> + *
> + * @param list - The pointer to servinfo list
> + *
> + * @return A pointer to the sorted servinfo list
> + */
> +static struct servinfo *
> +sort_servinfo(struct servinfo *list)
> +{
> +    struct servinfo ordered, *tail = &ordered;
> +    int count = 0;
> +    struct gc_arena gc = gc_new();
> +
> +    ASSERT(list);
> +
> +    /* count and number entries in reverse order */
> +    for (struct servinfo *si = list; si; si = si->next)
> +    {
> +        si->order = count++;
> +    }
> +
> +    struct servinfo **sorted;
> +    ALLOC_ARRAY_CLEAR_GC(sorted, struct servinfo *, count, &gc);
> +    for (struct servinfo *si = list; si; si = si->next)
> +    {
> +        sorted[si->order] = si;
> +    }
> +
> +    /* sort records by priority and zero weight */
> +    qsort(sorted, count, sizeof(sorted[0]), servinfo_cmp);
> +
> +    /* apply weighted selection mechanism */
> +    ordered.next = NULL;
> +    for (int i = 0; i < count; )
> +    {
> +        struct servinfo unordered;
> +
> +        /* compute the sum of the weights of records of the same
> +         * priority and put them in the unordered list */
> +        unordered.prio = sorted[i]->prio;
> +        unordered.weight = 0;
> +        unordered.next = NULL;
> +        for (struct servinfo *prev = &unordered;
> +             i < count && sorted[i]->prio == unordered.prio; i++)
> +        {
> +            unordered.weight += sorted[i]->weight;
> +
> +            /* add entry to the tail of unordered list */
> +            sorted[i]->next = NULL;
> +            prev->next = sorted[i], prev = sorted[i];
> +        }
> +
> +        /* process the unordered list */
> +        while (unordered.next)
> +        {
> +            /* choose a uniform random number between 0 and the sum
> +             * computed (inclusive) */
> +            int weight = get_random() % (unordered.weight + 1);
> +
> +            /* select the entries whose running sum value is the first
> +             * in the selected order which is greater than or equal
> +             * to the random number selected */
> +            for (struct servinfo *si = unordered.next, *prev = &unordered;
> +                 si; prev = si, si = si->next)
> +            {
> +                /* selected entry is the next one to be contacted */
> +                if (si->weight >= weight)
> +                {
> +                    unordered.weight -= si->weight;
> +
> +                    /* move entry to the ordered list */
> +                    prev->next = si->next;
> +                    si->next = NULL;
> +                    tail->next = si, tail = si;
> +
> +                    /*
> +                     * RFC 2782 is ambiguous, it says:
> +                     *   In the presence of records containing weights 
> greater
> +                     *   than 0, records with weight 0 should have a very
> +                     *   small chance of being selected.
> +                     * According that, within the same priority, after all
> +                     * records containing weights greater than 0 were 
> selected,
> +                     * the rest of records with weight 0 should be skipped.
> +                     * At the same time, it says:
> +                     *   The following algorithm SHOULD be used to order the
> +                     *   SRV RRs of the same priority:
> +                     *   ...
> +                     *   Continue the ordering process until there are no
> +                     *   unordered SRV RRs.
> +                     * This means records with wight 0 should always be
> +                     * selected, as last ones in worst case.
> +                     *
> +                     * We implement the second option and do not skip any of
> +                     * the records with unordered.weight == 0 after the last
> +                     * one with weight greater than 0.
> +                     */
> +                }
> +                weight -= si->weight;
> +            }
> +        }
> +    }
> +
> +    gc_free(&gc);
> +    return ordered.next;
> +}
> +
> +/**
> + * Resolves DNS SRV records for given domain and service names.
> + *
> + * @param domain  - The DNS SRV domain name
> + * @param service - The DNS SRV service name
> + * @param flags   - GETADDR_* DNS resultion flags
> + * @param res     - The pointer to the resulting servinfo list
> + *
> + * @return 0 on success, a EAI-* status code otherwise
> + */
> +static int
> +getservinfo(const char *domain,
> +            const char *service,
> +            int flags,
> +            struct servinfo **res)
> +{
> +    static const struct {
> +        int flags;
> +        int proto;
> +        const char *name;
> +    } proto[] = {
> +        { GETADDR_DATAGRAM, PROTO_UDP, "udp" },
> +        { GETADDR_STREAM, PROTO_TCP_CLIENT, "tcp" }
> +    };
> +    struct servinfo *list = NULL;
> +    int status = EAI_SOCKTYPE;
> +
> +    ASSERT(res);
> +
> +    if (!domain)
> +    {
> +        return EAI_NONAME;
> +    }
> +    if (!service)
> +    {
> +        return EAI_SERVICE;
> +    }
> +
> +    int proto_flags = flags & GETADDR_PROTO_MASK;
> +    for (int i = 0; i < SIZE(proto); i++)
> +    {
> +        if (proto_flags & proto[i].flags)
> +        {
> +            proto_flags &= ~proto[i].flags;
> +
> +            char qname[256];
> +            if (!openvpn_snprintf(qname, sizeof(qname), "_%s._%s.%s",
> +                                  service, proto[i].name, domain))
> +            {
> +                freeservinfo(list);
> +                return EAI_MEMORY;
> +            }
> +
> +            status = query_servinfo(qname, proto[i].proto, list, &list);
> +        }
> +    }
> +
> +    if (list)
> +    {
> +        *res = sort_servinfo(list);
> +        status = 0;
> +    }
> +
> +    return status;
> +}
> +
> +void
> +freeservinfo(struct servinfo *res)
> +{
> +    while (res)
> +    {
> +        struct servinfo *si = res;
> +        res = res->next;
> +        free(si);
> +    }
> +}
> +
> +/*
> + * Translate IPv4/IPv6 hostname and service name into struct servinfo
> + * If resolve error, try again for resolve_retry_seconds seconds.
> + */
> +int
> +openvpn_getservinfo(unsigned int flags,
> +                    const char *hostname,
> +                    const char *servname,
> +                    int resolve_retry_seconds,
> +                    volatile int *signal_received,
> +                    int family,
> +                    struct servinfo **res)
> +{
> +    int status;
> +    int sigrec = 0;
> +    int msglevel = (flags & GETADDR_FATAL) ? M_FATAL : D_RESOLVE_ERRORS;
> +    struct gc_arena gc = gc_new();
> +    const char *print_hostname;
> +    const char *print_servname;
> +
> +    ASSERT(res);
> +
> +    ASSERT(hostname || servname);
> +    ASSERT(!(flags & GETADDR_HOST_ORDER));
> +
> +    if (hostname)
> +    {
> +        print_hostname = hostname;
> +    }
> +    else
> +    {
> +        print_hostname = "undefined";
> +    }
> +
> +    if (servname)
> +    {
> +        print_servname = servname;
> +    }
> +    else
> +    {
> +        print_servname = "";
> +    }
> +
> +    if (flags & GETADDR_MSG_VIRT_OUT)
> +    {
> +        msglevel |= M_MSG_VIRT_OUT;
> +    }
> +
> +    if ((flags & (GETADDR_FATAL_ON_SIGNAL|GETADDR_WARN_ON_SIGNAL))
> +        && !signal_received)
> +    {
> +        signal_received = &sigrec;
> +    }
> +
> +    const int fail_wait_interval = 5; /* seconds */
> +    /* Add +4 to cause integer division rounding up (1 + 4) = 5, (0+4)/5=0 */
> +    int resolve_retries = (flags & GETADDR_TRY_ONCE) ? 1 :
> +                          ((resolve_retry_seconds + 4)/ fail_wait_interval);
> +    const char *fmt;
> +    int level = 0;
> +
> +    fmt = "RESOLVE: Cannot resolve remote service: %s:%s (%s)";
> +    if ((flags & GETADDR_MENTION_RESOLVE_RETRY)
> +        && !resolve_retry_seconds)
> +    {
> +        fmt = "RESOLVE: Cannot resolve remote service: %s:%s (%s) "
> +              "(I would have retried this name query if you had "
> +              "specified the --resolv-retry option.)";
> +    }
> +
> +#ifdef ENABLE_MANAGEMENT
> +    if (flags & GETADDR_UPDATE_MANAGEMENT_STATE)
> +    {
> +        if (management)
> +        {
> +            management_set_state(management,
> +                                 OPENVPN_STATE_RESOLVE,
> +                                 NULL,
> +                                 NULL,
> +                                 NULL,
> +                                 NULL,
> +                                 NULL);
> +        }
> +    }
> +#endif
> +
> +    /*
> +     * Resolve service
> +     */
> +    while (true)
> +    {
> +#ifndef _WIN32
> +        /* force resolv.conf reload */
> +        res_init();
> +#endif
> +        dmsg(D_SOCKET_DEBUG, "GETSERVINFO flags=0x%04x family=%d %s:%s",
> +             flags, family, print_hostname, print_servname);
> +        status = getservinfo(hostname, servname, flags, res);
> +
> +        if (signal_received)
> +        {
> +            get_signal(signal_received);
> +            if (*signal_received) /* were we interrupted by a signal? */
> +            {
> +                if (*signal_received == SIGUSR1) /* ignore SIGUSR1 */
> +                {
> +                    msg(level, "RESOLVE: Ignored SIGUSR1 signal received "
> +                        "during DNS resolution attempt");
> +                    *signal_received = 0;
> +                }
> +                else
> +                {
> +                    /* turn success into failure (interrupted syscall) */
> +                    if (0 == status)
> +                    {
> +                        ASSERT(res);
> +                        freeservinfo(*res);
> +                        *res = NULL;
> +                        status = EAI_AGAIN; /* = temporary failure */
> +                        errno = EINTR;
> +                    }
> +                    goto done;
> +                }
> +            }
> +        }
> +
> +        /* success? */
> +        if (0 == status)
> +        {
> +            break;
> +        }
> +
> +        /* resolve lookup failed, should we
> +         * continue or fail? */
> +        level = msglevel;
> +        if (resolve_retries > 0)
> +        {
> +            level = D_RESOLVE_ERRORS;
> +        }
> +
> +        msg(level,
> +            fmt,
> +            print_hostname,
> +            print_servname,
> +            gai_strerror(status));
> +
> +        if (--resolve_retries <= 0)
> +        {
> +            goto done;
> +        }
> +
> +        management_sleep(fail_wait_interval);
> +    }
> +
> +    ASSERT(res);
> +
> +    /* service resolve succeeded */
> +
> +done:
> +    if (signal_received && *signal_received)
> +    {
> +        int level = 0;
> +        if (flags & GETADDR_FATAL_ON_SIGNAL)
> +        {
> +            level = M_FATAL;
> +        }
> +        else if (flags & GETADDR_WARN_ON_SIGNAL)
> +        {
> +            level = M_WARN;
> +        }
> +        msg(level, "RESOLVE: signal received during DNS resolution attempt");
> +    }
> +
> +    gc_free(&gc);
> +    return status;
> +}
> +
>  /*
>   * Translate IPv4/IPv6 addr or hostname into struct addrinfo
>   * If resolve error, try again for resolve_retry_seconds seconds.
> @@ -557,8 +1314,9 @@ openvpn_getaddrinfo(unsigned int flags,
>              /* try hostname lookup */
>              hints.ai_flags &= ~AI_NUMERICHOST;
>              dmsg(D_SOCKET_DEBUG,
> -                 "GETADDRINFO flags=0x%04x ai_family=%d ai_socktype=%d",
> -                 flags, hints.ai_family, hints.ai_socktype);
> +                 "GETADDRINFO flags=0x%04x ai_family=%d ai_socktype=%d 
> %s:%s",
> +                 flags, hints.ai_family, hints.ai_socktype,
> +                 print_hostname, print_servname);
>              status = getaddrinfo(hostname, servname, &hints, res);
>  
>              if (signal_received)
> @@ -1816,7 +2574,110 @@ done:
>      gc_free(&gc);
>  }
>  
> +void
> +do_resolve_service(struct context *c)
> +{
> +    struct connection_entry *ce = &c->options.ce;
>  
> +    if (ce->remote_srv
> +        && !c->c1.link_socket_addr.service_list)
> +    {
> +        if (ce->remote)
> +        {
> +            unsigned int flags = sf2gaf(GETADDR_RESOLVE
> +                                        |GETADDR_UPDATE_MANAGEMENT_STATE
> +                                        |GETADDR_SERVICE,
> +                                        c->options.sockflags);
> +            int retry = 0;
> +            int status = -1;
> +            struct servinfo *si;
> +
> +            if (proto_is_dgram(ce->proto) || ce->proto == PROTO_AUTO)
> +            {
> +                flags |= GETADDR_DATAGRAM;
> +            }
> +            if (proto_is_stream(ce->proto) || ce->proto == PROTO_AUTO)
> +            {
> +                flags |= GETADDR_STREAM;
> +            }
> +
> +            if (c->options.sockflags & SF_HOST_RANDOMIZE)
> +            {
> +                flags |= GETADDR_RANDOMIZE;
> +            }
> +
> +            if (c->options.resolve_retry_seconds == RESOLV_RETRY_INFINITE)
> +            {
> +                flags |= (GETADDR_TRY_ONCE | GETADDR_FATAL);
> +                retry = 0;
> +            }
> +            else if (c->options.resolve_retry_seconds)
> +            {
> +                flags |= GETADDR_FATAL;
> +                retry = c->options.resolve_retry_seconds;
> +            }
> +            else
> +            {
> +                flags |= (GETADDR_FATAL | GETADDR_MENTION_RESOLVE_RETRY);
> +                retry = 0;
> +            }
> +
> +            status = get_cached_srv_entry(c->c1.dns_cache,
> +                                          ce->remote,
> +                                          ce->remote_port,
> +                                          ce->af,
> +                                          flags, &si);
> +            if (status)
> +            {
> +                status = openvpn_getservinfo(flags, ce->remote, 
> ce->remote_port,
> +                                             retry, &c->sig->signal_received,
> +                                             ce->af, &si);
> +            }
> +
> +            if (status == 0)
> +            {
> +                c->c1.link_socket_addr.service_list = si;
> +                c->c1.link_socket_addr.current_service = NULL;
> +
> +                /* advance to the first appropriate service */
> +                while (si)
> +                {
> +                    /* map in current service */
> +                    if (!c->c1.link_socket_addr.current_service
> +                        && options_mutate_ce_servinfo(&c->options, si))
> +                    {
> +                        c->c1.link_socket_addr.current_service = si;
> +                    }
> +
> +                    /* log discovered service hosts */
> +                    msg(D_RESOLVE,
> +                        "Resolved remote service host: %s:%s,%s prio %u 
> weight %u",
> +                        np(si->hostname), np(si->servname),
> +                        proto2ascii(si->proto, ce->af, false),
> +                        si->prio, si->weight);
> +
> +                    si = si->next;
> +                }
> +                if (!c->c1.link_socket_addr.current_service)
> +                {
> +                    status = EAI_NONAME;
> +                }
> +
> +                dmsg(D_SOCKET_DEBUG,
> +                     "RESOLVE_SERVICE flags=0x%04x rrs=%d sig=%d status=%d",
> +                     flags,
> +                     retry,
> +                     c->sig->signal_received,
> +                     status);
> +            }
> +
> +            if (!c->sig->signal_received && status != 0)
> +            {
> +                c->sig->signal_received = SIGUSR1;
> +            }
> +        }
> +    }
> +}
>  
>  struct link_socket *
>  link_socket_new(void)
> @@ -3077,16 +3938,19 @@ static const struct proto_names proto_names[] = {
>      {"tcp-server",  "TCP_SERVER", AF_UNSPEC, PROTO_TCP_SERVER},
>      {"tcp-client",  "TCP_CLIENT", AF_UNSPEC, PROTO_TCP_CLIENT},
>      {"tcp",         "TCP", AF_UNSPEC, PROTO_TCP},
> +    {"auto",        "AUTO", AF_UNSPEC, PROTO_AUTO},
>      /* force IPv4 */
>      {"udp4",        "UDPv4", AF_INET, PROTO_UDP},
>      {"tcp4-server", "TCPv4_SERVER", AF_INET, PROTO_TCP_SERVER},
>      {"tcp4-client", "TCPv4_CLIENT", AF_INET, PROTO_TCP_CLIENT},
>      {"tcp4",        "TCPv4", AF_INET, PROTO_TCP},
> +    {"auto4",       "AUTOv4", AF_INET, PROTO_AUTO},
>      /* force IPv6 */
>      {"udp6",        "UDPv6", AF_INET6, PROTO_UDP},
>      {"tcp6-server", "TCPv6_SERVER", AF_INET6, PROTO_TCP_SERVER},
>      {"tcp6-client", "TCPv6_CLIENT", AF_INET6, PROTO_TCP_CLIENT},
>      {"tcp6",        "TCPv6", AF_INET6, PROTO_TCP},
> +    {"auto6",       "AUTOv6", AF_INET6, PROTO_AUTO},
>  };
>  
>  int
> @@ -3147,6 +4011,11 @@ proto2ascii_all(struct gc_arena *gc)
>  
>      for (i = 0; i < SIZE(proto_names); ++i)
>      {
> +        if (proto_names[i].proto == PROTO_NONE
> +            || proto_names[i].proto == PROTO_AUTO)
> +        {
> +            continue;
> +        }
>          if (i)
>          {
>              buf_printf(&out, " ");
[...]

-- 
  Frank Lichtenheld


_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to