/***************************************************************************/
/*                                                                         */
/* Project:     OpenSLP - OpenSource implementation of Service Location    */
/*              Protocol                                                   */
/*                                                                         */
/* File:        slp_message.h                                              */
/*                                                                         */
/* Abstract:    Header file that defines structures and constants that are */
/*              specific to the SLP wire protocol messages.                */
/*                                                                         */
/*-------------------------------------------------------------------------*/
/*                                                                         */
/*     Please submit patches to http://www.openslp.org                     */
/*                                                                         */
/*-------------------------------------------------------------------------*/
/*                                                                         */
/* Copyright (C) 2000 Caldera Systems, Inc                                 */
/* All rights reserved.                                                    */
/*                                                                         */
/* Redistribution and use in source and binary forms, with or without      */
/* modification, are permitted provided that the following conditions are  */
/* met:                                                                    */ 
/*                                                                         */
/*      Redistributions of source code must retain the above copyright     */
/*      notice, this list of conditions and the following disclaimer.      */
/*                                                                         */
/*      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.                                                      */
/*                                                                         */
/*      Neither the name of Caldera Systems 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 COPYRIGHT HOLDERS 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 CALDERA      */
/* SYSTEMS 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.    */
/*                                                                         */
/***************************************************************************/

#include "slp_message.h"
#include "slp_xmalloc.h"

#ifndef _WIN32
#include <sys/types.h>
#include <netinet/in.h>
#endif

#if defined(ENABLE_SLPv1)
#include <slp_v1message.h>
#endif

/*=========================================================================*/
int SLPMessageParseHeader(SLPBuffer buffer, SLPHeader* header)
/* Fill out a header structure with what ever is in the buffer             */
/*                                                                         */
/* buffer (IN) the buffer to be parsed                                     */
/*                                                                         */
/* header (IN/OUT) pointer to the header structure to fill out             */
/*=========================================================================*/
{
    if (buffer->end - buffer->start < 2)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    header->version     = *(buffer->curpos);
    header->functionid  = *(buffer->curpos + 1);
	
    if(header->version != 2)
    {
        return SLP_ERROR_VER_NOT_SUPPORTED;
    }
    /* check for invalid length 18 bytes is the smallest v2 message*/
    if (buffer->end - buffer->start < 18)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    header->length      = AsUINT24(buffer->curpos + 2);
    header->flags       = AsUINT16(buffer->curpos + 5);
    header->encoding    = 0; /* not used for SLPv2 */
    header->extoffset   = AsUINT24(buffer->curpos + 7);
    header->xid         = AsUINT16(buffer->curpos + 10);
    header->langtaglen  = AsUINT16(buffer->curpos + 12);
    header->langtag     = buffer->curpos + 14;

    /* check for invalid function id */
    if(header->functionid > SLP_FUNCT_SAADVERT)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    if(header->length != buffer->end - buffer->start)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    /* check for invalid flags */
    if(header->flags & 0x1fff)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    buffer->curpos = buffer->curpos + header->langtaglen + 14;

    /* check for invalid langtaglen */
    if((void*)(header->langtag + header->langtaglen) > (void*)buffer->end)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    /* check for invalid ext offset */
    if(buffer->start + header->extoffset > buffer->end)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    return 0;
}

/*--------------------------------------------------------------------------*/
int ParseAuthBlock(SLPBuffer buffer, SLPAuthBlock* authblock)
/* Returns  - Zero on success, SLP_ERROR_INTERNAL_ERROR (out of memory) or  */
/*            SLP_ERROR_PARSE_ERROR.                                        */
/*--------------------------------------------------------------------------*/
{
    /* make sure that min size is met */
    if(buffer->end - buffer->curpos < 10)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    authblock->opaque = buffer->curpos;
    
    authblock->bsd          = AsUINT16(buffer->curpos);
    authblock->length       = AsUINT16(buffer->curpos + 2);
    
    if(authblock->length > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    authblock->timestamp    = AsUINT32(buffer->curpos + 4);
    authblock->spistrlen    = AsUINT16(buffer->curpos + 8);
    authblock->spistr       = buffer->curpos + 10;

    if(authblock->spistrlen > buffer->end - buffer->curpos + 10)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    authblock->authstruct   = buffer->curpos + authblock->spistrlen + 10;
    
    authblock->opaquelen = authblock->length;

    buffer->curpos = buffer->curpos + authblock->length;

    return 0;
}

/*--------------------------------------------------------------------------*/
int ParseUrlEntry(SLPBuffer buffer, SLPUrlEntry* urlentry)
/*                                                                          */
/* Returns  - Zero on success, SLP_ERROR_INTERNAL_ERROR (out of memory) or  */
/*            SLP_ERROR_PARSE_ERROR.                                        */
/*--------------------------------------------------------------------------*/
{
    int             result;
    int             i;

    /* make sure that min size is met */
    if(buffer->end - buffer->curpos < 6)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    urlentry->opaque = buffer->curpos;

    /* parse out reserved */
    urlentry->reserved = *(buffer->curpos);
    buffer->curpos = buffer->curpos + 1;

    /* parse out lifetime */
    urlentry->lifetime = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;

    /* parse out url */
    urlentry->urllen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(urlentry->urllen + 1 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    urlentry->url = buffer->curpos;
    buffer->curpos = buffer->curpos + urlentry->urllen;

    /* parse out auth block count */
    urlentry->authcount = *(buffer->curpos);
    buffer->curpos = buffer->curpos + 1;

    /* parse out the auth block (if any) */
    if(urlentry->authcount)
    {
        urlentry->autharray = (SLPAuthBlock*)xmalloc(sizeof(SLPAuthBlock) * urlentry->authcount);
        if(urlentry->autharray == 0)
        {
            return SLP_ERROR_INTERNAL_ERROR;
        }
        memset(urlentry->autharray,0,sizeof(SLPAuthBlock) * urlentry->authcount);

        for(i=0;i<urlentry->authcount;i++)
        {
            result = ParseAuthBlock(buffer,&(urlentry->autharray[i]));
            if(result) return result;
        }
    }

    urlentry->opaquelen = (char*)buffer->curpos - urlentry->opaque;

    return 0;
}


/*--------------------------------------------------------------------------*/
int ParseSrvRqst(SLPBuffer buffer, SLPSrvRqst* srvrqst)
/*--------------------------------------------------------------------------*/
{
    /* make sure that min size is met */
    if(buffer->end - buffer->curpos < 10)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    /* parse the prlist */
    srvrqst->prlistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(srvrqst->prlistlen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvrqst->prlist = buffer->curpos;
    buffer->curpos = buffer->curpos + srvrqst->prlistlen;


    /* parse the service type */
    srvrqst->srvtypelen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(srvrqst->srvtypelen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvrqst->srvtype = buffer->curpos;
    buffer->curpos = buffer->curpos + srvrqst->srvtypelen;    


    /* parse the scope list */
    srvrqst->scopelistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(srvrqst->scopelistlen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvrqst->scopelist = buffer->curpos;
    buffer->curpos = buffer->curpos + srvrqst->scopelistlen;    


    /* parse the predicate string */
    srvrqst->predicatever = 2;  /* SLPv2 predicate (LDAPv3) */
    srvrqst->predicatelen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(srvrqst->predicatelen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvrqst->predicate = buffer->curpos;
    buffer->curpos = buffer->curpos + srvrqst->predicatelen;


    /* parse the slpspi string */
    srvrqst->spistrlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(srvrqst->spistrlen > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvrqst->spistr = buffer->curpos;
    buffer->curpos = buffer->curpos + srvrqst->spistrlen;

    return 0;
}


/*--------------------------------------------------------------------------*/
int ParseSrvRply(SLPBuffer buffer, SLPSrvRply* srvrply)
/*--------------------------------------------------------------------------*/
{
    int             result;
    int             i;

    /* make sure that min size is met */
    if(buffer->end - buffer->curpos < 4)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    /* parse out the error code */
    srvrply->errorcode = AsUINT16(buffer->curpos);
    if(srvrply->errorcode)
    {
        /* We better not trust the rest of the packet */
        memset( srvrply, 0, sizeof(SLPSrvRply)); 
        srvrply->errorcode = AsUINT16(buffer->curpos);
        return 0;
    }
    buffer->curpos = buffer->curpos + 2;

    /* parse out the url entry count */
    srvrply->urlcount = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;

    /* parse out the url entries (if any) */
    if(srvrply->urlcount)
    {
        srvrply->urlarray = (SLPUrlEntry*)xmalloc(sizeof(SLPUrlEntry) * srvrply->urlcount);
        if(srvrply->urlarray == 0)
        {
            return SLP_ERROR_INTERNAL_ERROR;
        }
        memset(srvrply->urlarray,0,sizeof(SLPUrlEntry) * srvrply->urlcount);

        for(i=0;i<srvrply->urlcount;i++)
        {
            result = ParseUrlEntry(buffer,&(srvrply->urlarray[i]));   
            if(result) return result;
        }
    }
    else
    {
        srvrply->urlarray = 0;
    }

    return 0;
}


/*--------------------------------------------------------------------------*/
int ParseSrvReg(SLPBuffer buffer, SLPSrvReg* srvreg)
/*--------------------------------------------------------------------------*/
{
    int             result;
    int             i;

    /* Parse out the url entry */
    result = ParseUrlEntry(buffer,&(srvreg->urlentry));
    if(result)
    {
        return result;
    }

    if(buffer->end - buffer->curpos < 2)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    /* parse the service type */
    srvreg->srvtypelen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(srvreg->srvtypelen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvreg->srvtype = buffer->curpos;
    buffer->curpos = buffer->curpos + srvreg->srvtypelen;    


    /* parse the scope list */
    srvreg->scopelistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(srvreg->scopelistlen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvreg->scopelist = buffer->curpos;
    buffer->curpos = buffer->curpos + srvreg->scopelistlen;    


    /* parse the attribute list*/
    srvreg->attrlistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(srvreg->attrlistlen + 1 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvreg->attrlist = buffer->curpos;
    buffer->curpos = buffer->curpos + srvreg->attrlistlen;


    /* parse out attribute auth block count */
    srvreg->authcount = *(buffer->curpos);
    buffer->curpos = buffer->curpos + 1;

    /* parse out the auth block (if any) */
    if(srvreg->authcount)
    {
        srvreg->autharray = (SLPAuthBlock*)xmalloc(sizeof(SLPAuthBlock) * srvreg->authcount);
        if(srvreg->autharray == 0)
        {
            return SLP_ERROR_INTERNAL_ERROR;
        }
        memset(srvreg->autharray,0,sizeof(SLPAuthBlock) * srvreg->authcount);

        for(i=0;i<srvreg->authcount;i++)
        {
            result = ParseAuthBlock(buffer,&(srvreg->autharray[i]));
            if(result) return result;
        }
    }

    return 0;
}


/*--------------------------------------------------------------------------*/
int ParseSrvDeReg(SLPBuffer buffer, SLPSrvDeReg* srvdereg)
/*--------------------------------------------------------------------------*/
{
    int            result;

    /* make sure that min size is met */
    if(buffer->end - buffer->curpos < 4)
    {
        return SLP_ERROR_PARSE_ERROR;
    }


    /* parse the scope list */
    srvdereg->scopelistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(srvdereg->scopelistlen > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvdereg->scopelist = buffer->curpos;
    buffer->curpos = buffer->curpos + srvdereg->scopelistlen;

    /* parse the url entry */
    result = ParseUrlEntry(buffer,&(srvdereg->urlentry));
    if(result)
    {
        return result;
    }

    /* parse the tag list */
    if(buffer->end - buffer->curpos < 2)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvdereg->taglistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(srvdereg->taglistlen > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvdereg->taglist = buffer->curpos;
    buffer->curpos = buffer->curpos + srvdereg->taglistlen;

    return 0;
}


/*--------------------------------------------------------------------------*/
int ParseSrvAck(SLPBuffer buffer, SLPSrvAck* srvack)
/*--------------------------------------------------------------------------*/
{
    srvack->errorcode = AsUINT16(buffer->curpos);
    return 0;
}


/*--------------------------------------------------------------------------*/
int ParseAttrRqst(SLPBuffer buffer, SLPAttrRqst* attrrqst)
/*--------------------------------------------------------------------------*/
{
    /* make sure that min size is met */
    if(buffer->end - buffer->curpos < 10)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    /* parse the prlist */
    attrrqst->prlistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(attrrqst->prlistlen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    attrrqst->prlist = buffer->curpos;
    buffer->curpos = buffer->curpos + attrrqst->prlistlen;

    /* parse the url */
    attrrqst->urllen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(attrrqst->urllen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    attrrqst->url = buffer->curpos;
    buffer->curpos = buffer->curpos + attrrqst->urllen;    


    /* parse the scope list */
    attrrqst->scopelistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(attrrqst->scopelistlen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    attrrqst->scopelist = buffer->curpos;
    buffer->curpos = buffer->curpos + attrrqst->scopelistlen;    


    /* parse the taglist string */
    attrrqst->taglistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(attrrqst->taglistlen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    attrrqst->taglist = buffer->curpos;
    buffer->curpos = buffer->curpos + attrrqst->taglistlen;


    /* parse the slpspi string */
    attrrqst->spistrlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(attrrqst->spistrlen > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    attrrqst->spistr = buffer->curpos;
    buffer->curpos = buffer->curpos + attrrqst->spistrlen;

    return 0;
}


/*--------------------------------------------------------------------------*/
int ParseAttrRply(SLPBuffer buffer, SLPAttrRply* attrrply)
/*--------------------------------------------------------------------------*/
{
    int             result;
    int             i;

    /* make sure that min size is met */
    if(buffer->end - buffer->curpos < 4)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    /* parse out the error code */
    attrrply->errorcode = AsUINT16(buffer->curpos);
    if(attrrply->errorcode)
    {
        /* We better not trust the rest of the packet */
        memset(attrrply,0,sizeof(SLPAttrRply));
        attrrply->errorcode = AsUINT16(buffer->curpos);
        return 0;
    }
    buffer->curpos = buffer->curpos + 2;

    /* parse out the attrlist */
    attrrply->attrlistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(attrrply->attrlistlen + 1 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    attrrply->attrlist = buffer->curpos;
    buffer->curpos = buffer->curpos + attrrply->attrlistlen;

    /* parse out auth block count */
    attrrply->authcount = *(buffer->curpos);
    buffer->curpos = buffer->curpos + 1;

    /* parse out the auth block (if any) */
    if(attrrply->authcount)
    {
        attrrply->autharray = (SLPAuthBlock*)xmalloc(sizeof(SLPAuthBlock) * attrrply->authcount);
        if(attrrply->autharray == 0)
        {
            return SLP_ERROR_INTERNAL_ERROR;
        }
        memset(attrrply->autharray,0,sizeof(SLPAuthBlock) * attrrply->authcount);

        for(i=0;i<attrrply->authcount;i++)
        {
            result = ParseAuthBlock(buffer,&(attrrply->autharray[i]));
            if(result) return result;
        }
    }

    return 0;
}

/*-------------------------------------------------------------------------*/
int ParseDAAdvert(SLPBuffer buffer, SLPDAAdvert* daadvert)
/*-------------------------------------------------------------------------*/
{
    int             result;
    int             i;

    /* make sure that min size is met */
    if(buffer->end - buffer->curpos < 4)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    /* parse out the error code */
    daadvert->errorcode = AsUINT16(buffer->curpos);
    if(daadvert->errorcode)
    {
        /* We better not trust the rest of the packet */
        memset(daadvert,0,sizeof(SLPDAAdvert));
        daadvert->errorcode = AsUINT16(buffer->curpos);
        return 0;
    }
    buffer->curpos = buffer->curpos + 2;

    /* parse out the bootstamp */
    if(buffer->end - buffer->curpos < 6)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    daadvert->bootstamp = AsUINT32(buffer->curpos);
    buffer->curpos = buffer->curpos + 4;

    /* parse out the url */
    daadvert->urllen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(daadvert->urllen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    daadvert->url = buffer->curpos;
    buffer->curpos = buffer->curpos + daadvert->urllen;

    /* parse the scope list */
    daadvert->scopelistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(daadvert->scopelistlen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    daadvert->scopelist = buffer->curpos;
    buffer->curpos = buffer->curpos + daadvert->scopelistlen;  

    /* parse the attr list */
    daadvert->attrlistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(daadvert->attrlistlen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    daadvert->attrlist = buffer->curpos;
    buffer->curpos = buffer->curpos + daadvert->attrlistlen;

    /* parse the SPI list */
    daadvert->spilistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(daadvert->spilistlen + 1 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    daadvert->spilist = buffer->curpos;
    buffer->curpos = buffer->curpos + daadvert->spilistlen;


    /* parse out auth block count */
    daadvert->authcount = *(buffer->curpos);
    buffer->curpos = buffer->curpos + 1;

    /* parse out the auth block (if any) */
    if(daadvert->authcount)
    {
        daadvert->autharray = (SLPAuthBlock*)xmalloc(sizeof(SLPAuthBlock) * daadvert->authcount);
        if(daadvert->autharray == 0)
        {
            return SLP_ERROR_INTERNAL_ERROR;
        }
        memset(daadvert->autharray,0,sizeof(SLPAuthBlock) * daadvert->authcount);

        for(i=0;i<daadvert->authcount;i++)
        {
            result = ParseAuthBlock(buffer,&(daadvert->autharray[i]));
            if(result) return result;
        }
    }

    return 0;
}


/*-------------------------------------------------------------------------*/
int ParseSAAdvert(SLPBuffer buffer, SLPSAAdvert* saadvert)
/*-------------------------------------------------------------------------*/
{
    int             result;
    int             i;

    /* make sure that min size is met */
    if(buffer->end - buffer->curpos < 4)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    /* parse out the url */
    saadvert->urllen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(saadvert->urllen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    saadvert->url = buffer->curpos;
    buffer->curpos = buffer->curpos + saadvert->urllen;

    /* parse the scope list */
    saadvert->scopelistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(saadvert->scopelistlen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    saadvert->scopelist = buffer->curpos;
    buffer->curpos = buffer->curpos + saadvert->scopelistlen;  

    /* parse the attr list */
    saadvert->attrlistlen = AsUINT16(buffer->curpos);
    buffer->curpos = buffer->curpos + 2;
    if(saadvert->attrlistlen + 1 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    saadvert->attrlist = buffer->curpos;
    buffer->curpos = buffer->curpos + saadvert->attrlistlen;

    /* parse out auth block count */
    saadvert->authcount = *(buffer->curpos);
    buffer->curpos = buffer->curpos + 1;

    /* parse out the auth block (if any) */
    if(saadvert->authcount)
    {
        saadvert->autharray = (SLPAuthBlock*)xmalloc(sizeof(SLPAuthBlock) * saadvert->authcount);
        if(saadvert->autharray == 0)
        {
            return SLP_ERROR_INTERNAL_ERROR;
        }
        memset(saadvert->autharray,0,sizeof(SLPAuthBlock) * saadvert->authcount);

        for(i=0;i<saadvert->authcount;i++)
        {
            result = ParseAuthBlock(buffer,&(saadvert->autharray[i]));
            if(result) return result;
        }
    }

    return 0;
}


/*--------------------------------------------------------------------------*/
int ParseSrvTypeRqst(SLPBuffer buffer, SLPSrvTypeRqst* srvtyperqst)
/*--------------------------------------------------------------------------*/
{
    /* make sure that min size is met */
    if(buffer->end - buffer->curpos < 6)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    /* parse the prlist */
    srvtyperqst->prlistlen = AsUINT16(buffer->curpos);
    buffer->curpos += 2;
    if(srvtyperqst->prlistlen + 2 > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvtyperqst->prlist = srvtyperqst->prlistlen ? buffer->curpos : 0;
    buffer->curpos += srvtyperqst->prlistlen;

    /* parse the naming authority if present */
    srvtyperqst->namingauthlen = AsUINT16(buffer->curpos);
    buffer->curpos += 2;
    if(!srvtyperqst->namingauthlen || srvtyperqst->namingauthlen == 0xffff)
    {
        srvtyperqst->namingauth = 0;
    }
    else
    {
        if(srvtyperqst->namingauthlen > buffer->end - buffer->curpos)
        {
            return SLP_ERROR_PARSE_ERROR;
        }
        srvtyperqst->namingauth = buffer->curpos;
        buffer->curpos += srvtyperqst->namingauthlen;
    }

    /* parse the scope list */
    if(buffer->end - buffer->curpos < 2)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvtyperqst->scopelistlen = AsUINT16(buffer->curpos);
    buffer->curpos += 2;
    if(srvtyperqst->scopelistlen > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvtyperqst->scopelist = buffer->curpos;
    buffer->curpos += srvtyperqst->scopelistlen;    

    return 0;
}


/*--------------------------------------------------------------------------*/
int ParseSrvTypeRply(SLPBuffer buffer, SLPSrvTypeRply* srvtyperply)
/*--------------------------------------------------------------------------*/
{
    /* make sure that min size is met */
    if(buffer->end - buffer->curpos < 4)
    {
        return SLP_ERROR_PARSE_ERROR;
    }

    /* parse out the error code */
    srvtyperply->errorcode = AsUINT16(buffer->curpos);
    if(srvtyperply->errorcode)
    {
        /* We better not trust the rest of the packet */
        memset(srvtyperply,0,sizeof(SLPSrvTypeRply));
        srvtyperply->errorcode = AsUINT16(buffer->curpos);
        return 0;
    }
    buffer->curpos += 2;

    /* parse out the error srvtype-list length */
    srvtyperply->srvtypelistlen = AsUINT16(buffer->curpos);
    buffer->curpos += 2;

    if(srvtyperply->srvtypelistlen > buffer->end - buffer->curpos)
    {
        return SLP_ERROR_PARSE_ERROR;
    }
    srvtyperply->srvtypelist = buffer->curpos;

    return 0;
}

/*--------------------------------------------------------------------------*/
int ParseExtension(SLPBuffer buffer, SLPMessage message)
/* Parse extensions *after* all standard protocol fields are parsed         */
/*--------------------------------------------------------------------------*/
{
    int             extid;
    int             nextoffset;
    int             result  = SLP_ERROR_OK;

    nextoffset = message->header.extoffset;
    while(nextoffset)
    {
        buffer->curpos = buffer->start + nextoffset;
        if(buffer->curpos + 5 >= buffer->end)
        {
            /* Extension takes us past the end of the buffer */
            result = SLP_ERROR_PARSE_ERROR;
            goto CLEANUP;
        }
    
        extid = AsUINT16(buffer->curpos);
        buffer->curpos += 2;

        nextoffset = AsUINT24(buffer->curpos);
        buffer->curpos += 3;
        
        switch(extid)
        {
        case SLP_EXTENSION_ID_REG_PID:
            if(message->header.functionid == SLP_FUNCT_SRVREG)
            {
                /* check to see if buffer is large enough to contain the 4 byte pid */
                if(buffer->curpos + 4 > buffer->end)
                {
                    result = SLP_ERROR_PARSE_ERROR;
                    goto CLEANUP;
                }
                
                message->body.srvreg.pid = AsUINT32(buffer->curpos);
                buffer->curpos += 4;
            }
            break;

        default:
            if (extid >= 0x4000 && extid <= 0x7FFF )
            {
                /* This is a required extension.  We better error out */
                result = SLP_ERROR_MESSAGE_NOT_SUPPORTED;
                goto CLEANUP;
            }
            break;
        }
    }

CLEANUP:
    
    return result;
}

/*=========================================================================*/
void SLPMessageFreeInternals(SLPMessage message)
/*=========================================================================*/
{
    int i;

    switch(message->header.functionid)
    {
    case SLP_FUNCT_SRVRPLY:
        if(message->body.srvrply.urlarray)
        {
            for(i=0;i<message->body.srvrply.urlcount;i++)
            {
                if(message->body.srvrply.urlarray[i].autharray)
                {
                    xfree(message->body.srvrply.urlarray[i].autharray);
                    message->body.srvrply.urlarray[i].autharray = 0;
                }
            }

            xfree(message->body.srvrply.urlarray);
            message->body.srvrply.urlarray = 0;
        }
        break;

    case SLP_FUNCT_SRVREG:
        if(message->body.srvreg.urlentry.autharray)
        {
            xfree(message->body.srvreg.urlentry.autharray);
            message->body.srvreg.urlentry.autharray = 0;
        }
        if(message->body.srvreg.autharray)
        {
            xfree(message->body.srvreg.autharray);
            message->body.srvreg.autharray = 0;
        }
        break;


    case SLP_FUNCT_SRVDEREG:
        if(message->body.srvdereg.urlentry.autharray)
        {
            xfree(message->body.srvdereg.urlentry.autharray);
            message->body.srvdereg.urlentry.autharray = 0;
        }
        break;

    case SLP_FUNCT_ATTRRPLY:
        if(message->body.attrrply.autharray)
        {
            xfree(message->body.attrrply.autharray);
            message->body.attrrply.autharray = 0;
        }
        break;


    case SLP_FUNCT_DAADVERT:
        if(message->body.daadvert.autharray)
        {
            xfree(message->body.daadvert.autharray);
            message->body.daadvert.autharray = 0;
        }
        break; 

    case SLP_FUNCT_SAADVERT:
        if(message->body.saadvert.autharray)
        {
            xfree(message->body.saadvert.autharray);
            message->body.saadvert.autharray = 0;
        }
        break; 

    case SLP_FUNCT_ATTRRQST:
    case SLP_FUNCT_SRVACK:
    case SLP_FUNCT_SRVRQST:
    case SLP_FUNCT_SRVTYPERQST:
    case SLP_FUNCT_SRVTYPERPLY:
    default:
        /* don't do anything */
        break;
    }
}

/*=========================================================================*/
SLPMessage SLPMessageAlloc()
/* Allocates memory for a SLP message descriptor                           */
/*                                                                         */
/* Returns   - A newly allocated SLPMessage pointer of NULL on ENOMEM      */
/*=========================================================================*/
{
    SLPMessage result = (SLPMessage)xmalloc(sizeof(struct _SLPMessage));
    if(result)
    {
        memset(result,0,sizeof(struct _SLPMessage));
    }

    return result;
}


/*=========================================================================*/
SLPMessage SLPMessageRealloc(SLPMessage msg)
/* Reallocates memory for a SLP message descriptor                         */
/*                                                                         */
/* Returns   - A newly allocated SLPMessage pointer of NULL on ENOMEM      */
/*=========================================================================*/
{
    if(msg == 0)
    {
        msg = SLPMessageAlloc();
        if(msg == 0)
        {
            return 0;
        }
    }
    else
    {
        SLPMessageFreeInternals(msg);
    }

    return msg;
}


/*=========================================================================*/
void SLPMessageFree(SLPMessage message)
/* Frees memory that might have been allocated by the SLPMessage for       */
/* UrlEntryLists or AuthBlockLists.                                        */
/*                                                                         */
/* message  - (IN) the SLPMessage to free                                  */
/*=========================================================================*/
{
    if(message)
    {
        SLPMessageFreeInternals(message);
        xfree(message);
    }
}


/*=========================================================================*/
int SLPMessageParseBuffer(struct sockaddr_in* peerinfo,
                          SLPBuffer buffer, 
                          SLPMessage message)
/* Initializes a message descriptor by parsing the specified buffer.       */
/*                                                                         */
/* buffer   - (IN) pointer the SLPBuffer to parse                          */
/*                                                                         */
/* message  - (OUT) set to describe the message from the buffer            */
/*                                                                         */
/* Returns  - Zero on success, SLP_ERROR_PARSE_ERROR, or                   */
/*            SLP_ERROR_INTERNAL_ERROR if out of memory.  SLPMessage is    */
/*            invalid return is not successful.                            */
/*                                                                         */
/* WARNING  - If successful, pointers in the SLPMessage reference memory in*/ 
/*            the parsed SLPBuffer.  If SLPBufferFree() is called then the */
/*            pointers in SLPMessage will be invalidated.                  */
/*=========================================================================*/
{
    int result;

    /* Copy in the peer info */
    memcpy(&message->peer,peerinfo,sizeof(message->peer));

    /* Get ready to parse */
    SLPMessageFreeInternals(message);
    buffer->curpos = buffer->start;

    /* parse the header first */
    result = SLPMessageParseHeader(buffer,&(message->header));
    if(result == 0)
    {
        /* switch on the function id to parse the body */
        switch(message->header.functionid)
        {
        case SLP_FUNCT_SRVRQST:
            result = ParseSrvRqst(buffer,&(message->body.srvrqst));
            break;

        case SLP_FUNCT_SRVRPLY:
            result = ParseSrvRply(buffer,&(message->body.srvrply));
            break;

        case SLP_FUNCT_SRVREG:
            result = ParseSrvReg(buffer,&(message->body.srvreg));
            break;

        case SLP_FUNCT_SRVDEREG:
            result = ParseSrvDeReg(buffer,&(message->body.srvdereg));
            break;

        case SLP_FUNCT_SRVACK:
            result = ParseSrvAck(buffer,&(message->body.srvack));
            break;

        case SLP_FUNCT_ATTRRQST:
            result = ParseAttrRqst(buffer,&(message->body.attrrqst));
            break;

        case SLP_FUNCT_ATTRRPLY:
            result = ParseAttrRply(buffer,&(message->body.attrrply));
            break;

        case SLP_FUNCT_DAADVERT:
            result = ParseDAAdvert(buffer,&(message->body.daadvert));
            break;

        case SLP_FUNCT_SRVTYPERQST:
            result = ParseSrvTypeRqst(buffer,&(message->body.srvtyperqst));
            break;

        case SLP_FUNCT_SRVTYPERPLY:
            result = ParseSrvTypeRply(buffer,&(message->body.srvtyperply));
            break;

        case SLP_FUNCT_SAADVERT:
            result = ParseSAAdvert(buffer,&(message->body.saadvert));
            break;
        default:
            result = SLP_ERROR_MESSAGE_NOT_SUPPORTED;
        }
    }

    if(result == 0 && message->header.extoffset)
    {
        result = ParseExtension(buffer,message);
    }

    return result;
}

/*=========================================================================*/
/* Functions used to parse buffers                                         */

/*-------------------------------------------------------------------------*/
unsigned short AsUINT16(const char *charptr)
/*-------------------------------------------------------------------------*/
{
    unsigned char *ucp = (unsigned char *) charptr;
    return(ucp[0] << 8) | ucp[1];
}

/*-------------------------------------------------------------------------*/
unsigned int AsUINT24(const char *charptr)
/*-------------------------------------------------------------------------*/
{
    unsigned char *ucp = (unsigned char *) charptr;
    return(ucp[0] << 16) | (ucp[1] << 8) |  ucp[2];
}

/*-------------------------------------------------------------------------*/
unsigned int AsUINT32(const char *charptr)
/*-------------------------------------------------------------------------*/
{
    unsigned char *ucp = (unsigned char *) charptr;
    return(ucp[0] << 24) | (ucp[1] << 16) | (ucp[2] << 8) | ucp[3]; 
}

/*=========================================================================*/
/* Functions used to set buffers                                           */

/*-------------------------------------------------------------------------*/
void ToUINT16(char *charptr, unsigned int val)
/*-------------------------------------------------------------------------*/
{
    charptr[0] = (val >> 8) & 0xff;
    charptr[1] = val & 0xff;
}

/*-------------------------------------------------------------------------*/
void ToUINT24(char *charptr, unsigned int val)
/*-------------------------------------------------------------------------*/
{
    charptr[0] = (val >> 16) & 0xff;
    charptr[1] = (val >> 8) & 0xff;
    charptr[2] = val & 0xff;
}

/*-------------------------------------------------------------------------*/
void ToUINT32(char *charptr, unsigned int val)
/*-------------------------------------------------------------------------*/
{
    charptr[0] = (val >> 24) & 0xff;
    charptr[1] = (val >> 16) & 0xff;
    charptr[2] = (val >> 8) & 0xff;
    charptr[3] = val & 0xff;
}
