T168_111\system\Ethernet\pro:第1~10

asn1.c   。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

/******************************************************************************
 *                                                                            *
 *                         M O D U L E   D E F I N E                          *
 *                                                                            *
 ******************************************************************************/

#define ASN1_C

/******************************************************************************
 *                                                                            *
 *        C O M P I L E R   D E F I N E D   I N C L U D E   F I L E S         *
 *                                                                            *
 ******************************************************************************/

#include <string.h>

/******************************************************************************
 *                                                                            *
 *            U S E R   D E F I N E D   I N C L U D E   F I L E S             *
 *                                                                            *
 ******************************************************************************/

#include "Common.h"
#include "XCore.h"
#include "XNutOS.h"

#include "asn1.h"

#if defined(NUTNET)

/******************************************************************************
 *                                                                            *
 *                         L O C A L   D E F I N E S                          *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *                        L O C A L   T Y P E D E F S                         *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *             L O C A L   F U N C T I O N   P R O T O T Y P E S              *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *    L O C A L   I N I T I A L I Z E D   D A T A   D E F I N I T I O N S     *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *    L O C A L   U N I T I A L I Z E D   D A T A   D E F I N I T I O N S     *
 *                                                                            *
 ******************************************************************************/


/*
 * Abstract Syntax Notation One, ASN.1 as defined in ISO/IS 8824 and 
 * ISO/IS 8825. This implements a subset of the above international 
 * standards that is sufficient for SNMP.
 */

/*!
 * \brief Interpret the length of the current object.
 *
 * @param data   Pointer to start of length field.
 * @param length Pointer to the variable that receives the value of this 
 *               length field.
 *
 * \return A pointer to the first byte after this length field (aka: the 
 *         start of the data field). Returns NULL on any error.
 */
static CONST u_char *AsnLenParse(CONST u_char * data, u_long * length)
{
    u_char lengthbyte = *data++;

    if (lengthbyte & ASN_LONG_LEN) {
        /* Long length. */
        lengthbyte &= ~ASN_LONG_LEN;
        if (lengthbyte == 0 || lengthbyte > sizeof(long)) {
            return NULL;
        }
        *length = 0;
        while (lengthbyte--) {
            *length <<= 8;
            *length |= *data++;
        }
    } else {
        /* Short length. */
        *length = lengthbyte;
    }
    return data;
}

/*!
 * \brief Build an ASN header for a specified length.
 *
 * \param data       Pointer to start of the object.
 * \param datalength Contains the number of available bytes following 
 *                   the start of the object. On exit, it is returned 
 *                   as the number of available bytes following the
 *                   encoded length of this object.
 * \param length     Length of object.
 *
 * \return A pointer to the first byte of the contents of this object.
 *         Returns NULL on any error.
 */
static u_char *AsnLenBuild(u_char * data, size_t * datalength, size_t length)
{
    if (length < 0x80) {
        /* Check for buffer overflow. */
        if (*datalength < 1) {
            return NULL;
        }
        *datalength -= 1;
        *data++ = (u_char) length;
    } else if (length <= 0xFF) {
        /* Check for buffer overflow. */
        if (*datalength < 2) {
            return NULL;
        }
        *datalength -= 2;
        *data++ = (u_char) (0x01 | ASN_LONG_LEN);
        *data++ = (u_char) length;
    } else {
        /* Check for buffer overflow. */
        if (*datalength < 3) {
            return NULL;
        }
        *datalength -= 3;
        *data++ = (u_char) (0x02 | ASN_LONG_LEN);
        *data++ = (u_char) (((unsigned) length >> 8) & 0xFF);
        *data++ = (u_char) (length & 0xFF);
    }
    return data;
}

/*!
 * \brief Interpret the ID and length of the next object.
 *
 * \param data       Pointer to start of the object.
 * \param datalength Contains the number of valid bytes following the 
 *                   start of the object. On exit, it is returned as 
 *                   the number of valid bytes following the ID and
 *                   length.
 * \param type       Pointer to the variable that receives the ASN type 
 *                   of the object.
 * \return A pointer to the first byte following ID and length (aka the 
 *         start of the data field). Returns NULL on any error.
 */
CONST u_char *AsnHeaderParse(CONST u_char * data, size_t * datalength, u_char * type)
{
    size_t header_len;
    u_long asn_length;
    CONST u_char *bufp = data;

    if (*datalength <= 0) {
        return NULL;
    }

    /*
     * The first byte of the header is the type. This only
     * works on data types < 30, i.e. no extension octets.
     */
    *type = *bufp;
    if ((*type & ASN_EXTENSION_ID) == ASN_EXTENSION_ID) {
        return NULL;
    }

    /*
     * Interpret the length (short or long) of this object.
     */
    if ((bufp = AsnLenParse(bufp + 1, &asn_length)) == NULL) {
        return NULL;
    }

    header_len = bufp - data;
    /* Data length exceeds packet size. */
    if (header_len + asn_length > *datalength) {
        return NULL;
    }
    *datalength = (int) asn_length;

    return bufp;
}

/*!
 * \brief Build an ASN header for an object with a given ID and length.
 *
 *  This only works on data types < 30, i.e. no extension octets.
 *  The maximum length is 0xFFFF;
 *
 * \param data       Pointer to start of object.
 * \param datalength Contains the number of available bytes following 
 *                   the start of the object. On exit, it is returned 
 *                   as the number of available bytes following the
 *                   encoded ID and length of this object.
 * \param type       ASN type of the object.
 * \param length     Length of object.
 *
 * \return Returns a pointer to the first byte of the contents of this object.
 *          Returns NULL on any error.
 */
u_char *AsnHeaderBuild(u_char * data, size_t * datalength, u_char type, size_t length)
{
    if (*datalength < 1) {
        return NULL;
    }
    *data++ = type;
    (*datalength)--;

    return AsnLenBuild(data, datalength, length);
}

/*!
 * \brief Check the type and get the length of the next object.
 *
 * Similare to AsnHeaderParse, but tests for expected type.
 *
 * \param data       Pointer to start of the object.
 * \param datalength Contains the number of valid bytes following the 
 *                   start of the object. On exit, it is returned as 
 *                   the number of valid bytes following the ID and
 *                   length.
 * \param type       The expected ASN type of the object.
 *
 * \return A pointer to the first byte following ID and length (aka the 
 *         start of the data field). Returns NULL on any error.
 */
CONST u_char *AsnSequenceParse(CONST u_char * data, size_t * datalength, u_char type)
{
    u_char t;

    if ((data = AsnHeaderParse(data, datalength, &t)) != NULL) {
        if (t != type) {
            data = NULL;
        }
    }
    return data;
}

/*!
 * \brief Build an ASN header for a sequence with a given type and length.
 *
 *  This only works on data types < 30, i.e. no extension octets.
 *  The maximum length is 0xFFFF;
 *
 * \param data       Pointer to start of object.
 * \param datalength Contains the number of available bytes following 
 *                   the start of the object. On exit, it is returned 
 *                   as the number of available bytes following the
 *                   encoded ID and length of this object.
 * \param type       ASN type of the object.
 * \param length     Length of object.
 *
 * \return Returns a pointer to the first byte of the contents of this object.
 *          Returns NULL on any error.
 */
u_char *AsnSequenceBuild(u_char * data, size_t * datalength, u_char type, size_t length)
{
    if (*datalength < 4) {
        /* Not enough space in output packet. */
        return NULL;
    }

    *datalength -= 4;
    *data++ = type;
    *data++ = (u_char) (0x02 | ASN_LONG_LEN);
    *data++ = (u_char) (((unsigned) length >> 8) & 0xFF);
    *data++ = (u_char) length;

    return data;
}

/*!
 * \brief Pull a long out of an ASN integer type.
 *
 * \param data       Pointer to start of the object.
 * \param datalength Contains the number of valid bytes following the 
 *                   start of the object. On exit, it is returned as 
 *                   the number of valid bytes following the end of 
 *                   this object.
 * \param type       Pointer to the variable that receives the ASN type 
 *                   of the object.
 * \param intp       Pointer to the variable that receives the value
 *                   of the object.
 *
 * \return Pointer to the first byte past the end of this object 
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
CONST u_char *AsnIntegerParse(CONST u_char * data, size_t * datalength, u_char * type, long *intp)
{
    CONST u_char *bufp = data;
    u_long asn_length;

    /* Get the type. */
    *type = *bufp++;
    /* Parse the length. */
    if ((bufp = AsnLenParse(bufp, &asn_length)) == NULL) {
        return NULL;
    }
    /* Check for overflow. */
    if (asn_length > sizeof(long) || asn_length + (bufp - data) > *datalength) {
        return NULL;
    }
    /* Update the number of valid bytes. */
    *datalength -= asn_length + (bufp - data);
    /* Determine sign. */
    if (*bufp & 0x80) {
        *intp = -1;
    } else {
        *intp = 0;
    }
    /* Retrieve the value. */
    while (asn_length--) {
        *intp <<= 8;
        *intp |= *bufp++;
    }
    return bufp;
}

/*!
 * \brief Build an ASN object containing an integer.
 *
 * \param data       Pointer to start of output buffer
 * \param datalength Contains the number of available bytes following 
 *                   the start of the object. On exit, it is returned 
 *                   as the number of available bytes following the end 
 *                   of this object.
 * \param type       ASN type of the object.
 * \param intp       Value of the object.
 * 
 * \return A pointer to the first byte past the end of this object
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
u_char *AsnIntegerBuild(u_char * data, size_t * datalength, u_char type, long *intp)
{
    u_long mask;
    long value = *intp;
    size_t size = sizeof(long);

    /*
     * Truncate unnecessary bytes off of the most significant end of 
     * this 2's complement integer. Skip any leading sequence of 
     * 9 consecutive 1's or 0's.
     */
    mask = 0x1FFUL << ((8 * (sizeof(long) - 1)) - 1);
    while (((value & mask) == 0 || (value & mask) == mask) && size > 1) {
        size--;
        value <<= 8;
    }
    /* We know the size, so let's build the header. */
    if ((data = AsnHeaderBuild(data, datalength, type, size)) == NULL) {
        return NULL;
    }
    /* Check if there's enough space for the value. */
    if (*datalength < size) {
        return NULL;
    }
    *datalength -= size;

    /* Store the value, MSB first. */
    mask = 0xFFUL << (8 * (sizeof(long) - 1));
    while (size--) {
        *data++ = (u_char) ((value & mask) >> (8 * (sizeof(long) - 1)));
        value <<= 8;
    }
    return data;
}

/*!
 * \brief Pull an unsigned long out of an ASN integer type.
 *
 * \param data       Pointer to start of the object.
 * \param datalength Contains the number of valid bytes following the 
 *                   start of the object. On exit, it is returned as 
 *                   the number of valid bytes following the end of 
 *                   this object.
 * \param type       Pointer to the variable that receives the ASN type 
 *                   of the object.
 * \param intp       Pointer to the variable that receives the value
 *                   of the object.
 *
 * \return Pointer to the first byte past the end of this object 
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
CONST u_char *AsnUnsignedParse(CONST u_char * data, size_t * datalength, u_char * type, u_long * intp)
{
    CONST u_char *bufp = data;
    u_long asn_length;

    /* Get the type. */
    *type = *bufp++;
    /* Parse the length. */
    if ((bufp = AsnLenParse(bufp, &asn_length)) == NULL) {
        return NULL;
    }
    /* Check for length overflow. */
    if (asn_length > sizeof(long) + 1 || (asn_length == sizeof(long) + 1 && *bufp != 0x00)) {
        return NULL;
    }
    /* Check for sufficient data. */
    if (asn_length + (bufp - data) > *datalength) {
        return NULL;
    }
    /* Update the number of valid bytes. */
    *datalength -= (int) asn_length + (bufp - data);
    /* Determine sign. */
    if (*bufp & 0x80) {
        *intp = (u_long)-1;
    } else {
        *intp = 0;
    }
    /* Retrieve the value. */
    while (asn_length--) {
        *intp <<= 8;
        *intp |= *bufp++;
    }
    return bufp;
}

/*!
 * \brief Build an ASN object containing an unsigned integer.
 *
 * \param data       Pointer to start of output buffer
 * \param datalength Contains the number of available bytes following 
 *                   the start of the object. On exit, it is returned 
 *                   as the number of available bytes following the end 
 *                   of this object.
 * \param type       ASN type of the object.
 * \param intp       Value of the object.
 * 
 * \return A pointer to the first byte past the end of this object
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
u_char *AsnUnsignedBuild(u_char * data, size_t * datalength, u_char type, u_long * intp)
{
    int msb;
    u_long mask;
    u_long value = *intp;
    size_t size = sizeof(u_long);

    /* Check if MSB is set. */
    if (value & (0x80UL << (8 * (sizeof(long) - 1)))) {
        msb = 1;
        size++;
    } else {
        msb = 0;
        /*
         * Truncate unnecessary bytes off of the most significant end.
         * Skip any leading sequence of 9 consecutive 1's or 0's.
         */
        mask = 0x1FFUL << ((8 * (sizeof(long) - 1)) - 1);
        while ((((value & mask) == 0) || ((value & mask) == mask)) && size > 1) {
            size--;
            value <<= 8;
        }
    }
    /* We know the size, so let's build the header. */
    if ((data = AsnHeaderBuild(data, datalength, type, size)) == NULL) {
        return NULL;
    }
    /* Check if there's enough space for the value. */
    if (*datalength < size) {
        return NULL;
    }
    *datalength -= size;
    /* Add leading null byte if MSB set. */
    if (msb) {
        *data++ = '\0';
        size--;
    }
    /* Store the value, MSB first. */
    mask = 0xFFUL << (8 * (sizeof(long) - 1));
    while (size--) {
        *data++ = (u_char) ((value & mask) >> (8 * (sizeof(long) - 1)));
        value <<= 8;
    }
    return data;
}

/*!
 * \brief Pulls a string out of an ASN octet string type.
 *
 * \param data       Pointer to start of the object.
 * \param datalength Contains the number of valid bytes following the 
 *                   start of the object. On exit, it is returned as 
 *                   the number of valid bytes following the end of 
 *                   this object.
 * \param type       Pointer to the variable that receives the ASN type 
 *                   of the object.
 * \param string     Pointer to the variable that receives the value
 *                   of the object.
 * \param strlength  Contains the size of the string buffer on entry.
 *                   On exit, it is returned as the number of bytes 
 *                   stored in the string buffer.
 *
 * \return Pointer to the first byte past the end of this object 
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
CONST u_char *AsnOctetStringParse(CONST u_char * data, size_t * datalength, u_char * type, u_char * string, size_t * strlength)
{
    CONST u_char *bufp = data;
    u_long asn_length;

    /* Get the type. */
    *type = *bufp++;
    /* Get the length. */
    if ((bufp = AsnLenParse(bufp, &asn_length)) == NULL) {
        return NULL;
    }
    /* Check for overflow. */
    if (asn_length > *strlength || asn_length + (bufp - data) > *datalength) {
        return NULL;
    }
    if (asn_length) {
        /* Store value and update lengths. */
        memcpy(string, bufp, asn_length);
        *strlength = (size_t) asn_length;
    }
    *datalength -= (size_t) (asn_length + (bufp - data));

    return bufp + asn_length;
}

/*!
 * \brief Build an ASN object containing an octet string.
 *
 * \param data       Pointer to start of output buffer
 * \param datalength Contains the number of available bytes following 
 *                   the start of the object. On exit, it is returned 
 *                   as the number of available bytes following the end 
 *                   of this object.
 * \param type       ASN type of the object.
 * \param string     Pointer to the value. If NULL, the octet string will
 *                   be filled with zeros.
 * \param strlength  Number of bytes in the string value.
 * 
 * \return A pointer to the first byte past the end of this object
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
u_char *AsnOctetStringBuild(u_char * data, size_t * datalength, u_char type, CONST u_char * string, size_t strlength)
{
    if ((data = AsnHeaderBuild(data, datalength, type, strlength)) == NULL) {
        return NULL;
    }
    if (strlength) {
        /* Check for overflow. */
        if (*datalength < strlength) {
            return NULL;
        }
        *datalength -= strlength;
        if (string) {
            memcpy(data, string, strlength);
        } else {
            memset(data, 0, strlength);
        }
        data += strlength;
    }
    return data;
}

/*!
 * \brief Pulls an object identifier out of an ASN object ID type.
 *
 * \param data        Pointer to start of the object.
 * \param datalength  Contains the number of valid bytes following the 
 *                    start of the object. On exit, it is returned as 
 *                    the number of valid bytes following the end of 
 *                    this object.
 * \param type        Pointer to the variable that receives the ASN type 
 *                    of the object.
 * \param objid       Pointer to the variable that receives the object
 *                    identifier.
 * \param objidlength Points to a variable that contains the size of the 
 *                    output buffer on entry. On exit, it is returned as 
 *                    the number of sub IDs stored in the output buffer.
 *
 * \return Pointer to the first byte past the end of this object 
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
CONST u_char *AsnOidParse(CONST u_char * data, size_t * datalength, u_char * type, OID * objid, size_t * objidlength)
{
    CONST u_char *bufp = data;
    OID *oidp = objid + 1;
    u_long subidentifier;
    long length;
    u_long asn_length;

    /* Get type and length. */
    *type = *bufp++;
    if ((bufp = AsnLenParse(bufp, &asn_length)) == NULL) {
        return NULL;
    }
    /* Check for overflow and update number of remaining bytes. */
    if (asn_length + (bufp - data) > *datalength) {
        /* Message overflow. */
        return NULL;
    }
    *datalength -= (int) asn_length + (bufp - data);

    /* Handle invalid object ID encodings of the form 06 00 robustly. */
    if (asn_length == 0) {
        objid[0] = objid[1] = 0;
    }
    length = asn_length;
    /* Account for expansion of first byte. */
    (*objidlength)--;
    while (length > 0 && (*objidlength)-- > 0) {
        subidentifier = 0;
        /*
         * Shift and add in low order 7 bits.
         * Last byte has high bit clear.
         */
        do {
            subidentifier = (subidentifier << 7) + (*(u_char *) bufp & ~ASN_BIT8);
            length--;
        } while (*bufp++ & ASN_BIT8);
        *oidp++ = (OID) subidentifier;
    }

    /*
     * The first two subidentifiers are encoded into the first component 
     * with the value (X * 40) + Y, where
     *
     *  X is the value of the first subidentifier.
     *  Y is the value of the second subidentifier.
     */
    subidentifier = objid[1];
    if (subidentifier == 0x2B) {
        objid[0] = 1;
        objid[1] = 3;
    } else {
        objid[1] = (u_char) (subidentifier % 40);
        objid[0] = (u_char) ((subidentifier - objid[1]) / 40);
    }
    *objidlength = oidp - objid;

    return bufp;
}

/*!
 * \brief Build an ASN object identifier.
 *
 * \param data        Pointer to start of the object.
 * \param datalength  Contains the number of available bytes following 
 *                    the start of the object. On exit, it is returned 
 *                    as the number of available bytes following the end 
 *                    of this object.
 * \param type        ASN type of the object.
 * \param objid       Pointer to the object identifier.
 * \param objidlength Number of sub IDs in the object identifier.
 *
 * \return Pointer to the first byte past the end of this object 
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
u_char *AsnOidBuild(u_char * data, size_t * datalength, u_char type, CONST OID * objid, size_t objidlength)
{
    u_char *buf;
    u_long objid_val;
    u_long first_objid_val;
    size_t asnlength;
    CONST OID *op = objid;
    size_t i;

    if (objidlength == 0) {
        first_objid_val = 0;
        objidlength++;
    } else if (objid[0] > 2) {
        /* First sub identifier is bad. */
        return NULL;
    } else if (objidlength == 1) {
        first_objid_val = op[0] * 40;
    } else if (op[1] > 40 && op[0] < 2) {
        /* Second sub identifier is bad. */
        return NULL;
    } else if (objidlength > MAX_OID_LEN) {
        /* Bad object ID length. */
        return NULL;
    } else {
        first_objid_val = op[0] * 40 + op[1];
        objidlength--;
    }

    /*
     * Determine the number of bytes needed to store the encoded value.
     */
    if ((buf = NutHeapAlloc(MAX_OID_LEN * sizeof(OID))) == NULL) {
        return NULL;
    }
    asnlength = 0;
    for (i = 0; i < objidlength; i++) {
        if (i) {
            objid_val = *op++;
        } else {
            objid_val = first_objid_val;
            op = objid + 2;
        }
        if (objid_val < 0x80UL) {
            buf[i] = 1;
            asnlength += 1;
        } else if (objid_val < 0x4000UL) {
            buf[i] = 2;
            asnlength += 2;
        } else if (objid_val < 0x200000UL) {
            buf[i] = 3;
            asnlength += 3;
        } else if (objid_val < 0x10000000UL) {
            buf[i] = 4;
            asnlength += 4;
        } else {
            buf[i] = 5;
            asnlength += 5;
        }
    }
    /* Build the header after knowing the length. */
    if ((data = AsnHeaderBuild(data, datalength, type, asnlength)) == NULL || asnlength > *datalength) {
        NutHeapFree(buf);
        return NULL;
    }

    /*
     * Store the encoded OID value 
     */
    for (i = 0; i < objidlength; i++) {
        if (i) {
            objid_val = *op++;
        } else {
            objid_val = first_objid_val;
            op = objid + 2;
        }
        switch (buf[i]) {
        case 1:
            *data++ = (u_char) objid_val;
            break;
        case 2:
            *data++ = (u_char) ((objid_val >> 7) | 0x80);
            *data++ = (u_char) (objid_val & 0x07f);
            break;
        case 3:
            *data++ = (u_char) ((objid_val >> 14) | 0x80);
            *data++ = (u_char) ((objid_val >> 7 & 0x7f) | 0x80);
            *data++ = (u_char) (objid_val & 0x07f);
            break;
        case 4:
            *data++ = (u_char) ((objid_val >> 21) | 0x80);
            *data++ = (u_char) ((objid_val >> 14 & 0x7f) | 0x80);
            *data++ = (u_char) ((objid_val >> 7 & 0x7f) | 0x80);
            *data++ = (u_char) (objid_val & 0x07f);
            break;
        case 5:
            *data++ = (u_char) ((objid_val >> 28) | 0x80);
            *data++ = (u_char) ((objid_val >> 21 & 0x7f) | 0x80);
            *data++ = (u_char) ((objid_val >> 14 & 0x7f) | 0x80);
            *data++ = (u_char) ((objid_val >> 7 & 0x7f) | 0x80);
            *data++ = (u_char) (objid_val & 0x07f);
            break;
        }
    }

    *datalength -= asnlength;
    NutHeapFree(buf);

    return data;
}

/*!
 * \brief Parse an ASN null type.
 *
 * \param data        Pointer to start of the object.
 * \param datalength  Contains the number of valid bytes following the 
 *                    start of the object. On exit, it is returned as 
 *                    the number of valid bytes following the end of 
 *                    this object.
 * \param type        Pointer to the variable that receives the ASN type 
 *                    of the object.
 *
 * \return Pointer to the first byte past the end of this object 
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
CONST u_char *AsnNullParse(CONST u_char * data, size_t * datalength, u_char * type)
{
    CONST u_char *bufp = data;
    u_long asn_length;

    *type = *bufp++;
    if ((bufp = AsnLenParse(bufp, &asn_length)) == NULL) {
        return NULL;
    }
    if (asn_length) {
        return NULL;
    }
    *datalength -= (bufp - data);

    return bufp;
}

/*!
 * \brief Build an ASN null object.
 *
 * \param data        Pointer to start of the object.
 * \param datalength  Contains the number of available bytes following 
 *                    the start of the object. On exit, it is returned 
 *                    as the number of available bytes following the end 
 *                    of this object.
 * \param type        ASN type of the object.
 *
 * \return Pointer to the first byte past the end of this object 
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
u_char *AsnNullBuild(u_char * data, size_t * datalength, u_char type)
{
    return AsnHeaderBuild(data, datalength, type, 0);
}

/*!
 * \brief Pull a bitstring out of an ASN bitstring type.
 *
 * \param data       Pointer to start of the object.
 * \param datalength Contains the number of valid bytes following the 
 *                   start of the object. On exit, it is returned as 
 *                   the number of valid bytes following the end of 
 *                   this object.
 * \param type       Pointer to the variable that receives the ASN type 
 *                   of the object.
 * \param string     Pointer to the variable that receives the value
 *                   of the object.
 * \param strlength  Contains the size of the string buffer on entry.
 *                   On exit, it is returned as the number of bytes 
 *                   stored in the string buffer.
 *
 * \return Pointer to the first byte past the end of this object 
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
CONST u_char *AsnBitStringParse(CONST u_char * data, size_t * datalength, u_char * type, u_char * string, size_t * strlength)
{
    CONST u_char *bufp = data;
    u_long asn_length;

    *type = *bufp++;
    if ((bufp = AsnLenParse(bufp, &asn_length)) == NULL) {
        return NULL;
    }
    if (asn_length + (bufp - data) > *datalength) {
        return NULL;
    }
    if (asn_length < 1 || (size_t) asn_length > *strlength) {
        return NULL;
    }
    if (*bufp > 7) {
        return NULL;
    }
    memcpy(string, bufp, asn_length);
    *strlength = (size_t) asn_length;
    *datalength -= (size_t) asn_length + (bufp - data);

    return bufp + asn_length;
}

/*!
 * \brief Build an ASN bit string.
 *
 * \param data       Pointer to start of output buffer
 * \param datalength Contains the number of available bytes following 
 *                   the start of the object. On exit, it is returned 
 *                   as the number of available bytes following the end 
 *                   of this object.
 * \param type       ASN type of the object.
 * \param string     Pointer to the value. If NULL, the octet string will
 *                   be filled with zeros.
 * \param strlength  Number of bytes in the string value.
 * 
 * \return A pointer to the first byte past the end of this object
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
u_char *AsnBitStringBuild(u_char * data, size_t * datalength, u_char type, CONST u_char * string, size_t strlength)
{
    if ((data = AsnHeaderBuild(data, datalength, type, strlength)) == NULL) {
        return NULL;
    }
    if (strlength) {
        if (strlength > *datalength) {
            return NULL;
        }
        memcpy((char *) data, (char *) string, strlength);
        *datalength -= strlength;
        data += strlength;
    }
    return data;
}

/*!
 * \brief Pull a 64 bit unsigned long out of an ASN integer type.
 *
 * \param data       Pointer to start of the object.
 * \param datalength Contains the number of valid bytes following the 
 *                   start of the object. On exit, it is returned as 
 *                   the number of valid bytes following the end of 
 *                   this object.
 * \param type       Pointer to the variable that receives the ASN type 
 *                   of the object.
 * \param intp       Pointer to the variable that receives the value
 *                   of the object.
 *
 * \return Pointer to the first byte past the end of this object 
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
CONST u_char *AsnUnsigned64Parse(CONST u_char * data, size_t * datalength, u_char * type, UNSIGNED64 * cp)
{
    CONST u_char *bufp = data;
    u_long asn_length;
    u_long low = 0;
    u_long high = 0;
    size_t intsize = 4;

    *type = *bufp++;
    if ((bufp = AsnLenParse(bufp, &asn_length)) == NULL) {
        return NULL;
    }
    if (asn_length + (bufp - data) > *datalength) {
        return NULL;
    }
    if ((asn_length > (intsize * 2 + 1)) || ((asn_length == (intsize * 2) + 1) && *bufp != 0x00)) {
        return NULL;
    }
    *datalength -= (int) asn_length + (bufp - data);
    if (*bufp & 0x80) {
        low = (u_long) - 1;
        high = (u_long) - 1;
    }
    while (asn_length--) {
        high = (high << 8) | ((low & 0xFF000000) >> 24);
        low = (low << 8) | *bufp++;
    }
    cp->low = low;
    cp->high = high;
    return bufp;
}

/*!
 * \brief Build an ASN object containing a 64 bit unsigned integer.
 *
 * \param data       Pointer to start of output buffer
 * \param datalength Contains the number of available bytes following 
 *                   the start of the object. On exit, it is returned 
 *                   as the number of available bytes following the end 
 *                   of this object.
 * \param type       ASN type of the object.
 * \param intp       Value of the object.
 * 
 * \return A pointer to the first byte past the end of this object
 *         (i.e. the start of the next object). Returns NULL on any 
 *         error.
 */
u_char *AsnUnsigned64Build(u_char * data, size_t * datalength, u_char type, CONST UNSIGNED64 * cp)
{
    u_long low;
    u_long high;
    u_long mask;
    u_long mask2;
    int add_null_byte = 0;
    size_t intsize;

    intsize = 8;
    low = cp->low;
    high = cp->high;
    mask = 0xFFUL << (8 * (sizeof(long) - 1));
    /*
     * mask is 0xFF000000 on a big-endian machine
     */
    if ((u_char) ((high & mask) >> (8 * (sizeof(long) - 1))) & 0x80) {
        /* if MSB is set */
        add_null_byte = 1;
        intsize++;
    }
    /*
     * Truncate "unnecessary" bytes off of the most significant end of 
     * this 2's complement integer.
     * There should be no sequence of 9 consecutive 1's or 0's at the most
     * significant end of the integer.
     */
    mask2 = 0x1FFUL << ((8 * (sizeof(long) - 1)) - 1);
    /*
     * mask2 is 0xFF800000 on a big-endian machine
     */
    while ((((high & mask2) == 0) || ((high & mask2) == mask2))
           && intsize > 1) {
        intsize--;
        high = (high << 8)
            | ((low & mask) >> (8 * (sizeof(long) - 1)));
        low <<= 8;
    }
    data = AsnHeaderBuild(data, datalength, type, intsize);
    if (data == NULL || *datalength < intsize) {
        return NULL;
    }
    *datalength -= intsize;
    if (add_null_byte == 1) {
        *data++ = '\0';
        intsize--;
    }
    while (intsize--) {
        *data++ = (u_char) ((high & mask) >> (8 * (sizeof(long) - 1)));
        high = (high << 8)
            | ((low & mask) >> (8 * (sizeof(long) - 1)));
        low <<= 8;

    }
    return data;
}

#endif

asn1.h  。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

#ifndef ASN1_H

/******************************************************************************
 *                                                                            *
 *                         M O D U L E   D E F I N E                          *
 *                                                                            *
 ******************************************************************************/

#define ASN1_H

/******************************************************************************
 *                                                                            *
 *        C O M P I L E R   D E F I N E D   I N C L U D E   F I L E S         *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *            U S E R   D E F I N E D   I N C L U D E   F I L E S             *
 *                                                                            *
 ******************************************************************************/

#ifdef __cplusplus
extern "C" {            /* Assume C declarations for C++ */
#endif    /* __cplusplus */

/******************************************************************************
 *                                                                            *
 *                        G L O B A L   D E F I N E S                         *
 *                                                                            *
 ******************************************************************************/

#ifndef MAX_OID_LEN
#define MAX_OID_LEN     32
#endif

#define MAX_SUBID   0xFFFFFFFF

#define MIN_OID_LEN     2

#define ASN_BOOLEAN     0x01
#define ASN_INTEGER     0x02
#define ASN_BIT_STR     0x03
#define ASN_OCTET_STR   0x04
#define ASN_NULL        0x05
#define ASN_OBJECT_ID   0x06
#define ASN_SEQUENCE    0x10
#define ASN_SET         0x11

#define ASN_UNIVERSAL   0x00
#define ASN_APPLICATION 0x40
#define ASN_CONTEXT     0x80
#define ASN_PRIVATE     0xC0

#define ASN_PRIMITIVE   0x00
#define ASN_CONSTRUCTOR 0x20

#define ASN_LONG_LEN        0x80
#define ASN_EXTENSION_ID    0x1F

/* RFC 1157. */
#define ASN_IPADDRESS   (ASN_APPLICATION | 0)
#define ASN_COUNTER     (ASN_APPLICATION | 1)
#define ASN_GAUGE       (ASN_APPLICATION | 2)
#define ASN_UNSIGNED    (ASN_APPLICATION | 2)
#define ASN_TIMETICKS   (ASN_APPLICATION | 3)
#define ASN_OPAQUE      (ASN_APPLICATION | 4)

/* RFC 1442. */
#define ASN_NSAP        (ASN_APPLICATION | 5)
#define ASN_COUNTER64   (ASN_APPLICATION | 6)
#define ASN_UINTEGER    (ASN_APPLICATION | 7)

#define ACL_RONLY       0xAAAA  /* read access for everyone */
#define ACL_RWRITE      0xAABA  /* add write access for community private */
#define ACL_NOACCESS    0x0000  /* no access for anybody */

#define ASN_BIT8        0x80

/******************************************************************************
 *                                                                            *
 *                 S T R U C T U R E   D E F I N I T I O N S                  *
 *                                                                            *
 ******************************************************************************/

typedef u_long OID;

/* 
 * Internal 64 bit representation.
 */
typedef struct {
    u_long high;
    u_long low;
} UNSIGNED64;

/******************************************************************************
 *                                                                            *
 *    G L O B A L   V A R I A B L E S   -   N O   I N I T I A L I Z E R S     *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *       G L O B A L   V A R I A B L E S   -   I N I T I A L I Z E R S        *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *                   F U N C T I O N   P R O T O T Y P E S                    *
 *                                                                            *
 ******************************************************************************/

extern CONST u_char *AsnHeaderParse(CONST u_char *, size_t *, u_char *);
extern u_char *AsnHeaderBuild(u_char *, size_t *, u_char, size_t);

extern CONST u_char * AsnSequenceParse(CONST u_char *, size_t *, u_char);
extern u_char *AsnSequenceBuild(u_char *, size_t *, u_char, size_t);

extern CONST u_char *AsnIntegerParse(CONST u_char *, size_t *, u_char *, long *);
extern u_char *AsnIntegerBuild(u_char *, size_t *, u_char, long *);

extern CONST u_char *AsnUnsignedParse(CONST u_char *, size_t *, u_char *, u_long *);
extern u_char *AsnUnsignedBuild(u_char *, size_t *, u_char, u_long *);

extern CONST u_char *AsnOctetStringParse(CONST u_char *, size_t *, u_char *, u_char *, size_t *);
extern u_char *AsnOctetStringBuild(u_char *, size_t *, u_char, CONST u_char *, size_t);

extern CONST u_char *AsnOidParse(CONST u_char *, size_t *, u_char *, OID *, size_t *);
extern u_char *AsnOidBuild(u_char *, size_t *, u_char, CONST OID *, size_t);

extern CONST u_char *AsnNullParse(CONST u_char *, size_t *, u_char *);
extern u_char *AsnNullBuild(u_char *, size_t *, u_char);

extern CONST u_char *AsnBitStringParse(CONST u_char *, size_t *, u_char *, u_char *, size_t *);
extern u_char *AsnBitStringBuild(u_char *, size_t *, u_char, CONST u_char *, size_t);

extern CONST u_char *AsnUnsigned64Parse(CONST u_char *, size_t *, u_char *, UNSIGNED64 *);
extern u_char *AsnUnsigned64Build(u_char *, size_t *, u_char, CONST UNSIGNED64 *);

#ifdef __cplusplus
}                       /* End of extern "C" { */
#endif    /* __cplusplus */

#endif

asp.c              。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

/******************************************************************************
 *                                                                            *
 *                         M O D U L E   D E F I N E                          *
 *                                                                            *
 ******************************************************************************/

#define ASP_C

/******************************************************************************
 *                                                                            *
 *        C O M P I L E R   D E F I N E D   I N C L U D E   F I L E S         *
 *                                                                            *
 ******************************************************************************/

#include <string.h>

/******************************************************************************
 *                                                                            *
 *            U S E R   D E F I N E D   I N C L U D E   F I L E S             *
 *                                                                            *
 ******************************************************************************/

#include "Common.h"
#include "XCore.h"
#include "XNutOS.h"
#include "XLib.h"

#include "..\net\confnet.h"
#include "..\net\inet.h"

#include "httpd.h"
#include "asp.h"

#if defined(NUTNET)

/******************************************************************************
 *                                                                            *
 *                         L O C A L   D E F I N E S                          *
 *                                                                            *
 ******************************************************************************/

/*==========================================================*/
/*  DEFINE: All Structures and Common Constants             */
/*==========================================================*/
/*
 * Use the half size of the normal buffer, because we need
 * 2 buffers. To save memory, we have divide the size too.
 */
#define MAX_BUFFER_SIZE     256

/*
 * Do NOT use more than 250 for the len.
 * Because we use a unsigned char for the ASPFuncLen
 */
#define MAX_ASP_FUNC_SIZE   64

enum {
    ASP_STATE_IDLE = 0,
    ASP_STATE_START,
    ASP_STATE_COPY_FUNC
};

/******************************************************************************
 *                                                                            *
 *                        L O C A L   T Y P E D E F S                         *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *             L O C A L   F U N C T I O N   P R O T O T Y P E S              *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *    L O C A L   I N I T I A L I Z E D   D A T A   D E F I N I T I O N S     *
 *                                                                            *
 ******************************************************************************/

/*==========================================================*/
/*  DEFINE: Definition of all local Data                    */
/*==========================================================*/
static int (*asp_callback)(char *, FILE *) = NULL;

/******************************************************************************
 *                                                                            *
 *    L O C A L   U N I T I A L I Z E D   D A T A   D E F I N I T I O N S     *
 *                                                                            *
 ******************************************************************************/


/*==========================================================*/
/*  DEFINE: Definition of all local Procedures              */
/*==========================================================*/
/************************************************************/
/*  ProcessASPFunction                                      */
/************************************************************/
static void ProcessAspFunction(char *pASPFunction, FILE * stream)
{
    if (strstr(pASPFunction, "nut_version") != NULL) {
        fprintf(stream, "%s", "4.8.7");
        return;
    }

    if (strstr(pASPFunction, "nut_mac_addr") != NULL) {
        fprintf(stream, "%02X:%02X:%02X:%02X:%02X:%02X",
                confnet.cdn_mac[0], confnet.cdn_mac[1], confnet.cdn_mac[2],
                confnet.cdn_mac[3], confnet.cdn_mac[4], confnet.cdn_mac[5]);
        return;
    }

    if (strstr(pASPFunction, "nut_ip_addr") != NULL) {
        fputs(inet_ntoa(confnet.cdn_ip_addr), stream);
        return;
    }

    if (strstr(pASPFunction, "nut_ip_mask") != NULL) {
        fputs(inet_ntoa(confnet.cdn_ip_mask), stream);
        return;
    }

    if (strstr(pASPFunction, "nut_gateway") != NULL) {
        fputs(inet_ntoa(confnet.cdn_gateway), stream);
        return;
    }

    /*
     * Check if the user has registered an ASPCallback
     */
    if (asp_callback != NULL) {
        asp_callback(pASPFunction, stream);
    }
}

/*==========================================================*/
/*  DEFINE: All code exported                               */
/*==========================================================*/
/************************************************************/
/*  NutHttpCheckAsp                                         */
/************************************************************/
void NutHttpProcessAsp(FILE * stream, int fd, int file_len, char* http_root, REQUEST *req)
{
    int n;
    char *pReadBuffer = NULL;
    char *pWriteBuffer = NULL;
    char *pASPFunction = NULL;
    char Data;
    int Size;
    long lFileLen;
    unsigned char bASPState = ASP_STATE_IDLE;
    int ReadCount;
    int WriteCount;
    unsigned char bASPFuncLen;

    lFileLen = _filelength(fd);

    Size = MAX_BUFFER_SIZE;
    WriteCount = 0;
    bASPFuncLen = 0;
    pASPFunction = NutHeapAlloc(MAX_ASP_FUNC_SIZE);
    pReadBuffer = NutHeapAlloc(Size);
    /*
     * For our VERY SPECIAL case, the size of the WriteBuffer must have one char more
     * as the ReadBuffer. Because we must be able to save one more char from the round before.
     */
    pWriteBuffer = NutHeapAlloc(Size + 1);

    if ((pReadBuffer != NULL) && (pWriteBuffer != NULL) && (pASPFunction != NULL)) {

        while (lFileLen) {
            if (lFileLen < MAX_BUFFER_SIZE) {
                Size = (int) lFileLen;
            }

            n = _read(fd, pReadBuffer, Size);

            for (ReadCount = 0; ReadCount < n; ReadCount++) {

                Data = pReadBuffer[ReadCount];

                switch (bASPState) {
                
                case ASP_STATE_IDLE:
                    if (Data == '<') {
                        bASPState = ASP_STATE_START;
                    }
                    pWriteBuffer[WriteCount] = Data;
                    WriteCount++;
                    break;
                    /* ASP_STATE_IDLE */

                case ASP_STATE_START:
                    if (Data == '%') {
                        bASPState = ASP_STATE_COPY_FUNC;
                        if (WriteCount) {
                            WriteCount--;
                        }

                        /*
                         * Write the data up to the ASPFunction
                         */
                        if (WriteCount) {
                            fwrite(pWriteBuffer, 1, WriteCount, stream);
                            WriteCount = 0;
                        }
                    } else {
                        bASPState = ASP_STATE_IDLE;
                        pWriteBuffer[WriteCount] = Data;
                        WriteCount++;
                    }
                    break;
                    /* ASP_STATE_START_1 */

                case ASP_STATE_COPY_FUNC:
                    if (Data == '>') {
                        bASPState = ASP_STATE_IDLE;
                        pASPFunction[bASPFuncLen] = 0;

                        ProcessAspFunction(pASPFunction, stream);
                        bASPFuncLen = 0;
                    } else {
                        /*
                         * Skip over the END of an ASPFunction
                         */
                        if (Data == '%') {
                            Data = 0;
                        }

                        pASPFunction[bASPFuncLen] = Data;
                        bASPFuncLen++;
                        if (bASPFuncLen >= MAX_ASP_FUNC_SIZE) {
                            /*
                             * This make no sense, but protect our stack
                             */
                            bASPFuncLen = 0;
                        }
                    }
                    break;
                    /* ASP_STATE_COPY_FUNC */
                } /* end switch (bASPState) */
            } /* end for */

            /*
             * If data are available in the WriteBuffer, 
             * send it out...
             */
            if (WriteCount) {
                /*
                 * Now we must test a VERY SPECIAL case !
                 * It could be possible that the last char in the buffer is a '<'.
                 * Now the State is ASP_STATE_START. If the next chunk starts with a
                 * '%' it is to late, because we have send out the '<'.
                 * Therefore we must test if the last char is a '<', in this case send all the
                 * rest and save the '<' for the next round.
                 */

                if (pWriteBuffer[WriteCount - 1] == '<') {
                    WriteCount--;
                    fwrite(pWriteBuffer, 1, WriteCount, stream);

                    /*
                     * Now put the '<' in the buffer for the next round
                     */
                    pWriteBuffer[0] = '<';
                    WriteCount = 1;
                } else {
                    fwrite(pWriteBuffer, 1, WriteCount, stream);
                    WriteCount = 0;
                }
            }

            lFileLen -= (long) n;
        }
    }

    if (pReadBuffer != NULL) {
        NutHeapFree(pReadBuffer);
    }
    if (pWriteBuffer != NULL) {
        NutHeapFree(pWriteBuffer);
    }
    if (pASPFunction != NULL) {
        NutHeapFree(pASPFunction);
    }
}

/************************************************************/
/*  NutRegisterAspCallback                                  */
/*                                                          */
/*  return 0 on success, -1 otherwise.                      */
/************************************************************/
int NutRegisterAspCallback(int (*func) (char *, FILE *))
{
    register int result = 0;

    if (asp_callback == NULL) {
        asp_callback = func;
    } else {
        result = -1;
    }

    return (result);
}

/*!
 * \brief Register ASP handler for asp files.
 *
 * asp files may use the following syntax:
 *
 * <%my_function%>
 */

void NutRegisterAsp(void)
{
    NutSetMimeHandler(".asp", NutHttpProcessAsp);
}

#endif

asp.h                       。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

#ifndef ASP_H

/******************************************************************************
 *                                                                            *
 *                         M O D U L E   D E F I N E                          *
 *                                                                            *
 ******************************************************************************/

#define ASP_H

/******************************************************************************
 *                                                                            *
 *        C O M P I L E R   D E F I N E D   I N C L U D E   F I L E S         *
 *                                                                            *
 ******************************************************************************/

#include <stdio.h>

/******************************************************************************
 *                                                                            *
 *            U S E R   D E F I N E D   I N C L U D E   F I L E S             *
 *                                                                            *
 ******************************************************************************/

#ifdef __cplusplus
extern "C" {            /* Assume C declarations for C++ */
#endif    /* __cplusplus */

/******************************************************************************
 *                                                                            *
 *                        G L O B A L   D E F I N E S                         *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *                 S T R U C T U R E   D E F I N I T I O N S                  *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *    G L O B A L   V A R I A B L E S   -   N O   I N I T I A L I Z E R S     *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *       G L O B A L   V A R I A B L E S   -   I N I T I A L I Z E R S        *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *                   F U N C T I O N   P R O T O T Y P E S                    *
 *                                                                            *
 ******************************************************************************/

extern int  NutRegisterAspCallback (int (*func) (char *, FILE *));
extern void NutRegisterAsp(void);

#ifdef __cplusplus
}                       /* End of extern "C" { */
#endif    /* __cplusplus */

#endif

auth.c                  。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

/******************************************************************************
 *                                                                            *
 *                         M O D U L E   D E F I N E                          *
 *                                                                            *
 ******************************************************************************/

#define AUTH_C

/******************************************************************************
 *                                                                            *
 *        C O M P I L E R   D E F I N E D   I N C L U D E   F I L E S         *
 *                                                                            *
 ******************************************************************************/

#include <string.h>

/******************************************************************************
 *                                                                            *
 *            U S E R   D E F I N E D   I N C L U D E   F I L E S             *
 *                                                                            *
 ******************************************************************************/

#include "Common.h"
#include "XCore.h"
#include "XNutOS.h"

#include "dencode.h"
#include "httpd.h"

#if defined(NUTNET)

/******************************************************************************
 *                                                                            *
 *                         L O C A L   D E F I N E S                          *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *                        L O C A L   T Y P E D E F S                         *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *             L O C A L   F U N C T I O N   P R O T O T Y P E S              *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *    L O C A L   I N I T I A L I Z E D   D A T A   D E F I N I T I O N S     *
 *                                                                            *
 ******************************************************************************/

AUTHINFO *authList = 0;

/******************************************************************************
 *                                                                            *
 *    L O C A L   U N I T I A L I Z E D   D A T A   D E F I N I T I O N S     *
 *                                                                            *
 ******************************************************************************/


/*!
 * \brief Look up an authorization entry.
 */
static AUTHINFO *NutHttpAuthLookup(CONST char *dirname, CONST char *login)
{
    AUTHINFO *auth;

    for (auth = authList; auth; auth = auth->auth_next) {
        if (dirname && (strstr(dirname, auth->auth_dirname) != dirname))
            continue;
        if (login && strcmp(login, auth->auth_login))
            continue;
        break;
    }
    return auth;
}

/*!
 * \brief Register an authorization entry.
 *
 * Protect a specified directory from unauthorized access.
 *
 * \warning Directories not registered by this function are
 *          accessible by anyone.
 *
 * \param dirname Name of the directory to protect.
 * \param login   Required login to access this directory. This
 *                string must contain a user name, followed by
 *                a colon followed by an uncrypted password.
 *
 * \return 0 on success, -1 otherwise.
 */
int NutRegisterAuth(CONST char *dirname, CONST char *login)
{
    AUTHINFO *auth;

    /* Allocate a new list element. */
    if ((auth = NutHeapAlloc(sizeof(AUTHINFO))) != NULL) {
        auth->auth_next = authList;
        /* Allocate the path component. */
        if ((auth->auth_dirname = NutHeapAlloc(strlen(dirname) + 1)) != NULL) {
            strcpy(auth->auth_dirname, dirname);
            /* Allocate the login component. */
            if ((auth->auth_login = NutHeapAlloc(strlen(login) + 1)) != NULL) {
                strcpy(auth->auth_login, login);
                /* Success. Add element to the list and return. */
                authList = auth;
                return 0;
            }
            /* Allocation failed. */
            NutHeapFree(auth->auth_dirname);
        }
        NutHeapFree(auth);
    }
    return -1;
}


/*!
 * \brief Clear all authorization entries.
 *
 * Clears all authorization entries and frees the used ressouces.
 *
 */
void NutClearAuth(void)
{
    AUTHINFO *auth;

    while (authList) {
        auth = authList;
        authList = auth->auth_next;
        NutHeapFree(auth->auth_dirname);
        NutHeapFree(auth->auth_login);
        NutHeapFree(auth);
    }
}

/*!
 * \brief Validate an authorization request.
 *
 * \note This function is automatically called by the HTTP
 *       library on incoming requests. Applications do not
 *       need to call this function.
 *
 * \param req Request to be checked.
 *
 * \return 0, if access granted, -1 otherwise.
 */
int NutHttpAuthValidate(REQUEST * req)
{
    char *realm;
    char *cp = 0;
    int rc = -1;

    /*
     * Get directory by chopping off filename.
     */
    realm = req->req_url;
    if ((cp = strrchr(realm, '/')) != 0)
        *cp = 0;
    else
        realm = ".";

    /*
     * Check if authorization required.
     */
    if (NutHttpAuthLookup(realm, 0)) {
        /*
         * Check authorization.
         */
        if (req->req_auth) {
            /*
             * Acceptint basic authorization only.
             */
            if (strncmp(req->req_auth, "Basic ", 6) == 0) {
                NutDecodeBase64(req->req_auth + 6);
                if (NutHttpAuthLookup(realm, req->req_auth + 6))
                    rc = 0;
            }
        }
    } else
        rc = 0;

    if (cp)
        *cp = '/';

    return rc;
}

#endif

cgi.c                  。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

/******************************************************************************
 *                                                                            *
 *                         M O D U L E   D E F I N E                          *
 *                                                                            *
 ******************************************************************************/

#define CGI_C

/******************************************************************************
 *                                                                            *
 *        C O M P I L E R   D E F I N E D   I N C L U D E   F I L E S         *
 *                                                                            *
 ******************************************************************************/

#include <string.h>

/******************************************************************************
 *                                                                            *
 *            U S E R   D E F I N E D   I N C L U D E   F I L E S             *
 *                                                                            *
 ******************************************************************************/

#include "Common.h"
#include "XCore.h"
#include "XNutOS.h"
#include "XLib.h"

#include "httpd.h"

#if defined(NUTNET)

/******************************************************************************
 *                                                                            *
 *                         L O C A L   D E F I N E S                          *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *                        L O C A L   T Y P E D E F S                         *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *             L O C A L   F U N C T I O N   P R O T O T Y P E S              *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *    L O C A L   I N I T I A L I Z E D   D A T A   D E F I N I T I O N S     *
 *                                                                            *
 ******************************************************************************/

CGIFUNCTION *volatile cgiFunctionList = 0;
char *cgiBinPath = NULL;

/******************************************************************************
 *                                                                            *
 *    L O C A L   U N I T I A L I Z E D   D A T A   D E F I N I T I O N S     *
 *                                                                            *
 ******************************************************************************/


/*!
 * \brief Register a new cgi-bin path.
 *
 * This function allows to redfine the cgi-bin path. Default is "cgi-bin/"
 * \param path New path.
 */
void NutRegisterCgiBinPath(char *path)
{
    if (cgiBinPath) {
        NutHeapFree(cgiBinPath);
    }

    cgiBinPath = NutHeapAlloc(strlen(path) + 1);
    strcpy(cgiBinPath, path);
}

/*!
 * \brief Check if request is a cgi call.
 *
 * This functions checks the request if it's a cgi all and in case calls the cgi
 *
 * \param stream Stream of the socket connection, previously opened for 
 *               binary read and write.
 * \param req    Contains the HTTP request.  
 */
int NutCgiCheckRequest(FILE * stream, REQUEST * req)
{
    /*
     * My version sticks with basic C routines. I hope, that it is also
     * faster and uses less memory. But it hasn't been tested very well,
     * so let's keep Ole's variant for GCC.
     */
    size_t len;
    CONST char *cp;
    CONST char *cgi_bin = cgiBinPath ? cgiBinPath : "cgi-bin/";

    while (*cgi_bin) {
        /* Skip leading path separators. */
        while (*cgi_bin == ';')
            cgi_bin++;
        /* Determine the length of the next path component. */
        for (len = 0, cp = cgi_bin; *cp && *cp != ';'; len++, cp++);
        if (len) {
            /* Check if the URL starts with this component. If yes, run the CGI. */
            if (strncmp(req->req_url, cgi_bin, len) == 0) {
                NutCgiProcessRequest(stream, req, len);
                return 1;
            }
            /* Try the next path component. */
            cgi_bin += len;
        }
    }
    return 0;
}

/*!
 * \brief Register a CGI function.
 *
 * \param name Name of this CGI function. No dublicates allowed
 * \param func The function to be called, if the
 *             client requests the specified name.
 *
 * \return 0 on success, -1 otherwise.
 */
int NutRegisterCgi(char *name, int (*func) (FILE *, REQUEST *))
{
    int unique_name = 1;
    CGIFUNCTION *cgi;

    cgi = cgiFunctionList;
    while (cgi != NULL) {
        if (strcmp(name, cgi->cgi_name) == 0) {
            unique_name = 0;
            break;
        }
        cgi = cgi->cgi_next;
    }

    if ((!unique_name) || ((cgi = NutHeapAlloc(sizeof(CGIFUNCTION))) == 0)) {
        return -1;
    }
    cgi->cgi_next = cgiFunctionList;
    cgi->cgi_name = NutHeapAlloc(strlen(name)+1);
    strcpy((char *)cgi->cgi_name, name);
    cgi->cgi_func = func;
    cgiFunctionList = cgi;

    return 0;
}

/*!
 * \brief Process an incoming CGI request.
 *
 * Applications do not need to call this function. It
 * is automatically called by NutHttpProcessRequest().
 *
 * \param stream Stream of the socket connection, previously opened for 
 *               binary read and write.
 * \param req    Contains the HTTP request.
 */
void NutCgiProcessRequest(FILE * stream, REQUEST * req, int name_pos)
{
    CGIFUNCTION *cgi;

    if (req->req_method != METHOD_GET && req->req_method != METHOD_POST) {
        NutHttpSendError(stream, req, 501);
        return;
    }
    for (cgi = cgiFunctionList; cgi; cgi = cgi->cgi_next) {
        if (strcmp(cgi->cgi_name, req->req_url + name_pos) == 0)
            break;
    }
    if (cgi == 0)
        NutHttpSendError(stream, req, 404);
    else if ((*cgi->cgi_func) (stream, req))
        NutHttpSendError(stream, req, 500);
    return;
}

#endif

dencode.c                  。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

/******************************************************************************
 *                                                                            *
 *                         M O D U L E   D E F I N E                          *
 *                                                                            *
 ******************************************************************************/

#define DENCODE_C

/******************************************************************************
 *                                                                            *
 *        C O M P I L E R   D E F I N E D   I N C L U D E   F I L E S         *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *            U S E R   D E F I N E D   I N C L U D E   F I L E S             *
 *                                                                            *
 ******************************************************************************/
 
#include "Common.h"
#include "XCore.h"

#include "dencode.h"

#if defined(NUTNET)

/******************************************************************************
 *                                                                            *
 *                         L O C A L   D E F I N E S                          *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *                        L O C A L   T Y P E D E F S                         *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *             L O C A L   F U N C T I O N   P R O T O T Y P E S              *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *    L O C A L   I N I T I A L I Z E D   D A T A   D E F I N I T I O N S     *
 *                                                                            *
 ******************************************************************************/

/* Since base-64 encodes strings do not have any character above 127,
 * we need just the first 128 bytes. Furthermore there is no char
 * below 32, so we can save 32 additional bytes of flash.
 */
static const char base64dtab[96] = {
/*
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
*/
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
/*
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 */
};

static const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/******************************************************************************
 *                                                                            *
 *    L O C A L   U N I T I A L I Z E D   D A T A   D E F I N I T I O N S     *
 *                                                                            *
 ******************************************************************************/


/* Base-64 decoding.  This represents binary data as printable ASCII
** characters.  Three 8-bit binary bytes are turned into four 6-bit
** values, like so:
**
**   [11111111]  [22222222]  [33333333]
**
**   [111111] [112222] [222233] [333333]
**
** Then the 6-bit values are represented using the characters "A-Za-z0-9+/".
*/

int NutEncodeBase64(const void *data, int len, char *out)
{
    const u_char *in = data;
    u_char buf[3];
    int in_pos, out_pos;
    int num, dummy;

    in_pos = 0;
    out_pos = 0;
    while (in_pos < len) {
        for (num = 0; num < 3 && in_pos < len; num++)
            buf[num] = in[in_pos++];
        for (dummy = num; dummy < 3; dummy++)
            buf[dummy] = 0;
        out[out_pos++] = base64chars[buf[0] >> 2];
        out[out_pos++] = base64chars[(buf[0] & 0x3) << 4 | (buf[1] >> 4)];
        out[out_pos++] = (num >= 2) ? base64chars[(buf[1] & 0xf) << 2 | (buf[2] >> 6)] : '=';
        out[out_pos++] = (num >= 3) ? base64chars[buf[2] & 0x3f] : '=';
    }
    out[out_pos] = '\0';
    return out_pos;
}

/*
 * Do base-64 decoding on a string. Ignore any non-base64 bytes.
 * Return the actual number of bytes generated. The decoded size will
 * be at most 3/4 the size of the encoded, and may be smaller if there
 * are padding characters (blanks, newlines).
 */
char *NutDecodeBase64(char * str)
{
    /* bug fix from Damian Slee. */
    char code;
    char *sp;
    char *tp;
    char last = -1;
    char step = 0;

    for (tp = sp = str; *sp; ++sp) {
        if (*sp < 32)
            continue;
        if ((code = *(const char *)(&base64dtab[(int) *sp - 32])) == (char)-1)
            continue;
        switch (step++) {
        case 1:
            *tp++ = ((last << 2) | ((code & 0x30) >> 4));
            break;
        case 2:
            *tp++ = (((last & 0xf) << 4) | ((code & 0x3c) >> 2));
            break;
        case 3:
            *tp++ = (((last & 0x03) << 6) | code);
            step = 0;
            break;
        }
        last = code;
    }
    *tp = 0;
    return str;
}

int NutDecodeHex(char c)
{
    if (c >= '0' && c <= '9')
        return c - '0';
    if (c >= 'a' && c <= 'f')
        return c - 'a' + 10;
    if (c >= 'A' && c <= 'F')
        return c - 'A' + 10;
    return -1;
}

char *NutDecodePath(char *path)
{
    int x1;
    int x2;
    char *src = path;
    char *dst = path;
    char last = *path;

    if (last != '/')
        return 0;
    while (*++src) {
        if (*src == '%' &&
            (x1 = NutDecodeHex(*(src + 1))) >= 0 &&
            (x2 = NutDecodeHex(*(src + 2))) >= 0) {
            src += 2;
            if ((*src = x1 * 16 + x2) == 0)
                break;
        }
        if (*src == '\\')
            *src = '/';
        if ((last != '.' && last != '/') || (*src != '.' && *src != '/'))
            *dst++ = last = *src;

    }
    *dst = 0;

    return path;
}

#endif

dencode.h                          。。。。。。。。。。。。。。。。。。。。。。。。。。

#ifndef DENCODE_H

/******************************************************************************
 *                                                                            *
 *                         M O D U L E   D E F I N E                          *
 *                                                                            *
 ******************************************************************************/

#define DENCODE_H

/******************************************************************************
 *                                                                            *
 *        C O M P I L E R   D E F I N E D   I N C L U D E   F I L E S         *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *            U S E R   D E F I N E D   I N C L U D E   F I L E S             *
 *                                                                            *
 ******************************************************************************/

#ifdef __cplusplus
extern "C" {            /* Assume C declarations for C++ */
#endif    /* __cplusplus */

/******************************************************************************
 *                                                                            *
 *                        G L O B A L   D E F I N E S                         *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *                 S T R U C T U R E   D E F I N I T I O N S                  *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *    G L O B A L   V A R I A B L E S   -   N O   I N I T I A L I Z E R S     *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *       G L O B A L   V A R I A B L E S   -   I N I T I A L I Z E R S        *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *                   F U N C T I O N   P R O T O T Y P E S                    *
 *                                                                            *
 ******************************************************************************/

extern int NutEncodeBase64(const void *in_data, int in_length, char *out);
extern char *NutDecodeBase64(char *str);
extern int NutDecodeHex(char c);
extern char *NutDecodePath(char *path);

#ifdef __cplusplus
}                       /* End of extern "C" { */
#endif    /* __cplusplus */

#endif

dhcp.h                  。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

#ifndef DHCP_H

/******************************************************************************
 *                                                                            *
 *                         M O D U L E   D E F I N E                          *
 *                                                                            *
 ******************************************************************************/

#define DHCP_H

/******************************************************************************
 *                                                                            *
 *        C O M P I L E R   D E F I N E D   I N C L U D E   F I L E S         *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *            U S E R   D E F I N E D   I N C L U D E   F I L E S             *
 *                                                                            *
 ******************************************************************************/

#ifdef __cplusplus
extern "C" {            /* Assume C declarations for C++ */
#endif    /* __cplusplus */

/******************************************************************************
 *                                                                            *
 *                        G L O B A L   D E F I N E S                         *
 *                                                                            *
 ******************************************************************************/

/*!
 * \name DHCP Client States.
 *
 * Applications can request the current state of the DHCP client by 
 * calling NutDhcpStatus().
 */

/*! \brief DHCP state: Stopped.
 *
 * Indicates that the DHCP client is inactive. Either it just started,
 * gave up the lease or ran into an error. In the latter case the
 * application may call NutDhcpError() to retrieve the specific
 * error code.
 *
 * In order to save heap memory, the client will not open the UDP socket
 * while inactive.
 */
#define DHCPST_IDLE         0

/*! \brief DHCP state: Starting.
 * 
 * Indicates that the DHCP client started to request a configuration from
 * the DHCP server. If any previously allocated IP address is available
 * in the non-volatile configuration memory, then the client will continue 
 * with \ref DHCPST_REBOOTING. Otherwise it will move to 
 * \ref DHCPST_SELECTING.
 */
#define DHCPST_INIT         1

/*! \brief DHCP state: Selecting.
 *
 * In this state the client will transmit a DHCP discover message and
 * collect incoming offers from DHCP servers. If at least one acceptable
 * offer has been received, it will change to \ref DHCPST_REQUESTING.
 */
#define DHCPST_SELECTING    2

/*! \brief DHCP state: Requesting.
 *
 * Indicates that the client received and selected an offer from a
 * DHCP server. It is now sending a request for an offered 
 * configuration and waiting for an acknowledge, which will change
 * the client's state to \ref DHCPST_BOUND.
 */
#define DHCPST_REQUESTING   3

/*! \brief DHCP state: Rebooting.
 *
 * The client enters this state to request a previously assigned
 * configuration.
 */
#define DHCPST_REBOOTING    4

/*! \brief DHCP state: Bound.
 *
 * This state indicates, that the DHCP client has successfully allocated
 * a network address. Any thread blocked in NutDhcpIfConfig() will be
 * woken up.
 *
 * The client remains in this state until the renewal time elapses, in
 * which case it moves to \ref DHCPST_RENEWING.
 *
 * In order to save heap memory, the client will close the UDP socket
 * while in bound state.
 */
#define DHCPST_BOUND        5

/*! \brief DHCP state: Renewing.
 *
 * In this state the client tries to extend its lease by sending
 * a request to the DHCP server. If the server responds with an
 * acknowledge, the client returns to \ref DHCPST_BOUND.
 *
 * If no acknowledge is received until the rebind time elapses,
 * the client moves to \ref DHCPST_REBINDING.
 */
#define DHCPST_RENEWING     6

/*! \brief DHCP state: Rebinding.
 *
 * The client enters this state after the no acknowledge has been
 * received during \ref DHCPST_RENEWING and the rebind time
 * elapsed. It will broadcast a request to extend its lease.
 *
 * If no acknowledge is received until the lease time elapsed,
 * the client will move to \ref DHCPST_INIT.
 */
#define DHCPST_REBINDING    7

/*! \brief DHCP state: Informing.
 *
 * The client enters this state when the application calls
 * NutDhcpInform().
 */
#define DHCPST_INFORMING    8

/*! \brief DHCP state: Releasing.
 * 
 * The client enters this state when the application calls
 * NutDhcpRelease().
 */
#define DHCPST_RELEASING    9


/*!
 * \name DHCP Error Codes
 *
 * Applications can request the lastest error code of the DHCP client by 
 * calling NutDhcpError().
 */

/*!
 * \brief DHCP timeout error.
 *
 * No server response within the specified number of milliseconds. Either
 * the timeout value has been too low or no DHCP server is available in
 * the local network.
 */
#define DHCPERR_TIMEOUT     1

/*!
 * \brief DHCP MAC error.
 *
 * No Ethernet MAC address found in the non-volatile configuration memory
 * and none specified by calling NutDhcpIfConfig().
 */
#define DHCPERR_NOMAC       2

/*!
 * \brief DHCP state error.
 *
 * Either NutDhcpInform() has been called while not in state 
 * \ref DHCPST_IDLE or NutDhcpRelease() has been called while not in
 * state \ref DHCPST_BOUND.
 */
#define DHCPERR_STATE       3

/*!
 * \brief DHCP error: Bad device.
 *
 * The specified device name hasn't been registered or is not an
 * Ethernet device.
 */
#define DHCPERR_BADDEV      17

/*!
 * \brief DHCP system error.
 *
 * A system error occured during DHCP processing. Most probably the
 * system ran out of memory.
 */
#define DHCPERR_SYSTEM      18

/*!
 * \brief DHCP transmit error.
 *
 * Failed to send a UDP datagram. The DHCP client considers it a fatal
 * error if NutUdpSendTo() to the broadcast address fails.
 */
#define DHCPERR_TRANSMIT    19

/*!
 * \brief DHCP receive error.
 *
 * Failed to receive a UDP datagram. The DHCP client considers it a fatal
 * error if NutUdpReceiveFrom() fails.
 */
#define DHCPERR_RECEIVE     20

/******************************************************************************
 *                                                                            *
 *                 S T R U C T U R E   D E F I N I T I O N S                  *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *    G L O B A L   V A R I A B L E S   -   N O   I N I T I A L I Z E R S     *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *       G L O B A L   V A R I A B L E S   -   I N I T I A L I Z E R S        *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *                   F U N C T I O N   P R O T O T Y P E S                    *
 *                                                                            *
 ******************************************************************************/

int NutDhcpIfConfig(CONST char *name, u_char * mac, u_long timeout);
int NutDhcpRelease(CONST char *name, u_long timeout);
int NutDhcpInform(CONST char *name, u_long timeout);
int NutDhcpStatus(CONST char *name);
int NutDhcpError(CONST char *name);

int NutDhcpIsConfigured(void);

#ifdef __cplusplus
}                       /* End of extern "C" { */
#endif    /* __cplusplus */

#endif

dhcpc.c                     。。。。。。。。。。。。。。。。。。。。。。。。。。

/******************************************************************************
 *                                                                            *
 *                         M O D U L E   D E F I N E                          *
 *                                                                            *
 ******************************************************************************/

#define DHCPC_C

/******************************************************************************
 *                                                                            *
 *        C O M P I L E R   D E F I N E D   I N C L U D E   F I L E S         *
 *                                                                            *
 ******************************************************************************/

#include <string.h>
#include <time.h>

/******************************************************************************
 *                                                                            *
 *            U S E R   D E F I N E D   I N C L U D E   F I L E S             *
 *                                                                            *
 ******************************************************************************/

#include "Common.h"
#include "XCore.h"
#include "XNutOS.h"

#include "..\net\confnet.h"
#include "..\net\inet.h"
#include "..\net\in.h"
#include "..\net\netdb.h"
#include "..\net\route.h"
#include "..\net\socket.h"
#include "dhcp.h"

#ifdef NUTDEBUG
#include "..\net\netdebug.h"
#endif

#if 0
/* Use for local debugging. */
#define NUTDEBUG
#include <stdio.h>
#define __tcp_trs stdout
static u_char __tcp_trf = 1;
#endif

#if defined(NUTNET)

/******************************************************************************
 *                                                                            *
 *                         L O C A L   D E F I N E S                          *
 *                                                                            *
 ******************************************************************************/

/*!
 * \name DHCP Client Configuration
 *
 * The Nut/OS Configurator may be used to override the default values.
 */

/*!
 * \brief UDP port of DHCP server.
 *
 * \showinitializer
 */
#ifndef DHCP_SERVERPORT
#define DHCP_SERVERPORT      67
#endif

/*!
 * \brief UDP port of DHCP client.
 *
 * \showinitializer
 */
#ifndef DHCP_CLIENTPORT
#define DHCP_CLIENTPORT      68
#endif

/*!
 * \brief Maximum DHCP message size we can accept.
 *
 * RFC 2131 demands, that a DHCP client must be prepared to receive DHCP 
 * messages with an options field length of at least 312 octets. This
 * implies that we must be able to accept messages of up to 576 octets.
 *
 * \showinitializer
 */
#ifndef MAX_DHCP_MSGSIZE
#define MAX_DHCP_MSGSIZE    576
#endif

/*!
 * \brief Minimum DHCP message length.
 *
 * Used to maintain BOOTP compatibility of outgoing messages.
 *
 * \showinitializer
 */
#ifndef MIN_DHCP_MSGSIZE
#define MIN_DHCP_MSGSIZE    300
#endif

/*!
 * \brief Maximum UDP buffer size used by the DHCP client.
 *
 * If this item is not equal zero, the DHCP client will use its value to 
 * set #SO_RCVBUF by calling NutUdpSetSockOpt().
 *
 * If this item is set to zero, NutUdpSetSockOpt() is not called and the 
 * UDP socket interface will buffer the last incoming datagram on the 
 * #DHCP_CLIENTPORT socket port only. Any previously received datagram is 
 * silently discarded. As long as one DHCP server is expected in the local
 * network, this will be fine and save some heap memory while DHCP is
 * active.
 *
 * \showinitializer
 */
#ifndef MAX_DHCP_BUFSIZE
#define MAX_DHCP_BUFSIZE    1728
#endif

/*!
 * \brief Minimum number of milliseconds to wait for a response.
 *
 * If we receive no response from a DHCP server within this time, we 
 * will double this value up to \ref MAX_DHCP_WAIT and repeat our 
 * request up to \ref MAX_DCHP_RETRIES times before giving up.
 *
 * \showinitializer
 */
#ifndef MIN_DHCP_WAIT
#define MIN_DHCP_WAIT       4000
#endif

/*!
 * \brief Maximum number of milliseconds to wait for a response.
 *
 * The timeout value for receiving server responses will be doubled
 * on each retry but limited by this value.
 *
 * \showinitializer
 */
#ifndef MAX_DHCP_WAIT
#define MAX_DHCP_WAIT       64000
#endif

/*!
 * \brief Maximum number of request retries.
 *
 * We will give up after resending this number of requests without 
 * receiving a response.
 *
 * \showinitializer
 */
#ifndef MAX_DCHP_RETRIES
#define MAX_DCHP_RETRIES    3
#endif

/*!
 * \brief Maximum number of release retries.
 *
 * RFC 2131 doesn't specify a server response to release messages from
 * the client. If the message gets lost, then the lease isn't released.
 *
 * \showinitializer
 */
#ifndef MAX_DCHP_RELEASE_RETRIES
#define MAX_DCHP_RELEASE_RETRIES    0
#endif

/*!
 * \brief Default lease time in seconds.
 *
 * This value is used if the server doesn't provide a lease time.
 *
 * \showinitializer
 */
#ifndef DHCP_DEFAULT_LEASE
#define DHCP_DEFAULT_LEASE  43200
#endif

/*!
 * \brief Maximum sleep time in seconds.
 *
 * \showinitializer
 */
#ifndef MAX_DHCP_NAPTIME
#define MAX_DHCP_NAPTIME    4294967
#endif


/*!
 * \name DHCP Message Types
 *
 * See RFC 2131.
 */

/*! \brief Client broadcast to locate available servers.
 */
#define DHCP_DISCOVER   1

/*! \brief Server to client in response to DHCP_DISCOVER.
 *
 * Contains an offer of configuration parameters.
 */
#define DHCP_OFFER      2

/*! \brief Client message to servers.
 *
 * Used for 
 * - requesting offered parameters from one server and implicitly declining offers from all others.
 * - confirming correctness of previously allocated address after, e.g., system reboot.
 * - extending the lease on a particular network address.
 */
#define DHCP_REQUEST    3

/*! \brief Client to server indicating network address is already in use.
 *
 * Not used by Nut/Net.
 */
#define DHCP_DECLINE    4

/*! \brief Server to client with configuration parameters.
 *
 * Contains committed network address.
 */
#define DHCP_ACK        5

/*! \brief Server to client indicating client's notion of network address is incorrect.
 *
 * May be caused by the client's move to new subnet or by expiration of the client's lease.
 */
#define DHCP_NAK        6

/*! \brief Client to server relinquishing network address and cancelling remaining lease.
 */
#define DHCP_RELEASE    7

/*! \brief Client to server, asking only for local configuration parameters.
 *
 * Used, if the client already has externally configured network address.
 */
#define DHCP_INFORM     8


/*!
 * \name DHCP Options
 *
 * Nut/Net recognizes a subset of options defined in RFC 2132.
 */

/*!
 * \brief DHCP pad option.
 *
 * The pad option can be used to cause subsequent fields to align on 
 * word boundaries.
 */
#define DHCPOPT_PAD          0

/*!
 * \brief DHCP subnet mask option.
 */
#define DHCPOPT_NETMASK      1

/*!
 * \brief DHCP router option.
 */
#define DHCPOPT_GATEWAY      3

/*!
 * \brief DHCP domain name server option.
 */
#define DHCPOPT_DNS          6

/*!
 * \brief DHCP host name option.
 */
#define DHCPOPT_HOSTNAME     12

/*!
 * \brief DHCP domain name option.
 */
#define DHCPOPT_DOMAIN       15

/*!
 * \brief DHCP broadcast address option.
 */
#define DHCPOPT_BROADCAST    28

/*!
 * \brief DHCP requested IP address option.
 */
#define DHCPOPT_REQESTIP     50

/*!
 * \brief DHCP IP address lease time option.
 */
#define DHCPOPT_LEASETIME    51

/*!
 * \brief DHCP message type option.
 */
#define DHCPOPT_MSGTYPE      53

/*!
 * \brief DHCP server identifier option.
 */
#define DHCPOPT_SID          54

/*!
 * \brief DHCP parameter request list option.
 */
#define DHCPOPT_PARAMREQUEST 55

/*!
 * \brief Maximum DHCP message size option.
 */
#define DHCPOPT_MAXMSGSIZE   57

/*!
 * \brief DHCP renewal time option.
 */
#define DHCPOPT_RENEWALTIME  58

/*!
 * \brief DHCP rebinding time option.
 */
#define DHCPOPT_REBINDTIME   59

/*!
 * \brief DHCP end option.
 */
#define DHCPOPT_END          255

/******************************************************************************
 *                                                                            *
 *                        L O C A L   T Y P E D E F S                         *
 *                                                                            *
 ******************************************************************************/

/*!
 * \brief BOOTP message structure type.
 */
typedef struct bootp BOOTP;

/*!
 * \brief BOOTP message structure.
 */
#if defined(__ICCARM__)
#pragma diag_suppress = Pa039    // Use of address of unaligned structure member
#pragma pack ( 1 )
#endif

struct bootp {
    u_char bp_op;               /*!< \brief Packet opcode type: 1=request, 2=reply */
    u_char bp_htype;            /*!< \brief Hardware address type: 1=Ethernet */
    u_char bp_hlen;             /*!< \brief Hardware address length: 6 for Ethernet */
    u_char bp_hops;             /*!< \brief Gateway hops */
    u_long bp_xid;              /*!< \brief Transaction ID */
    u_short bp_secs;            /*!< \brief Seconds since boot began */
    u_short bp_flags;           /*!< \brief RFC1532 broadcast, etc. */
    u_long bp_ciaddr;           /*!< \brief Client IP address */
    u_long bp_yiaddr;           /*!< \brief 'Your' IP address */
    u_long bp_siaddr;           /*!< \brief Server IP address */
    u_long bp_giaddr;           /*!< \brief Gateway IP address */
    u_char bp_chaddr[16];       /*!< \brief Client hardware address */
    char bp_sname[64];          /*!< \brief Server host name */
    char bp_file[128];          /*!< \brief Boot file name */
    u_char bp_options[312];     /*!< \brief Vendor-specific area */
};

#if defined(__ICCARM__)
#pragma pack ( )
#endif

/*!
 * \brief Dynamic configuration structure type.
 */
typedef struct dyn_cfg DYNCFG;

/*!
 * \brief Dynamic configuration structure.
 */
struct dyn_cfg {
    u_char dyn_msgtyp;          /*!< \brief DHCP message type */
    u_long dyn_yiaddr;          /*!< \brief Offered IP address. */
    u_long dyn_netmask;         /*!< \brief Local IP netmask. */
    u_long dyn_broadcast;       /*!< \brief Local IP broadcast address. */
    u_long dyn_gateway;         /*!< \brief Default gate IP address. */
    u_long dyn_pdns;            /*!< \brief Primary DNS IP address. */
    u_long dyn_sdns;            /*!< \brief Secondary DNS IP address. */
    u_long dyn_sid;             /*!< \brief Server identifier. */
    u_long dyn_renewalTime;     /*!< \brief Renewal time in seconds. */
    u_long dyn_rebindTime;      /*!< \brief Rebind time in seconds. */
    u_long dyn_leaseTime;       /*!< \brief Offered lease time in seconds. */
    u_char *dyn_hostname;       /*!< \brief Local hostname. */
    u_char *dyn_domain;         /*!< \brief Name of local domain. */
};

/******************************************************************************
 *                                                                            *
 *             L O C A L   F U N C T I O N   P R O T O T Y P E S              *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *    L O C A L   I N I T I A L I Z E D   D A T A   D E F I N I T I O N S     *
 *                                                                            *
 ******************************************************************************/

/* None */

/******************************************************************************
 *                                                                            *
 *    L O C A L   U N I T I A L I Z E D   D A T A   D E F I N I T I O N S     *
 *                                                                            *
 ******************************************************************************/

/*!
 * \brief Current configuration.
 *
 * This structure is filled by parsing offer or acknowledge messages from
 * the server.
 */
static DYNCFG *dhcpConfig;

/*! 
 * \brief Client thread identifier.
 *
 * Used to determine if the client thread is running. Zero indicates that
 * it is not.
 */
static NUTHANDLE dhcpThread;

/*!
 * \brief Current state of the DHCP client state machine.
 */
static u_char dhcpState;

/*!
 * \brief Latest DHCP error code.
 */
static int dhcpError;

/*!
 * \brief DHCP wake up queue.
 *
 * The DHCP state machine can be woken up by posting to this queue.
 */
static NUTHANDLE dhcpWake;

/*!
 * \brief DHCP waiting queue.
 *
 * Application threads wait on this queue until DHCP success or failure.
 */
static NUTHANDLE dhcpDone;

/*!
 * \brief Maximum number of milliseconds to wait on \ref dhcpDone.
 *
 * Specified by the application when calling the DHCP client API.
 */
static u_long dhcpApiTimeout;

/*!
 * \brief Time at which the application started to wait.
 *
 * Used in conjunction with \ref dhcpApiTimeout to limit the maximum 
 * wait time for server responses.
 */
static u_long dhcpApiStart;

/*!
 * \brief DHCP device.
 *
 * The ARM port doesn't provide parameter passing to thread routines.
 * Thus we use a global variable to store the NUTDEVICE pointer.
 */
static NUTDEVICE *dhcpDev;

/*!
 * \brief Dynamic string copy.
 *
 * \param dst Points to a string pointer. If the pointer is not NULL, it
 *            is assumed that it points to a previously allocated buffer
 *            and this buffer will be released first. Then a new buffer
 *            will be allocated and the source string will be copied to
 *            this buffer.
 * \param src The source string. No delimiter required.
 * \param len Length of the source string.
 */
static void copy_str(u_char ** dst, void *src, int len)
{
    if (*dst) {
        NutHeapFree(*dst);
    }
    if ((*dst = NutHeapAlloc(len + 1)) != 0) {
        if (len) {
            memcpy(*dst, src, len);
        }
        *(*dst + len) = 0;
    }
}

/*!
 * \brief Release DYNCFG structure.
 *
 * Frees all memory occupied by a \ref DYNCFG structure.
 *
 * \param dyncfg This structure will be released.
 */
static void ReleaseDynCfg(DYNCFG * dyncfg)
{
    if (dyncfg) {
        if (dyncfg->dyn_hostname) {
            NutHeapFree(dyncfg->dyn_hostname);
        }
        if (dyncfg->dyn_domain) {
            NutHeapFree(dyncfg->dyn_domain);
        }
        NutHeapFree(dyncfg);
    }
}

/*!
 * \brief Parse a DHCP reply message.
 *
 * \param bp  Pointer to the reply message. The caller must make sure,
 *            that this contains a valid BOOTP reply header with at
 *            least five bytes in the options field.
 * \param len Number of valid bytes in the reply message.
 *
 * \return Pointer to config structure. Must be released by the caller.
 *         NULL is returned in case of parsing errors.
 */
static DYNCFG *ParseReply(BOOTP *bp, int len)
{
    u_char *op;
    int left;
    DYNCFG *cfgp;

    /* Allocate and initialize a new structure. */
    if ((cfgp = NutHeapAlloc(sizeof(DYNCFG))) == 0) {
        return 0;
    }
    memset(cfgp, 0, sizeof(DYNCFG));
    cfgp->dyn_leaseTime = DHCP_DEFAULT_LEASE;

    /* Set the assigned IP address. */
    memcpy(&cfgp->dyn_yiaddr, &bp->bp_yiaddr, 4);

    /* 
     * Parse options until an end option is found or until we reached
     * the end of the message.
     */
    op = bp->bp_options + 4;
    left = len - (sizeof(*bp) - sizeof(bp->bp_options)) - 4;
    while (*op != DHCPOPT_END && left > 0) {
        u_char ol;

#ifdef NUTDEBUG
        if (__tcp_trf) {
            fprintf(__tcp_trs, "[DHCP-Opt-%u]", *op);
        }
#endif
        /* Pad option is used for boundary alignment. */
        if (*op == DHCPOPT_PAD) {
            op++;
            left--;
            continue;
        }

        /* Reject if option length exceeds total length. */
        if ((ol = *(op + 1)) > left) {
            break;
        }

        /* Type of this message. */
        if (*op == DHCPOPT_MSGTYPE) {
            if (ol != 1) {
                break;
            }
            cfgp->dyn_msgtyp = *(op + 2);
        }
        /* Our host name. May or may not include the domain. */
        else if (*op == DHCPOPT_HOSTNAME) {
            copy_str(&cfgp->dyn_hostname, op + 2, ol);
        }
        /* Name of the domain we are in. */
        else if (*op == DHCPOPT_DOMAIN) {
            copy_str(&cfgp->dyn_domain, op + 2, ol);
        }

        /* All remaining options require at least 4 octets. */
        else if (ol >= 4) {
            /* Preset most often used long value. */
            u_long lval = *(op + 2);
            lval += (u_long)(*(op + 3)) << 8;
            lval += (u_long)(*(op + 4)) << 16;
            lval += (u_long)(*(op + 5)) << 24;

            /* Our IP network mask. */
            if (*op == DHCPOPT_NETMASK) {
                cfgp->dyn_netmask = lval;
            }
            /* Our IP broadcast address. */
            else if (*op == DHCPOPT_BROADCAST) {
                cfgp->dyn_broadcast = lval;
            }
            /* Our IP default gate. More than one gateway may be 
               specified. We take the fist one only and ignore the 
               rest. */
            else if (*op == DHCPOPT_GATEWAY) {
                cfgp->dyn_gateway = lval;
            }
            /* Our DNS server. Updated by Jelle Martijn Kok to 
               support a secondary DNS. */
            else if (*op == DHCPOPT_DNS) {
                cfgp->dyn_pdns = lval;
                if (ol >= 8) {
                    cfgp->dyn_sdns = *(op + 6);
                    cfgp->dyn_sdns += (u_long)(*(op + 7)) << 8;
                    cfgp->dyn_sdns += (u_long)(*(op + 8)) << 16;
                    cfgp->dyn_sdns += (u_long)(*(op + 9)) << 24;
                }
            }
            /* Server identifier. */
            else if (*op == DHCPOPT_SID) {
                cfgp->dyn_sid = lval;
            }
            /* Renewal time. */
            else if (*op == DHCPOPT_RENEWALTIME) {
                cfgp->dyn_renewalTime = ntohl(lval);
            }
            /* Rebinding time. */
            else if (*op == DHCPOPT_REBINDTIME) {
                cfgp->dyn_rebindTime = ntohl(lval);
            }
            /* Total lease time granted. */
            else if (*op == DHCPOPT_LEASETIME) {
                cfgp->dyn_leaseTime = ntohl(lval);
            }
        }
        op += ol + 2;
        left -= ol + 2;
    }

    /*
     * Discard this configuration if parsing stopped before reaching 
     * the end option or if we didn't receive an expected message type.
     */
    if (*op != DHCPOPT_END ||   /* */
        (cfgp->dyn_msgtyp != DHCP_OFFER &&      /* */
         cfgp->dyn_msgtyp != DHCP_ACK &&        /* */
         cfgp->dyn_msgtyp != DHCP_NAK)) {
#ifdef NUTDEBUG
        if (__tcp_trf) {
            fprintf(__tcp_trs, "[DHCP-Parse Error]");
        }
#endif
        ReleaseDynCfg(cfgp);
        return 0;
    }

    /* Calculate renewal and rebind times. */
    if (cfgp->dyn_renewalTime == 0) {
        cfgp->dyn_renewalTime = cfgp->dyn_leaseTime / 2;
    }
    if (cfgp->dyn_rebindTime == 0) {
        cfgp->dyn_rebindTime = cfgp->dyn_renewalTime +  /* */
            cfgp->dyn_renewalTime / 2 + /* */
            cfgp->dyn_renewalTime / 4;
    }
    return cfgp;
}

/*!
 * \brief Add variable length option.
 *
 * \param op  Pointer into the option buffer.
 * \param ot  Option type.
 * \param ov  Pointer to buffer to copy from.
 * \param len Number of octets to copy.
 *
 * \return Total number of octets added, include type and length.
 */
static size_t DhcpAddOption(u_char * op, u_char ot, void *ov, u_char len)
{
    *op++ = ot;
    *op++ = len;
    memcpy(op, ov, len);

    return 2 + len;
}

/*!
 * \brief Add single octet option.
 *
 * \param op Pointer into the option buffer.
 * \param ot Option type.
 * \param ov Option value.
 *
 * \return Total number of octets added, include type and length.
 */
static size_t DhcpAddByteOption(u_char * op, u_char ot, u_char ov)
{
    *op++ = ot;
    *op++ = 1;
    *op++ = ov;

    return 3;
}

/*!
 * \brief Add double octet option.
 *
 * \param op Pointer into the option buffer.
 * \param ot Option type.
 * \param ov Option value in host byte order. Will be converted to network
 *           byte order.
 *
 * \return Total number of octets added, include type and length.
 */
static size_t DhcpAddShortOption(u_char * op, u_char ot, u_short ov)
{
    *op++ = ot;
    *op++ = 2;
    ov = htons(ov);
    memcpy(op, &ov, 2);

    return 4;
}

/*!
 * \brief Add parameter request option.
 *
 * RFC 2131 demands to use the same parameter request in discover and
 * request messages.
 *
 * \param op Pointer into the option buffer.
 * \param ot Option type.
 * \param ov Option value in host byte order.
 *
 * \return Total number of octets added, include type and length.
 */
static size_t DhcpAddParmReqOption(u_char * op)
{
    *op++ = DHCPOPT_PARAMREQUEST;
    *op++ = 3;                  /* Adjust when adding more options! */
    *op++ = DHCPOPT_NETMASK;    /* Typically sent by default, but play safe. */
    *op++ = DHCPOPT_GATEWAY;    /* We want a default gateway. */
    *op++ = DHCPOPT_DNS;        /* We want the DNS' IP. */
    return 5;                   /* Adjust when adding more options! */
}

/*!
 * \brief Prepare a BOOTP request message header.
 *
 * A BOOTP header will be initialized at the specified buffer address
 * and the routine will add the DHCP magic cookie (RFC 1497) and an
 * additional option containing the specified DHCP message type.
 *
 * All other fields are cleared to zero.
 *
 * \param bp     Pointer to the buffer to initialize.
 * \param msgtyp DHCP message type, either \ref DHCP_DISCOVER, 
 *               \ref DHCP_REQUEST or \ref DHCP_RELEASE.
 * \param xid    Random transaction identifier.
 * \param ciaddr Our IP address. Should be set to zero unless we are in
 *               BOUND, RENEW or REBINDING state and can respond to ARP 
 *               requests.
 * \param secs   Seconds elapsed, since we began address acquisition or 
 *               renewal.
 *
 * \return Total number of octets added.
 */
static u_int DhcpPrepHeader(BOOTP *bp, u_char msgtyp, u_long xid, u_long ciaddr, u_short secs)
{
    u_char *op;

    memset(bp, 0, sizeof(*bp));
    /* Clients send bootp requests (op code 1) only. */
    bp->bp_op = 1;
    /* Ethernet addresses are type 1 with 6 octets. */
    bp->bp_htype = 1;
    bp->bp_hlen = 6;
    memcpy(bp->bp_chaddr, confnet.cdn_mac, 6);
    /* Transaction identifier. */
    bp->bp_xid = xid;
    /* Seconds elapsed since address acquisition. */
    bp->bp_secs = htons(secs);

#ifdef DHCP_BROADCAST_FLAG
    /*
     * We do not need the broadcast flag, because our stack accepts IP 
     * messages to any destination if no local address has been assigned,
     * However, we continue supporting this compile time option.
     */
    bp->bp_flags = htons(0x8000);
#endif

    bp->bp_ciaddr = ciaddr;

    /* Add the DHCP magic cookie according to RFC 1497. */
    op = bp->bp_options;
    *op++ = 0x63;
    *op++ = 0x82;
    *op++ = 0x53;
    *op++ = 0x63;

    /* Add the DHCP message type option. */
    return DhcpAddByteOption(op, DHCPOPT_MSGTYPE, msgtyp) + 4;
}

/*!
 * \brief Send a DHCP message to the server.
 *
 * This routine will also add an end of option identifier and take care,
 * that the message length will not fall below the minimum expected by 
 * BOOTP.
 *
 * \param sock Socket descriptor. This pointer must have been 
 *             retrieved by calling NutUdpCreateSocket().
 * \param addr Destination IP addres.
 * \param bp   Pointer to a buffer to be used for transmission.
 *             Must contain a fully initialized header and option
 *             fields.
 * \param len  Total number of DHCP option octets.
 *
 * \return 0 on success. On errors -1 is returned and \ref dhcpError will
 *         be set to \ref DHCPERR_TRANSMIT.
 */
static int DhcpSendMessage(UDPSOCKET * sock, u_long addr, BOOTP *bp, size_t len)
{
    /* Add 'end of options'. */
    bp->bp_options[len++] = DHCPOPT_END;

    /* Maintain a BOOTP compatible minimum packet size of 300 octets. 
       Thanks to Tomohiro Haraikawa. */
    if ((len += sizeof(BOOTP) - sizeof(bp->bp_options)) < MIN_DHCP_MSGSIZE) {
        len = MIN_DHCP_MSGSIZE;
    }
#ifdef NUTDEBUG
    if (__tcp_trf) {
        fprintf(__tcp_trs, "[DHCP-Send to %s]", inet_ntoa(addr));
    }
#endif
    if (NutUdpSendTo(sock, addr, DHCP_SERVERPORT, bp, len) < 0) {
        dhcpError = DHCPERR_TRANSMIT;
        return -1;
    }
    return 0;
}

/*!
 * \brief Receive a DHCP reply from the server.
 *
 * \param sock Socket descriptor. 
 * \param xid  Expected transaction identifier. Incoming messages with a
 *             different identifier are silently discarded.
 * \param bp   Pointer to the receive buffer.
 * \param tmo  Maximum number of milliseconds to wait for a valid message.
 *
 * \return The number of bytes received, if successful. The return
 *         value -1 indicates an error, in which case dhcpError is
 *         set to an error code. On timeout 0 is returned.
 */
static int DhcpRecvMessage(UDPSOCKET * sock, u_long xid, BOOTP *bp, u_long tmo)
{
    int rc;
    u_short port;
    u_long addr;
    u_long etim;
    u_long wtim;

    /* Set our start time. */
    etim = NutGetMillis();
    /* Set the initial receive timeout. */
    wtim = tmo;
    for (;;) {
        rc = NutUdpReceiveFrom(sock, &addr, &port, bp, sizeof(BOOTP), wtim);
#ifdef NUTDEBUG
        if (__tcp_trf) {
            if (rc > 0) {
                fprintf(__tcp_trs, "[DHCP-Recv from %s]", inet_ntoa(addr));
            } else if (rc < 0) {
                fprintf(__tcp_trs, "[DHCP-Recv Error]");
            } else {
                fprintf(__tcp_trs, "[DHCP-Recv Timeout %lu]", tmo);
            }
        }
#endif
        /* Immediately return on receive errors and timeouts. */
        if (rc <= 0) {
            if (rc < 0) {
                dhcpError = DHCPERR_RECEIVE;
            }
            break;
        }
        /* The message must at least include the BOOTP header plus five 
           bytes of options (magic and end). We are quite liberal here. */
        if (rc > sizeof(BOOTP) - sizeof(bp->bp_options) + 5) {
            /* The message must be a BOOTP reply with the expected XID. */
            if (bp->bp_op == 2 && bp->bp_xid == xid) {
                /* Message is acceptable. */
                break;
            }
        }
        /* Calculate the remaining timeout for not getting trapped here 
           on a busy network, which regularly broadcasts DHCP messages. */
        wtim = NutGetMillis() - etim;
        if (wtim >= tmo - 250) {
            /* Less than 250 ms left, return timeout. */
            rc = 0;
            break;
        }
        wtim = tmo - wtim;
    }
    return rc;
}

/*!
 * \brief Broadcast a DHCP discover message.
 *
 * \param sock  Socket descriptor. This pointer must have been retrieved 
 *              by calling NutUdpCreateSocket().
 * \param bp    Pointer to a buffer to be used for transmission. No specific 
 *              initialization required.
 * \param xid   Random transaction identifier.
 * \param raddr Requested IP address. Optional.
 * \param secs  Seconds elapsed since start of address acquisition. Related
 *              requests must use the same value.
 *
 * \return 0 on success, -1 if send failed.
 */
static int DhcpBroadcastDiscover(UDPSOCKET * sock, BOOTP *bp, u_long xid, u_long raddr, u_short secs)
{
    size_t optlen;
    int len;
    u_char *op = bp->bp_options;

    optlen = DhcpPrepHeader(bp, DHCP_DISCOVER, xid, 0, secs);

    /* Request a specific IP if one had been assigned previously. */
    if (raddr) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_REQESTIP, &raddr, sizeof(raddr));
    }

    optlen += DhcpAddParmReqOption(op + optlen);

    /* Pass host name if specified in confos structure.  
     * Win2k DHCP server can register this as dynamic DNS entry.
     * Also viewing DHCP lease table shows something sensible.
     */
    len = strlen(confos.hostname);
    if (len > 0) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
    }

    /* Request a maximum message size. */
    optlen += DhcpAddShortOption(op + optlen, DHCPOPT_MAXMSGSIZE, MAX_DHCP_MSGSIZE);

    return DhcpSendMessage(sock, INADDR_BROADCAST, bp, optlen);
}


/*!
 * \brief Send a DHCP request message.
 *
 * \param sock  Socket descriptor. This pointer must have been retrieved by 
 *              calling NutUdpCreateSocket().
 * \param daddr IP address of the DHCP server or \ref INADDR_BROADCAST.
 * \param bp    Pointer to a buffer to be used for transmission.
 * \param xid   Random transaction identifier.
 * \param caddr Our IP address. Should be set only if we are able to respond 
 *              to ARP requests. Otherwise must be set to 0.
 * \param raddr Requested IP address. Required.
 * \param sid   Server identifier. If this request is not an offer response, 
 *              then set it to zero.
 * \param secs  Seconds elapsed since start of address acquisition. If this
 *              request is sent in reponse to an offer, the same value must
 *              be used.
 *
 * \return 0 on success, -1 if send failed.
 */
static int DhcpSendRequest(UDPSOCKET * sock, u_long daddr, BOOTP *bp, u_long xid,        /* */
                           u_long caddr, u_long raddr, u_long sid, u_short secs)
{
    size_t optlen;
    int len;
    u_char *op = bp->bp_options;

    /* Initialize the BOOTP header. */
    optlen = DhcpPrepHeader(bp, DHCP_REQUEST, xid, caddr, secs);

    /* Add specified options. */
    if (raddr) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_REQESTIP, &raddr, sizeof(raddr));
    }
    if (sid) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_SID, &sid, sizeof(sid));
    }
    optlen += DhcpAddParmReqOption(op + optlen);

    /* Pass host name if specified in confos structure.  */
    /* viewing DHCP lease table shows something sensible. */
    len = strlen(confos.hostname);
    if (len > 0) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
    }

    return DhcpSendMessage(sock, daddr, bp, optlen);
}

/*!
 * \brief Broadcast a DHCP request message.
 *
 * \param sock  Socket descriptor. This pointer must have been retrieved by 
 *              calling NutUdpCreateSocket().
 * \param bp    Pointer to a buffer to be used for transmission.
 * \param xid   Random transaction identifier.
 * \param caddr Our IP address. Should be set only if we are able to respond 
 *              to ARP requests. Otherwise must be set to 0.
 * \param raddr Requested IP address. Required.
 * \param sid   Server identifier. If this request is not an offer response, 
 *              then set it to zero.
 * \param secs  Seconds elapsed since start of address acquisition. If this
 *              request is sent in reponse to an offer, the same value must
 *              be used.
 *
 * \return 0 on success, -1 if send failed.
 */
static int DhcpBroadcastRequest(UDPSOCKET * sock, BOOTP *bp, u_long xid, /* */
                                u_long caddr, u_long raddr, u_long sid, u_short secs)
{
    return DhcpSendRequest(sock, INADDR_BROADCAST, bp, xid, caddr, raddr, sid, secs);
}

/*!
 * \brief Relinguish our DHCP lease.
 *
 * \param sock  Socket descriptor. This pointer must have been retrieved by 
 *              calling NutUdpCreateSocket().
 * \param daddr IP address of the DHCP server.
 * \param bp    Pointer to a buffer to be used for transmission.
 * \param xid   Random transaction identifier.
 * \param caddr Our IP address. Should be set only if we are able to respond 
 *              to ARP requests. Otherwise must be set to 0.
 * \param sid   Server identifier. If this request is not an offer response, 
 *              then set it to zero.
 *
 * \return 0 on success, -1 if send failed.
 */
static int DhcpSendRelease(UDPSOCKET * sock, u_long daddr, BOOTP *bp, u_long xid, u_long caddr, u_long sid)
{
    size_t optlen;
    u_char *op = bp->bp_options;

    /* Prepare BOOTP header. 'secs' is set to zero. */
    optlen = DhcpPrepHeader(bp, DHCP_RELEASE, xid, caddr, 0);

    /* Optionally add server identifier. */
    if (sid) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_SID, &sid, sizeof(sid));
    }
    return DhcpSendMessage(sock, daddr, bp, optlen);
}

/*!
 * \brief Inform DHCP about an externally allocated address.
 *
 * \param sock  Socket descriptor. This pointer must have been retrieved by 
 *              calling NutUdpCreateSocket().
 * \param daddr IP address of the DHCP server or INADDR_BROADCAST.
 * \param bp    Pointer to a buffer to be used for transmission.
 * \param xid   Random transaction identifier.
 * \param caddr Our IP address. Required.
 *
 * \return 0 on success, -1 if send failed.
 */
static int DhcpSendInform(UDPSOCKET * sock, u_long daddr, BOOTP *bp, u_long xid, u_long caddr)
{
    size_t optlen;
    size_t len;
    u_char *op = bp->bp_options;

    /* Prepare BOOTP header. 'secs' is set to zero. */
    optlen = DhcpPrepHeader(bp, DHCP_INFORM, xid, caddr, 0);

    /* Additional options we want. */
    optlen += DhcpAddParmReqOption(op + optlen);

    /* Add our configured host name. */
    len = strlen(confos.hostname);
    if (len > 0) {
        optlen += DhcpAddOption(op + optlen, DHCPOPT_HOSTNAME, confos.hostname, len);
    }

    /* We should provide the maximum message size. */
    optlen += DhcpAddShortOption(op + optlen, DHCPOPT_MAXMSGSIZE, MAX_DHCP_MSGSIZE);

    return DhcpSendMessage(sock, daddr, bp, optlen);
}


/*!
 * \brief Check a new offer.
 *
 * \param dyncfg Current configuration, may be NULL.
 * \param ip     DHCP server IP address.
 * \param bp     DHCP offer message.
 * \param len    DHCP message length.
 *
 * \return Updated configuration.
 */
static DYNCFG *CheckOffer(DYNCFG * dyncfg, BOOTP *bp, size_t len)
{
    DYNCFG *offer;

    /* Parse the new offer. If it's invalid, return the current 
       configuration. */
    if ((offer = ParseReply(bp, len)) == 0) {
        return dyncfg;
    }

    /* Discard anything which is not an offer. */
    if (offer->dyn_msgtyp != DHCP_OFFER) {
        ReleaseDynCfg(offer);
        return dyncfg;
    }

    /* First offer, take it. */
    if (dyncfg == 0) {
        dyncfg = offer;
    }

    /* 
     * Check if the new offer is better than the current configuration:
     */
    else {
        /* If we remember a previously allocated IP which isn't in the
           current configuration but in the new offer, then let's take 
           the new one. */
        if (confnet.cdn_ip_addr & confnet.cdn_ip_mask) {
            if (dyncfg->dyn_yiaddr != confnet.cdn_ip_addr &&    /* */
                offer->dyn_yiaddr == confnet.cdn_ip_addr) {
                ReleaseDynCfg(dyncfg);
                dyncfg = offer;
            }
        }
        /* In the second place prefer long lease times. */
        else if (offer->dyn_leaseTime > dyncfg->dyn_leaseTime) {
            ReleaseDynCfg(dyncfg);
            dyncfg = offer;
        }
        /* The new one deosn't offer anything interesting. Discard it. */
        else {
            ReleaseDynCfg(offer);
        }
    }
    return dyncfg;
}

/*! \fn NutDhcpClient(void *arg)
 * \brief DHCP client thread.
 *
 * This thread implements a DHCP state machine and is automatically started
 * when calling NutDhcpIfConfig() or NutDhcpInform().
 *
 * \bug We are not able to shutdown our interface, which may cause problems
 *      if out original DHCP server dies.
 *
 * \todo We are using a bunch of global variables, which must be associated
 *       to a specific interfase if we want to support more than one
 *       Ethernet port.
 */
THREAD(NutDhcpClient, arg)
{
    DYNCFG *reply = 0;
    UDPSOCKET *sock = 0;
    BOOTP *bp = 0;
    int n;
    u_long xid;
    IFNET *nif;
    u_short secs = 0;
    u_long aqsTime = NutGetSeconds();
    u_long leaseTime = 0;
    u_long napTime;
    ureg_t retries;
    u_long tmo = MIN_DHCP_WAIT;
    u_long last_ip = confnet.cdn_ip_addr;
    u_long server_ip;

    /*
     * Hack alert: Our ARM port doesn't allow parameter
     * passing to threads.
     */
    nif = dhcpDev->dev_icb;

    /* 
     * Generate a random transaction identifier based on our MAC 
     * address with the least significant byte of the MAC address 
     * becoming the most significant byte of the identifier. This 
     * should give a sufficient unique value when several Ethernuts 
     * are started concurrently. 
     */
    xid = 0;
    for (retries = 0; retries < sizeof(xid); retries++) {
        xid <<= 8;
        xid += nif->if_mac[5 - retries];
    }
    retries = 0;

    for (;;) {
#ifdef NUTDEBUG
        if (__tcp_trf) {
            fprintf(__tcp_trs, "\n[%u.DHCP-", retries + 1);
            switch (dhcpState) {
            case DHCPST_INIT:
                fprintf(__tcp_trs, "INIT]");
                break;
            case DHCPST_SELECTING:
                fprintf(__tcp_trs, "SELECTING]");
                break;
            case DHCPST_REQUESTING:
                fprintf(__tcp_trs, "REQUESTING]");
                break;
            case DHCPST_REBOOTING:
                fprintf(__tcp_trs, "REBOOTING %s]", inet_ntoa(last_ip));
                break;
            case DHCPST_BOUND:
                fprintf(__tcp_trs, "BOUND %lu]", NutGetSeconds() - leaseTime);
                break;
            case DHCPST_RENEWING:
                fprintf(__tcp_trs, "RENEWING %lu]", NutGetSeconds() - leaseTime);
                break;
            case DHCPST_REBINDING:
                fprintf(__tcp_trs, "REBINDING %lu]", NutGetSeconds() - leaseTime);
                break;
            case DHCPST_INFORMING:
                fprintf(__tcp_trs, "INFORMING]");
                break;
            case DHCPST_RELEASING:
                fprintf(__tcp_trs, "RELEASING]");
                break;
            case DHCPST_IDLE:
                if (dhcpError) {
                    fprintf(__tcp_trs, "ERROR %u]", dhcpError);
                } else {
                    fprintf(__tcp_trs, "IDLE]");
                }
                break;
            default:
                fprintf(__tcp_trs, "UNKNOWN %u]", dhcpState);
                break;
            }
        }
#endif

        /*
         * Setup some values based on the number of retry attempts.
         */
        server_ip = INADDR_BROADCAST;   /* Broadcasting is default. */
        if (retries) {
            /* Double our timeout on each retry. */
            tmo += tmo;
            if (tmo > MAX_DHCP_WAIT) {
                tmo = MAX_DHCP_WAIT;
            }
        } else {
            /* Start with minimum timeout first. */
            tmo = MIN_DHCP_WAIT;
            /* Use a new xid for the first message in each state except 
             * when requesting, where we should continue using the xid 
             * from the offer message we received.
             */
            if (dhcpState != DHCPST_REQUESTING) {
                xid++;
            }

            /* If we know the server's IP, try to unicast on the first 
               attempt. */
            if (dhcpConfig && dhcpConfig->dyn_sid) {
                server_ip = dhcpConfig->dyn_sid;
            }
        }

        /*
         * Keep track of the API timeout.
         */
        if (dhcpState != DHCPST_IDLE && dhcpApiTimeout != NUT_WAIT_INFINITE) {
            u_long tt = NutGetMillis() - dhcpApiStart;

            if (dhcpApiTimeout <= tt) {
                dhcpError = DHCPERR_TIMEOUT;
                dhcpState = DHCPST_IDLE;
                continue;
            }
            if ((tt = dhcpApiTimeout - tt) < tmo) {
                tmo = tt;
            }
        }

        /*
         * Keep track of acquisition time.
         */
        if (dhcpState == DHCPST_SELECTING || dhcpState == DHCPST_RENEWING || dhcpState == DHCPST_REBINDING) {
            /* For retries make sure that secs doesn't overflow. */
            if (retries) {
                if (NutGetSeconds() - aqsTime > 0xffffUL) {
                    secs = 0xffff;
                } else {
                    secs = (u_short) (NutGetSeconds() - aqsTime);
                }
            }
            /* For first transmissions make sure that secs is zero. */
            else {
                aqsTime = NutGetSeconds();
                secs = 0;
            }
        }

        /*
         * Release UDP socket and buffer in states with long inactive time.
         */
        if (dhcpState == DHCPST_BOUND || dhcpState == DHCPST_IDLE) {
            if (sock) {
                NutUdpDestroySocket(sock);
                sock = 0;
            }
            if (bp) {
                NutHeapFree(bp);
                bp = 0;
            }
        }

        /*
         * In all other states we need the socket and the buffer.
         */
        else {
            /*
             * Check if something else configured our interface.
             */
            if (dhcpConfig == 0 && nif->if_local_ip) {
                /* If we need additional configuration, we can sent
                   a DHCP Inform message here. */
                dhcpState = DHCPST_IDLE;
                continue;
            }

            if (sock == 0 || bp == 0) {
                if (sock == 0) {
                    sock = NutUdpCreateSocket(DHCP_CLIENTPORT);
                }
                if (bp == 0) {
                    bp = NutHeapAlloc(sizeof(BOOTP));
                }
                if (sock == 0 || bp == 0) {
                    /* Looks like we are out of memory. */
                    dhcpError = DHCPERR_SYSTEM;
                    dhcpState = DHCPST_IDLE;
                    /* At this point either socket or buffer may be allocated. 
                       Thus we need to jump back to the top of our state loop
                       to release it. */
                    continue;
                }
#if MAX_DHCP_BUFSIZE
                {
                    u_short max_ms = MAX_DHCP_BUFSIZE;
                    NutUdpSetSockOpt(sock, SO_RCVBUF, &max_ms, sizeof(max_ms));
                }
#endif
            }
        }

        /*
         * (Re)Start.
         */
        if (dhcpState == DHCPST_INIT) {
            /* Clear the retry counter. */
            retries = 0;
            /* Use a new XID on each attempt. */
            xid++;
            /* Determine whether this is an initial boot or a reboot. */
            if ((last_ip & confnet.cdn_ip_mask) == 0) {
                /* No previous IP, start from ground up. */
                dhcpState = DHCPST_SELECTING;
            } else {
                /* We got a previously allocated IP configuration from
                 * non-volatile configuration memory. Try to re-use it. */
                dhcpState = DHCPST_REBOOTING;
            }
        }

        /*
         * Broadcast discover and collect incoming offers.
         */
        else if (dhcpState == DHCPST_SELECTING) {
            if (retries++ > MAX_DCHP_RETRIES) {
                /* Too many retries while discovering DHCP. Give up. */
                dhcpError = DHCPERR_TIMEOUT;
                dhcpState = DHCPST_IDLE;
            }
            /* Send the discovering broadcast. */
            else if (DhcpBroadcastDiscover(sock, bp, xid, last_ip, secs) < 0) {
                /* Fatal transmit error on broadcast. */
                dhcpState = DHCPST_IDLE;
            } else {
                /* Collect incoming offers. */
                while ((n = DhcpRecvMessage(sock, xid, bp, tmo)) > 0) {
                    /* Check if this is a valid offer. */
                    if ((dhcpConfig = CheckOffer(dhcpConfig, bp, n)) != 0) {
                        /* If the callers timeout is low, do not collect
                           more than one. Thanks to Jelle Kok. */
                        if (dhcpApiTimeout < MIN_DHCP_WAIT * 3) {
                            break;
                        }
                        /* Switch to lowest timeout after we received
                           a first response. */
                        tmo = MIN_DHCP_WAIT;
                    }
                }
                /* Change to ERROR state on fatal receive errors. */
                if (n < 0) {
                    dhcpState = DHCPST_IDLE;
                }
                /* Change to REQUESTING state if we got a valid offer.
                   Otherwise we stay in SELECTING state. */
                else if (dhcpConfig) {
                    retries = 0;
                    dhcpState = DHCPST_REQUESTING;
                }
            }
        }

        /*
         * Send request and wait for an acknowledge.
         */
        else if (dhcpState == DHCPST_REQUESTING) {
            if (retries++ > MAX_DCHP_RETRIES) {
                /* Too many retries with this server, fall back to discovery. */
                dhcpState = DHCPST_INIT;
            }
            /* Request an offered configuration. According to RFC 2131 this
               has to be broadcasted. */
            else if (DhcpBroadcastRequest(sock, bp, xid, 0, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_sid, secs) < 0) {
                /* Fatal transmit error on broadcast. Give up. */
                dhcpState = DHCPST_IDLE;
            } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
                /* Fatal receive error. */
                dhcpState = DHCPST_IDLE;
            } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
                /* The server accepted our request. We are bound. */
                if (reply->dyn_msgtyp == DHCP_ACK) {
                    ReleaseDynCfg(dhcpConfig);
                    dhcpConfig = reply;
                    reply = 0;
                    leaseTime = aqsTime;
                    dhcpState = DHCPST_BOUND;
                }
                /* The server declines a previously offered configuration. 
                   Restart discovery. */
                else if (reply->dyn_msgtyp == DHCP_NAK) {
                    dhcpState = DHCPST_INIT;
                }
            }
        }

        /*
         * Reusing a previously allocated network address after reboot.
         */
        else if (dhcpState == DHCPST_REBOOTING) {
            if (++retries > MAX_DCHP_RETRIES) {
                /* Too many retries, fall back to discovery. */
                last_ip = 0;
                dhcpState = DHCPST_INIT;
            }
            /* Broadcast a request for our previous configuration. */
            else if (DhcpBroadcastRequest(sock, bp, xid, 0, last_ip, 0, secs) < 0) {
                /* Fatal transmit error on broadcast. Give up. */
                dhcpState = DHCPST_IDLE;
            } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
                /* Fatal receive error. */
                dhcpState = DHCPST_IDLE;
            } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
                if (reply->dyn_msgtyp == DHCP_ACK) {
                    ReleaseDynCfg(dhcpConfig);
                    dhcpConfig = reply;
                    reply = 0;
                    leaseTime = aqsTime;
                    dhcpState = DHCPST_BOUND;
                } else if (reply->dyn_msgtyp == DHCP_NAK) {
                    /* Either our previous address had been allocated by
                       someone else or we changed the network. Remove the
                       previous address and restart. */
                    last_ip = 0;
                    dhcpState = DHCPST_INIT;
                }
            }
        }

        /*
         * Maintain lease time.
         */
        else if (dhcpState == DHCPST_BOUND) {
            retries = 0;
            NutEventBroadcast(&dhcpDone);
            if (dhcpConfig->dyn_renewalTime <= NutGetSeconds() - leaseTime) {
                dhcpState = DHCPST_RENEWING;
            } else {
                /* Calculate the remaining lease time and take a nap. */
                napTime = dhcpConfig->dyn_renewalTime - (NutGetSeconds() - leaseTime);
                if (napTime > MAX_DHCP_NAPTIME) {
                    napTime = MAX_DHCP_NAPTIME;
                }
                NutEventWait(&dhcpWake, napTime * 1000UL);
            }
        }

        /*
         * Waiting for an acknowledge of our renewal request.
         */
        else if (dhcpState == DHCPST_RENEWING) {
            retries++;
            if (tmo / 1000 > dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime)) {
                tmo = dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime) * 1000;
            }
            if (dhcpConfig->dyn_rebindTime <= NutGetSeconds() - leaseTime) {
                retries = 0;
                dhcpState = DHCPST_REBINDING;
            }
            /* Send a request to our leasing server. We must not include
               the server identifier. */
            else if (DhcpSendRequest(sock, dhcpConfig->dyn_sid, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_yiaddr, 0, secs) <
                     0) {
                /* Unicast transmit error. */
                retries = 0;
                dhcpState = DHCPST_REBINDING;
            } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
                /* Fatal receive error. */
                dhcpState = DHCPST_IDLE;
            } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
                if (reply->dyn_msgtyp == DHCP_ACK) {
                    /* Got an acknowledge, return to bound state. */
                    ReleaseDynCfg(dhcpConfig);
                    dhcpConfig = reply;
                    reply = 0;
                    leaseTime = aqsTime;
                    dhcpState = DHCPST_BOUND;
                } else if (reply->dyn_msgtyp == DHCP_NAK) {
                    /* Unexpected NAK. */
                    retries = 0;
                    dhcpState = DHCPST_REBINDING;
                }
            }
        }

        /*
         * Waiting for an acknowledge of our rebind request.
         */
        else if (dhcpState == DHCPST_REBINDING) {
            retries++;
            if (tmo > dhcpConfig->dyn_rebindTime - (NutGetSeconds() - leaseTime)) {
                tmo = dhcpConfig->dyn_rebindTime - NutGetSeconds() - leaseTime;
            }
            if (dhcpConfig->dyn_rebindTime <= NutGetSeconds() - leaseTime) {
                retries = 0;
                dhcpState = DHCPST_REBINDING;
            }
            /* Broadcast a request for our previous configuration. We 
               must not include a server identifier. */
            else if (DhcpBroadcastRequest(sock, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_yiaddr, 0, secs) < 0) {
                /* Fatal transmit error on broadcast. Give up. */
                dhcpState = DHCPST_IDLE;
            } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
                /* Fatal receive error. */
                dhcpState = DHCPST_IDLE;
            } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
                if (reply->dyn_msgtyp == DHCP_ACK) {
                    /* Got an acknowledge, return to bound state. */
                    ReleaseDynCfg(dhcpConfig);
                    dhcpConfig = reply;
                    reply = 0;
                    leaseTime = aqsTime;
                    dhcpState = DHCPST_BOUND;
                } else if (reply->dyn_msgtyp == DHCP_NAK) {
                    /*
                     * We have a problem here if the last DHCP server died. 
                     * If a backup server exists, it may probe our IP address
                     * using ARP or ICMP. Our interface is up and responding,
                     * so the backup server may think that the IP address
                     * is in use and respond with NAK. Without shutting
                     * down our interface (not yet implemented) we are stuck.
                     * We switch to discovery state, but the problem remains.
                     */
                    dhcpState = DHCPST_INIT;
                }
            }
        }

        /*
         * Send an inform and wait for its (optional) echo.
         */
        else if (dhcpState == DHCPST_INFORMING) {
            if (retries++ > MAX_DCHP_RETRIES) {
                dhcpState = DHCPST_IDLE;
            } else if (DhcpSendInform(sock, server_ip, bp, xid, nif->if_local_ip) < 0) {
                if (server_ip == INADDR_BROADCAST) {
                    dhcpState = DHCPST_IDLE;
                }
            } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) != 0) {
                if (n > 0 &&    /* No receive error. */
                    (reply = ParseReply(bp, n)) != 0 && /* No parser error. */
                    reply->dyn_msgtyp == DHCP_ACK) {    /* Acknowledged. */
                    /* Take over this configuration. */
                    ReleaseDynCfg(dhcpConfig);
                    dhcpConfig = reply;
                    reply = 0;
                }
                dhcpState = DHCPST_IDLE;
            }
        }

        /*
         * Send a release and wait for its (optional) echo.
         */
        else if (dhcpState == DHCPST_RELEASING) {
            if (dhcpConfig == 0 ||      /* Not configured. */
                retries++ > MAX_DCHP_RELEASE_RETRIES || /* Too many retries. */
                DhcpSendRelease(sock, server_ip, bp, xid, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_sid) < 0) {
                if (server_ip == INADDR_BROADCAST) {
                    dhcpState = DHCPST_IDLE;
                }
            } else if ((n = DhcpRecvMessage(sock, xid, bp, tmo)) < 0) {
                /* Fatal receive error. */
                dhcpState = DHCPST_IDLE;
            } else if (n > 0 && (reply = ParseReply(bp, n)) != 0) {
                if (reply->dyn_msgtyp == DHCP_ACK) {
                    dhcpState = DHCPST_IDLE;
                } else if (reply->dyn_msgtyp == DHCP_NAK) {
                    dhcpState = DHCPST_IDLE;
                }
            }
        }

        /*
         * We are done somehow. Either a fatal error occured or we 
         * reached the specified timeout time or our lease has been
         * release or something else configured our interface.
         * Release all resources and wait for a new API call to
         * wake us up.
         */
        else if (dhcpState == DHCPST_IDLE) {
            ReleaseDynCfg(dhcpConfig);
            dhcpConfig = 0;
            retries = 0;
            NutEventBroadcast(&dhcpDone);
            NutEventWait(&dhcpWake, NUT_WAIT_INFINITE);
        }

        /* Release any received reply. */
        if (reply) {
            ReleaseDynCfg(reply);
            reply = 0;
        }
    }
}

/*!
 * \brief Activate the DHCP client thread.
 *
 * Start the DHCP thread if not running or wake it up. Pass the caller's 
 * timeout to the thread and wait an infinite time. We rely on the thread 
 * to wake us up on timeout.
 *
 * \param name    Name of the registered Ethernet device.
 * \param state   State to start with.
 * \param timeout Maximum number of milliseconds to wait. To disable 
 *                timeout, set this parameter to \ref NUT_WAIT_INFINITE.
 *                This value must be larger than 3 times of \ref MIN_DHCP_WAIT
 *                to enable collection of offers from multiple servers.
 */
static int DhcpKick(CONST char *name, u_char state, u_long timeout)
{
    NUTDEVICE *dev;
    IFNET *nif;

    /* Lookup the Ethernet device. */
    if ((dev = NutDeviceLookup(name)) == 0 ||   /* No device */
        dev->dev_type != IFTYP_NET ||   /* Wrong type */
        (nif = dev->dev_icb) == 0 ||    /* No netif */
        nif->if_type != IFT_ETHER) {    /* Wrong if type */
        dhcpError = DHCPERR_BADDEV;
        return -1;
    }

    /* Initialize timeout checking. */
    dhcpApiStart = NutGetMillis();
    dhcpApiTimeout = timeout;

    dhcpState = state;
    if (dhcpThread == 0) {
        dhcpDev = dev;
        dhcpThread = NutThreadCreate("dhcpc", NutDhcpClient, dev, NUT_THREAD_DHCP_STACK);//THREAD(NutDhcpClient, arg)
    }
    NutEventPost(&dhcpWake);
    NutEventWait(&dhcpDone, NUT_WAIT_INFINITE);

    return 0;
}

/*!
 * \brief Automatically configure an Ethernet network interface.
 *
 * If no MAC address is specified, this routine will try to read a 
 * previously stored configuration from the EEPROM. If this retrieves 
 * a fixed IP configuration, then the network interface will be 
 * immediately configured with these values by calling NutNetIfConfig(). 
 * If no valid IP configuration has been read, then this routine will 
 * start the DHCP client thread and wait upto a given number of 
 * milliseconds for an acknowledged configuration from a DHCP server.
 *
 * If a MAC address has been specified, this routine will not read the 
 * EEPROM configuration. If the application has set the global 
 * \ref CONFNET structure to a valid IP configuration before calling 
 * this function, then the network interface will be immediately 
 * configured with these values by calling NutNetIfConfig(). Otherwise 
 * the DHCP client thread will be started and this routine will wait 
 * upto a given number of milliseconds for an acknowledged configuration 
 * from a DHCP server.
 *
 * \param name    Name of the registered Ethernet device.
 * \param mac     MAC address of the destination. Set NULL to use the 
 *                configuration stored in the EEPROM.
 * \param timeout Maximum number of milliseconds to wait. To disable 
 *                timeout, set this parameter to \ref NUT_WAIT_INFINITE.
 *                Otherwise the value must be larger than 3 times of 
 *                \ref MIN_DHCP_WAIT to enable collection of offers 
 *                from multiple servers.
 *
 * \return 0 if the interface had been successfully configured. In most
 *         cases this information is sufficient, because the application
 *         will not care whether the configuration had been provided by
 *         a DHCP server or EEPROM values. However, if EEPROM values had
 *         been used, then no DNS servers had been set. The application
 *         can call NutDhcpStatus() to check the DHCP bound state. -1 is
 *         returned if the interface configuration failed. In this case
 *         NutDhcpError() can be called to get a more specific error
 *         code.
 */
int NutDhcpIfConfig(CONST char *name, u_char * mac, u_long timeout)
{
    u_char mac0[6];
    u_char macF[6];
    NUTDEVICE *dev;
    IFNET *nif;

    /*
     * Lookup the Ethernet device.
     */
    if ((dev = NutDeviceLookup(name)) == 0 ||   /* No device */
        dev->dev_type != IFTYP_NET ||   /* Wrong type */
        (nif = dev->dev_icb) == 0 ||    /* No netif */
        nif->if_type != IFT_ETHER) {    /* Wrong if type */
        dhcpError = DHCPERR_BADDEV;
        return -1;
    }

    /*
     * We determine whether the interface is enabled by checking
     * the MAC address. This is so bloody brain dead.
     */
    memset(mac0, 0x00, sizeof(mac0));   /* Uses more code but less RAM... */
    memset(macF, 0xFF, sizeof(macF));   /* ...than init in declaration.   */
    if (memcmp(nif->if_mac, mac0, 6) == 0 || memcmp(nif->if_mac, macF, 6) == 0) {
        /*
         * If the caller specified a MAC address, we use it and
         * overwrite the configuration.
         */
        if (mac) {
            memcpy(confnet.cdn_mac, mac, sizeof(confnet.cdn_mac));
        }

        /*
         * If no MAC address has been specified, read the configuration 
         * from EEPROM. If this fails, we do not continue any further,
         * but let the caller know that something is wrong. He may call
         * us again with a valid MAC address.
         */
        else if (NutNetLoadConfig(name)) {
            dhcpError = DHCPERR_NOMAC;
            return -1;
        }

        /*
         * Copy the MAC address to the interface structure. This will
         * magically brain dead enable the interface.
         */
        memcpy(nif->if_mac, confnet.cdn_mac, 6);
        NutSleep(500);
    }

    /*
     * If the EEPROM contains a fixed network configuration, we skip DHCP.
     */
    if ((confnet.cdn_cip_addr & confnet.cdn_ip_mask) != 0) {
        confnet.cdn_ip_addr = confnet.cdn_cip_addr;
        NutNetIfConfig2(name, confnet.cdn_mac, confnet.cdn_ip_addr, confnet.cdn_ip_mask, confnet.cdn_gateway);
        NutDnsConfig2(0, 0, confnet.cdn_pdns, confnet.cdn_sdns);
        return 0;
    }

    /*
     * Start the DHCP thread if not running or wake it up. Pass the caller's 
     * timeout to the thread and wait an infinite time. We rely on the thread 
     * to wake us up on timeout.
     */
    if (DhcpKick(name, DHCPST_INIT, timeout) == 0) {
        /*
         * The thread finished its task. If it reached the bound state, then
         * we got a valid configuration from DHCP.
         */
        if (dhcpState == DHCPST_BOUND) {
#ifdef NUTDEBUG
            if (__tcp_trf) {
                fprintf(__tcp_trs, "[DHCP-Config %s]", inet_ntoa(dhcpConfig->dyn_yiaddr));
            }
#endif
            NutNetIfSetup(dev, dhcpConfig->dyn_yiaddr, dhcpConfig->dyn_netmask, dhcpConfig->dyn_gateway);
            NutDnsConfig2(0, 0, dhcpConfig->dyn_pdns, dhcpConfig->dyn_sdns);
            return 0;
        }

        /*
         * Our interface has been configured externally, possibly by auto 
         * ARP or a similar function implemented by the application.
         */
        if (nif->if_local_ip) {
#ifdef NUTDEBUG
            if (__tcp_trf) {
                fprintf(__tcp_trs, "[DHCP-External %s]", inet_ntoa(nif->if_local_ip));
            }
#endif
            return 0;
        }

        /*
         * DHCP failed. In case we remember a previously allocated address, 
         * then let's use it.
         */
        if ((confnet.cdn_ip_addr & confnet.cdn_ip_mask) != 0) {
#ifdef NUTDEBUG
            if (__tcp_trf) {
                fprintf(__tcp_trs, "[DHCP-Reusing %s]", inet_ntoa(confnet.cdn_ip_addr));
            }
#endif
            NutNetIfConfig2(name, confnet.cdn_mac, confnet.cdn_ip_addr, confnet.cdn_ip_mask, confnet.cdn_gateway);
            return 0;
        }
    }
    return -1;
}

/*!
 * \brief Relinguish our DHCP lease.
 *
 * This function may be called by the application if we are moving
 * to another network. It helps the DHCP server to tidy up his
 * allocation table, but is not a required DHCP function.
 *
 * Upon return, the system should be shut down within 20 seconds.
 *
 * The client must reside in state \ref DHCPST_BOUND.
 *
 * \param name    Name of the registered Ethernet device, currently ignored.
 * \param timeout Maximum number of milliseconds to wait.
 *
 * \return 0 on success or -1 in case of an error.
 */
int NutDhcpRelease(CONST char *name, u_long timeout)
{
    /* Check the state. */
    if (dhcpState != DHCPST_BOUND) {
        dhcpError = DHCPERR_STATE;
        return -1;
    }

    /* Action! */
    return DhcpKick(name, DHCPST_RELEASING, timeout);
}

/*!
 * \brief Inform DHCP about an allocated address.
 *
 * The client must reside in state \ref DHCPST_IDLE.
 *
 * \param name    Name of the registered Ethernet device, currently ignored.
 * \param timeout Maximum number of milliseconds to wait.
 *
 * \return 0 on success or -1 in case of an error.
 */
int NutDhcpInform(CONST char *name, u_long timeout)
{
    /* Check the state. */
    if (dhcpState != DHCPST_IDLE) {
        dhcpError = DHCPERR_STATE;
        return -1;
    }

    /* Action! */
    return DhcpKick(name, DHCPST_INFORMING, timeout);
}

/*!
 * \brief Return DHCP client status.
 *
 * \param name Name of the registered Ethernet device, currently ignored.
 *
 * \return DHCP status code, which may be any of the following:
 *         - \ref DHCPST_INIT
 *         - \ref DHCPST_SELECTING
 *         - \ref DHCPST_REQUESTING
 *         - \ref DHCPST_REBOOTING
 *         - \ref DHCPST_BOUND
 *         - \ref DHCPST_RENEWING
 *         - \ref DHCPST_REBINDING
 *         - \ref DHCPST_INFORMING
 *         - \ref DHCPST_RELEASING
 *         - \ref DHCPST_IDLE
 */
int NutDhcpStatus(CONST char *name)
{
    return dhcpState;
}

/*!
 * \brief Return DHCP error code.
 *
 * Possible error codes are
 *
 *         - \ref DHCPERR_TIMEOUT
 *         - \ref DHCPERR_NOMAC
 *         - \ref DHCPERR_BADDEV
 *         - \ref DHCPERR_SYSTEM
 *         - \ref DHCPERR_TRANSMIT
 *         - \ref DHCPERR_RECEIVE
 *
 * The error will be cleared upon return.
 *
 * \param name Name of the registered Ethernet device, currently ignored.
 *
 * \return DHCP error code or 0 if no error occured.
 */
int NutDhcpError(CONST char *name)
{
    int rc = dhcpError;
    dhcpError = 0;
    return rc;
}

/*!
 * \brief Check if DHCP has configured our interface 
 *
 * \deprecated Applications should use NutDhcpStatus().
 *
 * \return 0 if DHCP is in bound state.
 */
int NutDhcpIsConfigured(void)
{
    return (dhcpState == DHCPST_BOUND);
}

#endif

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值