Just a few questions and minor comments below :

On Fri, Aug 04, 2017 at 06:49:43PM +0200, Olivier Houchard wrote:
> This also adds support for SRV records. To use them, simply use a SRV label
> instead of a hostname on the server line, ie :
> server s1 _http._tcp.example.com  resolvers dns check
> server s2 _http._tcp.example.com  resolvers dns check
> 
> When this is done, haproxy will first resolve _http._tcp.example.com, and then
> give the hostname (as well as port and weight) to each available server, that
> will then do a regular DNS resolution to get the IP.

What makes the distinction between an SRV record and a real hostname here ?
Just the leading underscore, or the plain "_http." maybe ? I'm not expecting
any problem with this given that the underscore is not allowed as a regular
hostname character (except under windows). But at least this will deserve
a mention in the doc where the server's address is described, so that anyone
experiencing trouble could spot this easily.

> From 1b408464590fea38d8a45b2b7fed5c615465a858 Mon Sep 17 00:00:00 2001
> From: Olivier Houchard <[email protected]>
> Date: Thu, 6 Jul 2017 18:46:47 +0200
> Subject: [PATCH 1/4] MINOR: dns: Cache previous DNS answers.
> 
> As DNS servers may not return all IPs in one answer, we want to cache the
> previous entries. Those entries are removed when considered obsolete, which
> happens when the IP hasn't been returned by the DNS server for a time
> defined in the "hold obsolete" parameter of the resolver section. The default
> is 30s.
> ---
>  doc/configuration.txt  |   7 +-
>  include/proto/server.h |   2 +-
>  include/types/dns.h    |   9 +-
>  src/cfgparse.c         |   5 +-
>  src/dns.c              | 247 
> ++++++++++++++++++++++++++++---------------------
>  src/server.c           |  28 ++++--
>  6 files changed, 175 insertions(+), 123 deletions(-)
> 
> diff --git a/doc/configuration.txt b/doc/configuration.txt
> index bfeb3ce0..f4674387 100644
> --- a/doc/configuration.txt
> +++ b/doc/configuration.txt
> @@ -11693,6 +11693,10 @@ For example, with 2 name servers configured in a 
> resolvers section:
>   - first response is truncated and second one is a NX Domain, then HAProxy
>     stops resolution.
>  
> +As a DNS server may not answer all the IPs in one DNS request, haproxy keeps
> +a cache of previous answers, an answer will be considered obsolete after
> +"hold obsolete" seconds without the IP returned.
> +
>  
>  resolvers <resolvers id>
>    Creates a new name server list labelled <resolvers id>
> @@ -11709,7 +11713,7 @@ hold <status> <period>
>    Defines <period> during which the last name resolution should be kept based
>    on last resolution <status>
>      <status> : last name resolution status. Acceptable values are "nx",
> -               "other", "refused", "timeout", "valid".
> +               "other", "refused", "timeout", "valid", "obsolete".
>      <period> : interval between two successive name resolution when the last
>                 answer was in <status>. It follows the HAProxy time format.
>                 <period> is in milliseconds by default.
> @@ -11756,6 +11760,7 @@ timeout <event> <time>
>       hold nx              30s
>       hold timeout         30s
>       hold valid           10s
> +     hold obsolete        30s
>  
>  
>  6. HTTP header manipulation
> diff --git a/include/proto/server.h b/include/proto/server.h
> index 43e4e425..c4f8e1d5 100644
> --- a/include/proto/server.h
> +++ b/include/proto/server.h
> @@ -52,7 +52,7 @@ int srv_init_addr(void);
>  struct server *cli_find_server(struct appctx *appctx, char *arg);
>  
>  /* functions related to server name resolution */
> -int snr_update_srv_status(struct server *s);
> +int snr_update_srv_status(struct server *s, int has_no_ip);
>  int snr_resolution_cb(struct dns_requester *requester, struct dns_nameserver 
> *nameserver);
>  int snr_resolution_error_cb(struct dns_requester *requester, int error_code);
>  struct server *snr_check_ip_callback(struct server *srv, void *ip, unsigned 
> char *ip_family);
> diff --git a/include/types/dns.h b/include/types/dns.h
> index 7a19aa37..12c11552 100644
> --- a/include/types/dns.h
> +++ b/include/types/dns.h
> @@ -113,7 +113,7 @@ struct dns_query_item {
>  /* NOTE: big endian structure */
>  struct dns_answer_item {
>       struct list list;
> -     char *name;                             /* answer name
> +     char name[DNS_MAX_NAME_SIZE];           /* answer name

Do you have an estimate of the worst case increase of memory usage incurred
by using the max name size for every name component ? I understand that it
might not be possible to use a shared area anymore for all entries once you
start to deal with obsolescence, but it's just to get an idea of what the
worst DNS response could have as impact.

> @@ -124,7 +124,8 @@ struct dns_answer_item {
>       int16_t port;                           /* SRV type port */
>       int16_t data_len;                       /* number of bytes in target 
> below */
>       struct sockaddr address;                /* IPv4 or IPv6, network format 
> */
> -     char *target;                           /* Response data: SRV or CNAME 
> type target */
> +     char target[DNS_MAX_NAME_SIZE];         /* Response data: SRV or CNAME 
> type target */

Same here.

> @@ -1011,30 +1027,29 @@ int dns_validate_dns_response(unsigned char *resp, 
> unsigned char *bufend, struct
>       }
>  
>       /* now parsing response records */
> -     LIST_INIT(&dns_p->answer_list);
>       nb_saved_records = 0;
> -     for (dns_answer_record_id = 0; dns_answer_record_id < 
> dns_p->header.ancount; dns_answer_record_id++) {
> +     for (int i = 0; i < dns_p->header.ancount; i++) {

"for (int i ...)" will not build here, it's only C99 and we don't enforce
this (and personally I hate this form as it's the only exception I know
of where a variable declaration is used outside of the scope it's declared
in.

> @@ -1046,43 +1061,50 @@ int dns_validate_dns_response(unsigned char *resp, 
> unsigned char *bufend, struct
>  
>               }
>  
> -             dns_answer_record->name = chunk_newstr(dns_response_buffer);
> -             if (dns_answer_record->name == NULL)
> -                     return DNS_RESP_INVALID;
> -
> -             ret = chunk_strncat(dns_response_buffer, tmpname, len);
> -             if (ret == 0)
> -                     return DNS_RESP_INVALID;
> +             memcpy(dns_answer_record->name, tmpname, len);
> +             dns_answer_record->name[len] = 0;

I'm just realizing that when I merge the minimalist indirect string
manipulation functions I'm using for HTTP/2, it will simplify such
code, as dealing all the time with memcpy() and memcmp() for strings
is a pain. It will also give me ideas of what to improve there before
it represents a perceptible benefit. We'll probably see this for 1.9.

> From a2e3bba11a5c1a3c69e9f33c566d7c285c9121f2 Mon Sep 17 00:00:00 2001
> From: Olivier Houchard <[email protected]>
> Date: Fri, 4 Aug 2017 18:35:36 +0200
> Subject: [PATCH 3/4] MINOR: dns: Handle SRV records.
> 
> Make it so for each server, instead of specifying a hostname, one can use
> a SRV label.
> When doing so, haproxy will first resolve the SRV label, then use the
> resulting hostnames, as well as port and weight (priority is ignored right
> now), to each server using the SRV label.
> It is resolved periodically, and any server disappearing from the SRV records
> will be removed, and any server appearing will be added, assuming there're
> free servers in haproxy.
> ---
>  include/proto/dns.h    |   1 +
>  include/proto/server.h |   1 +
>  include/types/dns.h    |  16 +++
>  include/types/proxy.h  |   1 +
>  include/types/server.h |   1 +
>  src/cfgparse.c         |  22 +++-
>  src/dns.c              | 313 
> ++++++++++++++++++++++++++++++++++++++++++-------
>  src/proxy.c            |   1 +
>  src/server.c           |  96 ++++++++++++---
>  9 files changed, 395 insertions(+), 57 deletions(-)
> 
(...)
> diff --git a/src/dns.c b/src/dns.c
> index 0ce63c91..2931752a 100644
> --- a/src/dns.c
> +++ b/src/dns.c
> @@ -478,14 +497,104 @@ void dns_resolve_recv(struct dgram_conn *dgram)
>                               break;
(...)
> +                             /* Check if a server already uses that hostname 
> */
> +                             for (srv = srvrq->proxy->srv; srv != NULL; srv 
> = srv->next) {
> +                                     if (srv->srvrq == srvrq &&
> +                                         item1->data_len == 
> srv->hostname_dn_len &&
> +                                         !memcmp(srv->hostname_dn, 
> item1->target, item1->data_len) &&
> +                                         srv->svc_port == item1->port) {
> +                                             if (srv->uweight != 
> item1->weight) {
> +                                                     char weight[9];
> +
> +                                                     snprintf(weight, 
> sizeof(weight),
> +                                                         "%d", 
> item1->weight);
> +                                                     
> server_parse_weight_change_request(srv, weight);


Next time, you can use ultoa() as a replacement for snprintf(str, size, "%d"),
which also ensures you'll use a large enough string for the largest supported
integer types, there's also U2A() which uses a rotating buffer and which can
be used multiple times in a single function call.

> +                             reader += sizeof(uint16_t);
> +                             memcpy(&dns_answer_record->weight,
> +                                 reader, sizeof(uint16_t));
> +                             dns_answer_record->weight = 
> ntohs(dns_answer_record->weight);

I guess there's an alignment issue there, am I right ?

I think we'll have to add some easier conversion functions, like unaligned
reads using something like this to make such manipulation less of a pain :

  static inline uint16_t readu16(const void *p)
  {
      union { const void *p; uint16_t u16; } __attribute__((packed)) u = { .p = 
p };
      return u.u16;
  }

  static inline uint16_t readbe16(const void *p)
  {
      return ((unsigned char *)p)[0] << 8 +
             ((unsigned char *)p)[1];
  }

The the code above simply becomes :

                reader += sizeof(uint16_t);
                dns_answer_record->weight = readbe16(reader);

and for cases where native-endian word is needed instead :

                reader += sizeof(uint16_t);
                dns_answer_record->weight = readu16(reader);

> @@ -1635,6 +1801,27 @@ int dns_build_query(int query_id, int query_type, char 
> *hostname_dn, int hostnam
>       return ptr - buf;
>  }
>  
> +/* Turn a domain name label into a string */
> +void dns_dn_label_to_str(char *dn, char *str, int dn_len)
> +{
> +     int remain_size = 0;
> +
> +     for (int i = 0; i < dn_len; i++) {

Note, "for (int i" here as well.

> From 899413f3a965feeaa96ffb5b284b504dbdb01fa1 Mon Sep 17 00:00:00 2001
> From: Olivier Houchard <[email protected]>
> Date: Fri, 4 Aug 2017 18:39:01 +0200
> Subject: [PATCH 4/4] MINOR: check: Fix checks when using SRV records.
> 
> When started, a server may not yet have an associated protocol, so don't
> bother trying to run the checks until it is there.
> ---
>  src/checks.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/src/checks.c b/src/checks.c
> index 7938b873..fc92a243 100644
> --- a/src/checks.c
> +++ b/src/checks.c
> @@ -1557,7 +1557,7 @@ static int connect_conn_chk(struct task *t)
>       }
>  
>       ret = SF_ERR_INTERNAL;
> -     if (proto->connect)
> +     if (proto && proto->connect)
>               ret = proto->connect(conn, check->type, quickack ? 2 : 0);
>       if (s->check.send_proxy && !(check->state & CHK_ST_AGENT)) {
>               conn->send_proxy_ofs = 1;

Do you think this one might be triggered before SRV records ? If so
we'd need to tag it for backporting. If you're unsure we should also
backport it, of course :-)

Thanks!
Willy

Reply via email to