bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug-gnulib] DNS SRV: getsrv/freesrvhost


From: Simon Josefsson
Subject: [bug-gnulib] DNS SRV: getsrv/freesrvhost
Date: Fri, 10 Dec 2004 21:04:26 +0100
User-agent: Gnus/5.110003 (No Gnus v0.3) Emacs/21.3.50 (gnu/linux)

Hello.  It would be useful to have a gnulib module to access DNS SRV
records.  Old BIND releases has a contrib/srv/ directory with some
starting points for this.  The copying conditions says:

    Since the public domain does not exist as a concept in Norwegian
    law, we cannot release this hack into the public domain.  So:

        Copyright 1998 Troll Tech.  Use, modification and distribution
        is allowed without limitation, warranty, or liability of any
        kind.

Is this acceptable for use in gnulib?  The code is not large, and I
could rewrite it if necessary, but I'd prefer not to.

The implementation uses resolv.h and res_query from libresolv.  That
is widely available, but not POSIX as far as I know.  I suppose that a
gnulib module replacement for res_query could be written, if
necessary.  Is this dependency on libresolv a problem?

The srv.h API is:

struct srvhost {
  unsigned int pref;
  struct srvhost * next;
  unsigned int port;
  unsigned int weight;
  unsigned int rweight;
  char name[1];
};
extern void freesrvhost ( struct srvhost * );
extern struct srvhost * getsrv( const char * domain,
                                const char * service,
                                const char * protocol );

I suppose it is inspired by getaddrinfo.  Perhaps getsrvinfo is a
better name.  I haven't thought about the API at all; if people have
comments perhaps we could design something better.  OTOH, perhaps the
above API is widely used so that it is a de-facto standard (although I
doubt this).

I'm attaching the implementation below.

Thanks,
Simon

#include "srv.h"

#include <resolv.h>
#include <stdlib.h>
/* #include <malloc.h> */

/* the biggest packet we'll send and receive */
#if PACKETSZ > 1024
#define MAXPACKET PACKETSZ
#else
#define MAXPACKET 1024
#endif

/* and what we send and receive */
typedef union {
        HEADER hdr;
        u_char buf[MAXPACKET];
} querybuf;

#ifndef T_SRV
#define T_SRV           33
#endif


void freesrvhost ( struct srvhost * s )
{
    struct srvhost * n;
    while( s ) {
        n = s->next;
        /* hack to make double-free visible by causing null dereference */
        s->next = NULL;
        free( (void *)s );
        s = n;
    }
}


static int compare( const void * a, const void * b )
{
    struct srvhost * aa, * bb;

    if ( !a )
        return 1;
    if ( !b )
        return -1;

    aa  = (struct srvhost *) *(int*)a;
    bb = (struct srvhost *) *(int*)b;

    if ( aa->pref > bb->pref )
        return 1;
    if ( aa->pref < bb->pref )
        return -1;

    if ( aa->rweight > bb->rweight )
        return -1;
    if ( aa->rweight < bb->rweight )
        return 1;
    
    return 0;
}


struct srvhost * getsrv( const char * domain,
                         const char * service, const char * protocol ) {
    querybuf answer;            /* answer buffer from nameserver */
    int n;
    char * zone;
    int ancount, qdcount;               /* answer count and query count */
    HEADER *hp;                 /* answer buffer header */
    struct srvhost **replyarray;
    struct srvhost * firsthost;
    int answerno;
    u_char hostbuf[256];
    u_char *msg, *eom, *cp;     /* answer buffer positions */
    int dlen, type, pref, weight, port;

    if ( !domain || !*domain ||
         !service || !*service ||
         !protocol || !*protocol )
        return NULL;

    zone = (char *)malloc( strlen( domain ) + 
                           strlen( service ) +
                           strlen( protocol ) + 3 );
    /* assume malloc works */
    sprintf( zone, "%s.%s.%s", service, protocol, domain );

    n = res_query( zone, C_IN, T_SRV, (u_char *)&answer, sizeof( answer ) );

    (void) free( zone );
    zone = NULL;

    if ( n < (int)sizeof(HEADER) )
        return NULL;

    /* valid answer received. skip the query record. */

    hp = (HEADER *)&answer;
    qdcount = ntohs(hp->qdcount);
    ancount = ntohs(hp->ancount);

    msg = (u_char *)&answer;
    eom = (u_char *)&answer + n;
    cp  = (u_char *)&answer + sizeof(HEADER);

    while ( qdcount-- > 0 && cp < eom ) {
        n = dn_expand( msg, eom, cp, hostbuf, 256 );
        if (n < 0)
            return NULL;
        cp += n + QFIXEDSZ;
    }

    /* make a big-enough (probably too big) reply array */

    replyarray 
        = (struct srvhost **) malloc( ancount * sizeof(struct srvhost *) );
    for( n = 0; n < ancount; n++ )
        replyarray[n] = NULL;
    answerno = 0;

    /* loop through the answer buffer and extract SRV records */
    while ( ancount-- > 0 && cp < eom ) {
        n = dn_expand( msg, eom, cp, hostbuf, 256 );
        if ( n < 0 ) {
            for( n = 0; n < answerno; n++ )
                (void) free( replyarray[n] );
            (void)free( replyarray );
            return NULL;
        }

        cp += n;

        type = _getshort(cp);
        cp += sizeof(u_short);

        /* class = _getshort(cp); */
        cp += sizeof(u_short);

        /* ttl = _getlong(cp); */
        cp += sizeof(u_long);

        dlen = _getshort(cp);
        cp += sizeof(u_short);

        if ( type != T_SRV ) {
            cp += dlen;
            continue;
        }

        pref = _getshort(cp);
        cp += sizeof(u_short);

        weight = _getshort(cp);
        cp += sizeof(u_short);

        port = _getshort(cp);
        cp += sizeof(u_short);

        n = dn_expand( msg, eom, cp, hostbuf, 256 );
        if (n < 0)
            break;
        cp += n;

        replyarray[answerno] 
            = (struct srvhost *)malloc( sizeof( struct srvhost ) +
                                        strlen( hostbuf ) );
        replyarray[answerno]->pref = pref;
        replyarray[answerno]->weight = weight;
        if ( weight )
            replyarray[answerno]->rweight = 1+random()%( 10000 * weight );
        else
            replyarray[answerno]->rweight = 0;
        replyarray[answerno]->port = port;
        replyarray[answerno]->next = NULL;
        strcpy( replyarray[answerno]->name, hostbuf );

        answerno++;
    }

    qsort( replyarray, answerno, sizeof( struct srvhost * ),
           compare );

    for( n = 0; n < answerno; n++ )
        replyarray[n]->next = replyarray[n+1];
    replyarray[answerno-1]->next = NULL;

    firsthost = replyarray[0];
    (void) free( replyarray );
    return firsthost;
}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]