                           README for RULI

LICENSE

        RULI - Resolver User Layer Interface
        Copyright (C) 2001 Everton da Silva Marques
        
        This program is free software; you can redistribute it and/or
        modify it under the terms of the GNU General Public License as
        published by the Free Software Foundation; either version 2 of
        the License, or (at your option) any later version.
        
        This program is distributed in the hope that it will be
        useful, but WITHOUT ANY WARRANTY; without even the implied
        warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
        PURPOSE.  See the GNU General Public License for more details.
        
        You should have received a copy of the GNU General Public
        License along with this program -- see the file COPYING; if
        not, write to the Free Software Foundation, Inc., 59 Temple
        Place, Suite 330, Boston, MA 02111-1307 USA

INTRODUCTION

        RULI stands for Resolver User Layer Interface. It's a library
        built on top of the DNS resolver. RULI provides an easy-to-use
        interface for querying DNS SRV resource records. The main goal
        is to ease the replacement of gethostbyname(), an
        address-only-based function, by a small number of functions
        which fully support SRV records.

        RULI aims to automatically perform all the SRV logic described
        by RFC 2782 and to comply with related standards.

        This is an early, alpha release of RULI.

        As you can notice, I don't have good English skills. If you
        want to correct my language mistakes or to contribute with
        polished documentation, please feel free to do so.

        If you use (or are planning to use) RULI, please drop me a
        mail -- contact information is provided at the end of this
        document. I'm specially interested if you're having trouble
        with the API. I believe the API is as simple as possible
        while properly compliant with specifications.

REQUIREMENTS

        RULI currently depends on libresolv's res_search() function;
        so, you'll need 'libresolv'. As far as I know, 'libresolv' is
        part of glibc.

        If you want to use the threaded asynchronous API, you'll also
        need 'libpthread'.

BUILDING

        To build the library:

                cd src
                make

        The file 'libruli.so' should be issued.

        To install the library under '/usr/local/ruli', type:

                make install

        One can change that default library location by editing the
        tools/ruli-config script.

        To build the test programs 'http_srv_query' and 'srv_query':

                cd tools
                make

TEST PROGRAMS

        'srv_query.c' is a small program that illustrates the usage of
        the RULI library. For a given domain, it fetches the DNS
        records related to a specific service/protocol pair and show
        them in the exact order in which a real program should try to
        contact the corresponding servers.

        The syntax of the program 'srv_query' is:

                srv_query <service> <protocol> <domain>

        Example:

                ./srv_query smtp tcp some.domain.tld.

        'http_srv_query.c' is a more specific version of
        'srv_query.c'. It fetches the DNS records related to HTTP for
        a given domain and show them in the specific order in which a
        real program should try to contact the corresponding servers.

        The syntax of the program 'http_srv_query' is:

                http_srv_query <domain>

        Example:

                ./http_srv_query some.domain.tld.

LIBRARY USAGE

        SIMPLEST SCENARIO

        The following psedo-code shows the simplest possible scenario
        for correct utilization of the library's API. Error codes and
        parameters have been omitted for clarity.

                ruli_srv_make()
        
                ruli_srv_query() /* or ruli_http_srv_query() */

                do loop while (ruli_srv_target_pending())

                        ruli_srv_target_next()
        
                        /* 
                                Try to contact the fetched
                                address/port tuple; exit loop if
                                success on contacting the target.
                        */
                end loop

                ruli_srv_destroy()


        TYPICAL SCENARIO

        Now the example illustrates a more complex scenario for
        utilization of the library:
        
                ruli_srv_make()
                do loop while (is needed to perform SRV queries)
                        ruli_srv_reset()
        
                        ruli_srv_query() /* or ruli_http_srv_query() */
        
                        ruli_srv_target_reset()
                        do loop while (ruli_srv_target_pending())
                                ruli_srv_target_next()
        
                                /* 
                                        Try to contact the fetched
                                        address/port tuple; exit
                                        inner loop on success.
                                */
                        end loop
                end loop
                ruli_srv_destroy()

        LINKING

        To use the library, a program should:

                1) Include the 'ruli.h' header.

                        #include "ruli.h"

                2) Link against the library's shared object. Below
                it's provided a sample Makefile.

                        RULI_CFLAGS = $(shell ./ruli-config --cflags)
                        RULI_LIBS   = $(shell ./ruli-config --libs)

                        test: test.c
                                $(CC) $(RULI_CFLAGS) -o $@ $< $(RULI_LIBS)

        LIBRARY INTERFACE

        The library interface is defined in 'ruli.h' as follows. Every
        function returns 0 (false) on success.

struct ruli_srv_target_t {
  const char          *name;    /* target name (null-terminated string) */
  const unsigned char *addr;    /* target address */
  int                 addr_len; /* address length */
  int                 port;     /* target port */
};

        ruli_srv_target_t represents every tuple found by RULI. Its
        data is valid as long as ruli_srv_destroy() or
        ruli_srv_reset() are not called. If you need the data after
        invokation of such functions, previously copy it to a more
        persistent location.

int ruli_srv_make(struct ruli_t **ruli_srv_rec);

        ruli_srv_make() allocates and initializes RULI's internal data
        structures. A pointer to those structures is returned in
        'ruli_srv_sec'. All other functions require such pointer to
        properly identify their context.

void ruli_srv_reset(struct ruli_t *ruli_srv_rec);

        ruli_srv_reset() re-initializes the internal data structures
        so a new query can be performed with them. Obviously, this
        function dicards the most recently fetched data.
        
int ruli_srv_destroy(struct ruli_t *ruli_srv_rec);

        ruli_srv_destroy() disposes structures allocated by the
        library. Use this function as soon as there is no need to
        iterate over the collected tuples anymore.

int ruli_srv_query(struct ruli_t *ruli_srv_rec, const char *service, 
                   const char *proto, const char *dname, 
                   int default_port);

int ruli_http_srv_query(struct ruli_t *ruli_srv_rec, 
                        const char *dname, int default_port);

        ruli__srv_query() performs a query for a given (service,
        protocol, domain) tuple (i.e. for _service._protocol.domain).

        ruli_http_srv_query() performs an HTTP query for domain
        'dname' (i.e. for _http._tcp.dname). It's just a more specific
        version of ruli_srv_query().

        In some cases, RULI can't fetch SRV records (because they
        don't exist) and must fallback to requesting A records
        instead. In such situations, RULI uses the 'defaul_port'
        argument as port number. This hides the A-records workaround,
        keeping the interface uniform.

        If this function returns successfuly, one can use the
        function prefixed with ruli_srv_target_, described below, to
        retrieve the tuples.

void ruli_srv_target_reset(struct ruli_t *ruli_srv_rec);

        ruli_srv_target_reset() resets an internal iterator to the
        first tuple found. It's not needed just after
        ruli_http_srv_query invokation. This is useful if one wants to
        walk thru all the same tuples more than once.

int ruli_srv_target_pending(struct ruli_t *ruli_srv_rec);

        ruli_srv_target_pending() checks whether still there is some
        tuple to be visited. Returns 0 if there isn't.

int ruli_srv_target_next(struct ruli_t *ruli_srv_rec, 
                         struct ruli_srv_target_t **srv_target);

        ruli_srv_target_next() fetches the pointer to the next tuple
        into 'srv_target'. 

        The tuples are returned in the exact order in which they
        should be contacted, with all the SRV logic already
        performed. I.e. a SRV-cognizant program should try to contact
        the servers in the same order in which the tuples are issued
        by ruli_srv_target_next().

ASYNCHRONOUS THREADED LIBRARY USAGE

        RULI provides an asynchronous query interface based on
        pthreads. The asynchronous API is very similar to the blocking
        one. Note that all asynchronous functions and types are
        prefixed with 'aruli_' instead of 'ruli_'.

        ** ADVICE **
        Keep in mind that the threaded query will asynchronously store
        data into the library's data structure created by
        aruli_srv_make(). Thus, while the asynchronous query is
        running, do not try to change the library's internal
        state. E.g, don't lauch other query against the same data
        structures nor reset them.
        However, you could safely use aruli_srv_make() to create
        another library context and then work asynchronously on it.

        To use the asynchronous API, a program should:

                1) Include the 'aruli.h' header.

                        #include "aruli.h"

                2) Link against the asynchronous library's shared
                object. Below it's provided a sample Makefile.

                        RULI_CFLAGS = $(shell ./ruli-config --cflags)
                        ARULI_LIBS  = $(shell ./ruli-config --alibs)

                        atest: atest.c
                                $(CC) $(RULI_CFLAGS) -o $@ $< $(ARULI_LIBS)

        The asynchronous API presents the following methods for name
        look up.

        1. POLLING METHOD

        In addition to the synchronous API, consider the following
        functions:

int aruli_srv_query_done(aruli_t *aruli_srv_rec);

        aruli_srv_query_done() checks for query completion. It returns
        0 (false) if the query is yet being performed.

int aruli_srv_query_result(aruli_t *aruli_srv_rec);

        aruli_srv_query_result() fetches the result of a finished
        query, as reported by aruli_srv_query_result(). In the
        asynchronous API, the result returned by aruli_srv_query()
        only indicates whether the query was submitted. Once the query
        is done, one must call aruli_srv_query_result() to obtain the
        proper query result.

        SCENARIO FOR POLLING METHOD

        The following pseudo-code shows a possible case for the
        polling method.

                aruli_srv_make()

                aruli_srv_query(waitable = 0) /* or aruli_http_srv_query() */

                /*** 
                 * BEGIN -- asynchronous API only
                 */
                do loop while (!aruli_srv_query_done())
                        /*
                                The query is not finished; so let's do
                                something else while we wait a bit
                                more.
                        */
                end loop

                aruli_srv_query_result()
                /*
                        Abort if query result is bad.
                */
                /*
                 * END -- asynchronous API only 
                 ***/

                do loop while (aruli_srv_target_pending())

                        aruli_srv_target_next()
        
                        /* 
                                Try to contact the fetched
                                address/port tuple; exit loop if
                                success on contacting the target.
                        */
                end loop

                aruli_srv_destroy()

        2. BLOCKING WAIT METHOD

	This method is very similar to the polling-based one. But in
        this case the argument "waitable" for aruli_srv_query() must
        be non-zero. Consider the following function:

int aruli_srv_query_wait(aruli_t *aruli_srv_rec);

	aruli_srv_query_wait() can be used to block the main thread
        until the query is completed. aruli_srv_query_done() could
        still be used to previously check for query completion.

	Please note that once the "waitable" option is activated in
        aruli_srv_query(), one must later call aruli_srv_query_wait()
        in order to properly release pthread resources.

        SCENARIO FOR BLOCKING WAIT METHOD

        The following pseudo-code shows a possible case for the
        blocking wait method.

                aruli_srv_make()

                aruli_srv_query(waitable == 1) /* or aruli_http_srv_query() */

                /*** 
                 * BEGIN -- asynchronous API only
                 */
		aruli_srv_query_wait()

                aruli_srv_query_result()
                /*
                        Abort if query result is bad.
                */
                /*
                 * END -- asynchronous API only 
                 ***/

                do loop while (aruli_srv_target_pending())

                        aruli_srv_target_next()
        
                        /* 
                                Try to contact the fetched
                                address/port tuple; exit loop if
                                success on contacting the target.
                        */
                end loop

                aruli_srv_destroy()

        3. CALLBACK FUNCTION METHOD

	The callback method allows the user to register a function to
        be "called back" when the query is completed.

	Take a look at the aruli_srv_query() function:

int aruli_srv_query(aruli_t *aruli_srv_rec, int waitable, aruli_callback_t query_callback, void *callback_param, const char *service, const char *proto, const char *dname, int default_port);

	If the 'query_callback' argument is provided (not null), the
        corresponding function will be invoked by the library once the
        query is completed. The 'callback_param' pointer is passed
        back as argument for the callback function.

	The callback function can be used either alone or in
        conjunction with the other methods.

SUPPORT

        Corrections, suggestions for improvement and patches are
        welcome.

        The web site at SourceForge is probably the most useful
        resouce for RULI users:

        http://ruli.sourceforge.net
        http://sourceforge.net/projects/ruli/

        Author:
        Everton da Silva Marques <evertonm@my-deja.com>
