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