/*!\file res_mkqu.c
 * Resolver.
 */

/* ++Copyright++ 1985, 1993
 * -
 * Copyright (c) 1985, 1993
 *    The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *   This product includes software developed by the University of
 *   California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * -
 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies, and that
 * the name of Digital Equipment Corporation not be used in advertising or
 * publicity pertaining to distribution of the document or software without
 * specific, written prior permission.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 * -
 * --Copyright--
 */

#include "resolver.h"

#if defined(USE_BIND)

/*
 * Form all types of queries.
 * Returns the size of the result or -1.
 */
int W32_CALL res_mkquery (
    int           op,            /* opcode of query */
    const char   *dname,         /* domain name */
    int           Class,         /* class of query */
    int           type,          /* type of query */
    const u_char *data,          /* resource record data */
    int           datalen,       /* length of data */
    const u_char *newrr_in,      /* new rr for modify or append */
    u_char       *buf,           /* buffer to put query */
    int           buflen)        /* size of buffer */
{
  struct  rrec *newrr = (struct rrec *) newrr_in;
  HEADER  *hp;
  u_char  *cp;
  u_char  *dnptrs[20];
  u_char **dpp, **lastdnptr;
  int      n;

  if ((_res.options & RES_INIT) == 0 && res_init() == -1)
  {
    h_errno = NETDB_INTERNAL;
    return (-1);
  }

  if (_res.options & RES_DEBUG)
     (*_printf) (";; res_mkquery(%d, %s, %d, %d)\n", op, dname, Class, type);

  /* Initialize header fields.
   */
  if (!buf || buflen < HFIXEDSZ)
     return (-1);

  memset (buf, 0, HFIXEDSZ);
  hp = (HEADER*) buf;
  hp->id     = htons (++_res.id);
  hp->opcode = op;
  hp->rd     = (_res.options & RES_RECURSE) != 0;
  hp->rcode  = NOERROR;
  cp      = buf + HFIXEDSZ;
  buflen -= HFIXEDSZ;
  dpp     = dnptrs;
  *dpp++  = buf;
  *dpp++  = NULL;
  lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0];

  /* perform opcode specific processing
   */
  switch (op)
  {
    case STATUS:          /** \todo Handle STATUS case */
         return (-1);

    case QUERY:
         /* fallthrough */

    case NS_NOTIFY_OP:    /* Notify secondary server of SOA change */
         buflen -= QFIXEDSZ;
         if (buflen < 0)
            return (-1);

         if ((n = dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0)
            return (-1);
         cp     += n;
         buflen -= n;
         __putshort (type, cp);
         cp += INT16SZ;
         __putshort (Class, cp);
         cp += INT16SZ;
         hp->qdcount = htons (1);
         if (op == QUERY || !data)
            break;
         /*
          * Make an additional record for completion domain.
          */
         buflen -= RRFIXEDSZ;
         n = dn_comp ((char*)data, cp, buflen, dnptrs, lastdnptr);
         if (n < 0)
            return (-1);
         cp     += n;
         buflen -= n;
         __putshort (T_NULL, cp);
         cp += INT16SZ;
         __putshort (Class, cp);
         cp += INT16SZ;
         __putlong (0, cp);
         cp += INT32SZ;
         __putshort (0, cp);
         cp += INT16SZ;
         hp->arcount = htons (1);
         break;

    case IQUERY:
         /*
          * Initialize answer section
          */
         if (buflen < 1 + RRFIXEDSZ + datalen)
            return (-1);
         *cp++ = '\0';  /* no domain name */
         __putshort (type, cp);
         cp += INT16SZ;
         __putshort (Class, cp);
         cp += INT16SZ;
         __putlong (0, cp);
         cp += INT32SZ;
         __putshort (datalen, cp);
         cp += INT16SZ;
         if (datalen)
         {
           memcpy (cp,data,datalen);
           cp += datalen;
         }
         hp->ancount = htons (1);
         break;

#if ALLOW_UPDATES
       /*
        * For UPDATEM/UPDATEMA, do UPDATED/UPDATEDA followed by UPDATEA
        * (Record to be modified is followed by its replacement in msg.)
        */
    case UPDATEM:
    case UPDATEMA:
    case UPDATED:
         /*
          * The res code for UPDATED and UPDATEDA is the same; user
          * calls them differently: specifies data for UPDATED; server
          * ignores data if specified for UPDATEDA.
          */
    case UPDATEDA:
         buflen -= RRFIXEDSZ + datalen;
         if ((n = dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0)
            return (-1);
         cp += n;
         __putshort (type, cp);
         cp += INT16SZ;
         __putshort (Class, cp);
         cp += INT16SZ;
         __putlong (0, cp);
         cp += INT32SZ;
         __putshort (datalen, cp);
         cp += INT16SZ;
         if (datalen)
         {
           memcpy (cp,data,datalen);
           cp += datalen;
         }
         if (op == UPDATED || op == UPDATEDA)
         {
           hp->ancount = 0;
           break;
         }
         /* Else UPDATEM/UPDATEMA, so drop into code for UPDATEA
          */

    case UPDATEA:  /* Add new resource record */
         buflen -= RRFIXEDSZ + datalen;
         if ((n = dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0)
            return (-1);
         cp += n;
         __putshort (newrr->r_type, cp);
         cp += INT16SZ;
         __putshort (newrr->r_class, cp);
         cp += INT16SZ;
         __putlong (0, cp);
         cp += INT32SZ;
         __putshort (newrr->r_size, cp);
         cp += INT16SZ;
         if (newrr->r_size)
         {
           memcpy (cp,newrr->r_data,newrr->r_size);
           cp += newrr->r_size;
         }
         hp->ancount = 0;
         break;
#endif
    default:
         return (-1);
  }

#if ALLOW_UPDATES == 0
  ARGSUSED (newrr);
  ARGSUSED (newrr_in);
#endif

  return (cp - buf);
}
#endif /* USE_BIND */

