T168_111\system\Ethernet\pro:第11~22

ftpd.c               。。。。。。。。。。。。。。。。。。。。。。。。。。。。

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

#define FTPD_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 <stdio.h>
#include <stdlib.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 "XLib.h"

#include "..\net\socket.h"
#include "..\net\tcp.h"
#include "..\netrx.h"
#include "ftpd.h"

#if defined(NUTNET)

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

/*!
 * \brief Default FTP root path.
 *
 * \showinitializer
 */
#ifndef FTP_ROOTPATH
#define FTP_ROOTPATH "PNUT:"
#endif

/*!
 * \brief Default data port.
 *
 * \showinitializer
 */
#ifndef FTP_DATA_PORT
#define FTP_DATA_PORT   20
#endif

/******************************************************************************
 *                                                                            *
 *                        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     *
 *                                                                            *
 ******************************************************************************/

/*
 * On Harvard architectures constant strings are stored in ROM, 
 * because RAM is usually a scarce resource on these platforms.
 */
static const char cmd_cwd[]  = "CWD";
static const char cmd_dele[] = "DELE";
static const char cmd_list[] = "LIST";
static const char cmd_mkd[]  = "MKD";
static const char cmd_xmkd[] = "XMKD";
static const char cmd_nlst[] = "NLST";
static const char cmd_noop[] = "NOOP";
static const char cmd_pass[] = "PASS";
static const char cmd_pasv[] = "PASV";
static const char cmd_port[] = "PORT";
static const char cmd_pwd[]  = "PWD";
static const char cmd_xpwd[] = "XPWD";
static const char cmd_quit[] = "QUIT";
static const char cmd_retr[] = "RETR";
static const char cmd_rmd[]  = "RMD";
static const char cmd_xrmd[] = "XRMD";
static const char cmd_stor[] = "STOR";
static const char cmd_syst[] = "SYST";
static const char cmd_type[] = "TYPE";
static const char cmd_user[] = "USER";

static char *mon_name = "JanFebMarAprMayJunJulAugSepOctNovDec";

static const char rep_banner[] = "220 Nut/OS FTP %s ready at %.3s%3d %02d:%02d:%02d\r\n";

/******************************************************************************
 *                                                                            *
 *    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     *
 *                                                                            *
 ******************************************************************************/

static char *ftp_root;
static char *ftp_user;
static char *ftp_pass;

/*!
 * \brief Break a string into a command and an argument string.
 *
 * \param line Pointer to the buffer containing the string. Note, that
 *             its contents is not preserved.
 * \param cmd  Receives a pointer to the command word in the given
 *             string. The routine will convert all lower case letters
 *             into upper case.
 * \param args Receives a pointer to the list of arguments, if any.
 *             Trailing carriage return or linefeed characters are
 *             cut off.
 */
static void SplitCmdArg(char * line, char ** cmd, char ** args)
{
    /* Skip leading spaces. */
    while (*line && *line <= ' ') {
        line++;
    }

    /* The first word is the command. Convert it to upper case. */
    *cmd = line;
    while (*line > ' ') {
        if (*line >= (u_char) 'a' && *line <= (u_char) 'z') {
            *line -= (u_char) 'a' - 'A';
        }
        line++;
    }

    /* Mark end of the command word. */
    if (*line) {
        *line++ = '\0';
    }

    /* Skip spaces. */
    while (*line && *line <= ' ') {
        ++line;
    }

    /* Arguments start here. */
    *args = line;
    while (*line && *line != '\r' && *line != '\n') {
        line++;
    }

    /* Mark end of arguments. */
    *line = 0;
}

/*!
 * \brief Parses arguments of an FTP PORT command.
 *
 * FTP clients send a target port definition as a comma separated list
 * of 6 ASCII byte values. The first four bytes specify an IP address
 * and the remaining two a port number.
 *
 * \param arg  Pointer to the argument string.
 * \param ip   Pointer to an unsigned long, which receives the IP
 *             address in network byte order.
 * \param port Pointer to an unsigned short, which receives the port
 *             number in host byte order.
 *
 * \return The number of converted byte values. Should be 6.
 */
static int ParseIpPort(CONST char * arg, u_long * ip, u_short * port)
{
    int rc;

    *ip = 0;
    *port = 0;
    for (rc = 0; rc < 6; rc++) {
        if (*arg < '0' || *arg > '9') {
            break;
        }
        if (rc < 4) {
            *ip >>= 8;
            *ip += atol(arg) << 24;
        } else {
            *port <<= 8;
            *port += atoi(arg);
        }
        while (*arg && *arg != ',') {
            arg++;
        }
        if (*arg == ',') {
            arg++;
        }
    }
    return rc;
}

/*!
 * \brief Send a positive response.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param code    Response code.
 *
 * \return -1 if the session ended. Otherwise 0 is returned.
 */
int NutFtpRespondOk(FTPSESSION * session, int code)
{
    static const char fmt[] = "%d OK\r\n";

#ifdef FTPD_DEBUG
    printf("\n<'%d OK' ", code);
#endif
    fprintf(session->ftp_ostream, fmt, code);
    fflush(session->ftp_ostream);

    return 0;
}

/*!
 * \brief Send a negative response.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param code    Response code.
 *
 * \return -1 if the session ended. Otherwise 0 is returned.
 */
int NutFtpRespondBad(FTPSESSION * session, int code)
{
    static const char fmt[] = "%d Failed\r\n";

#ifdef FTPD_DEBUG
    printf("\n<'%d Failed' ", code);
#endif
    fprintf(session->ftp_ostream, fmt, code);
    fflush(session->ftp_ostream);

    return 0;
}

/*!
 * \brief Send a response including the specified transfer mode.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param binary  0 indicates ASCII transfer mode.
 *
 * \return -1 if the session ended. Otherwise 0 is returned.
 */
int NutFtpSendMode(FTPSESSION * session, int binary)
{
    static const char intro[] = "150 Opening ";
    static const char amode[] = "ASCII.\r\n";
    static const char bmode[] = "BINARY.\r\n";

#ifdef FTPD_DEBUG
    printf("\n<'150 Opening %s' ", binary ? "BINARY" : "ASCII");
#endif
    fputs(intro, session->ftp_ostream);
    fputs(binary ? bmode : amode, session->ftp_ostream);
    fflush(session->ftp_ostream);

    return 0;
}

/*!
 * \brief Create an absolute path name.
 *
 * Combines an absolute directory path with a relative path name to a
 * full absolute path name. The absolute directory is split into two
 * parts, the root and the current work directory. The resulting path
 * will never be above the specified root. 
 *
 * \param root Non-empty absolute root directory path including the 
 *             device name, but without a trailing slash.
 * \param work Absolute work directory path below root including a
 *             leading, but without a trailing slash. This path is
 *             ignored if the relative path starts with a slash.
 * \param path Relative path name of a file or directory without any
 *             trailing slash.
 *
 * \return Pointer to the resulting path name. The buffer for this name 
 *         is allocated from heap memory. The caller is responsible for 
 *         freeing it. In case of an error, a NULL pointer is returned.
 */
char *CreateFullPathName(char *root, char *work, char *path)
{
    char *full;
    char *cp;
    size_t rl = root ? strlen(root) : 0;
    size_t wl = work ? strlen(work) : 0;
    size_t pl = path ? strlen(path) : 0;

    /* Ignore trailing slashes in root and work. */
    if (rl && *(root + rl - 1) == '/') {
        rl--;
    }
    if (wl && *(work + wl - 1) == '/') {
        wl--;
    }

    if ((full = NutHeapAlloc(rl + wl + pl + 3)) != NULL) {
        /* Put the root in front. */
        cp = full;
        if (rl) {
            cp = strcpy(full, root) + rl;
        }

        /* If path is relative, prepend the working directory. */
        if(pl == 0 || *path != '/') {
            if (wl) {
                if (*work != '/') {
                    *cp++ = '/';
                }
                cp = strcpy(cp, work) + wl;
            }
            *cp++ = '/';
            rl++;
        }

        if (pl) {
            *cp = 0;
            work = full + rl;

            while (*path) {
                /* Ingore duplicate slashes. */
                if (*path == '/') {
                    *cp++ = *path++;
                    while (*path == '/') {
                        path++;
                    }
                }
                /* Ignore single dots. */
                if (*path == '.') {
                    path++;
                    if (*path == '/') {
                        path++;
                        continue;
                    }
                    if (*path == 0) {
                        break;
                    }
                    if (*path == '.') {
                        path++;
                        if (*path == '/' || *path == 0) {
                            if (cp != work) {
                                cp--;
                                while (cp != work) {
                                    cp--;
                                    if (*cp == '/') {
                                        break;
                                    }
                                }
                            }
                            continue;
                        }
                        path--;
                    }
                    path--;
                }
                /* Copy the current path component. */
                while (*path && *path != '/') {
                    *cp++ = *path++;
                }
            }
        }
        *cp = 0;
    }
    return full;
}

/*!
 * \brief Establish an FTP connection for data transfer.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 *
 * \return Socket descriptor of the newly created data connection or
 *         0 if we were unable to establish the connection.
 */
TCPSOCKET *NutFtpDataConnect(FTPSESSION * session)
{
    TCPSOCKET *sock;
    int rc;

    if ((sock = NutTcpCreateSocket()) != 0) {

        if (session->ftp_maxseg) {
            NutTcpSetSockOpt(sock, TCP_MAXSEG, &session->ftp_maxseg, sizeof(session->ftp_maxseg));
        }
        if (session->ftp_passive) {
            rc = NutTcpAccept(sock, session->ftp_data_port);
        } else {
            rc = NutTcpConnect(sock, session->ftp_data_ip, session->ftp_data_port);
        }
        if (rc) {
            NutTcpCloseSocket(sock);
            sock = 0;
        }
    }
    return sock;
}

/*!
 * \brief Register the FTP server's root directory.
 *
 * Only one root directory is supported. Subsequent calls will
 * override previous settings.
 *
 * \param path Path name of the root directory. Must include the
 *             device name followed by a colon followed by an
 *             absolute directory path. May be set to NULL for 
 *             the default #FTP_ROOTPATH.
 *
 * \return 0 on success, -1 otherwise.
 */
int NutRegisterFtpRoot(CONST char *path)
{
    /* Reset path to default. */
    if (path == NULL || *path == 0) {
        /* Release previously allocate space. */
        if (ftp_root) {
            NutHeapFree(ftp_root);
        }
        if ((ftp_root = NutHeapAlloc(sizeof(FTP_ROOTPATH))) == 0) {
            return -1;
        }
        strcpy(ftp_root, FTP_ROOTPATH);
    }

    /* Set a specified path. */
    else {
        char *cp = strchr(path, ':');
        int len = strlen(path);

        /* Make sure that the path fulfills all requirements. */
        if (len < 2 || cp == 0 || (*++cp && *cp != '/')) {
            return -1;
        }

        /* Allocate space for new path, but preserve the current one. */
        if ((cp = NutHeapAlloc(len + 1)) == 0) {
            return -1;
        }

        /* Take over, releasing previously allocate space. */
        strcpy(cp, path);
        if (ftp_root) {
            NutHeapFree(ftp_root);
        }
        ftp_root = cp;

        /* Chop off an optional trailing slash. */
        cp = cp + strlen(cp) - 1;
        if (*cp == '/') {
            *cp = 0;
        }
    }
    return 0;
}

/*!
 * \brief Register an FTP user.
 *
 * Only one username/password pair is supported. Subsequent calls will
 * override previous settings.
 *
 * \warning Not successfully registering any will make file systems 
 *          accessible by anyone.
 *
 * \param user User's name.
 * \param pass Uncrypted password for the specified user.
 *
 * \return 0 on success. -1 is returned on failures, in which case
 *         no protection may be assumed.
 */
int NutRegisterFtpUser(CONST char *user, CONST char *pass)
{
    if (ftp_user) {
        NutHeapFree(ftp_user);
        ftp_user = 0;
    }
    if (user && *user) {
        if ((ftp_user = NutHeapAlloc(strlen(user) + 1)) == 0) {
            return -1;
        }
        strcpy(ftp_user, user);
    }
    if (ftp_pass) {
        NutHeapFree(ftp_pass);
        ftp_pass = 0;
    }
    if (pass && *pass) {
        if ((ftp_pass = NutHeapAlloc(strlen(pass) + 1)) == 0) {
            return -1;
        }
        strcpy(ftp_pass, pass);
    }
    return 0;
}


/*!
 * \brief Open an FTP server session.
 *
 * \param sock    Socket of an established TCP connection.
 * \param istream Stream of the socket connection, previously opened for 
 *                binary read.
 * \param ostream Stream of the socket connection, previously opened for 
 *                binary write.
 *
 * \return Pointer to an \ref FTPSESSION structure, which can be used
 *         in subsequent API calls. NULL is returned in case of any
 *         error.
 */
FTPSESSION *NutFtpOpenSession(TCPSOCKET * sock, FILE * istream, FILE * ostream)
{
    FTPSESSION *session;

    session = NutHeapAlloc(sizeof(FTPSESSION));

    if (session) {
        memset(session, 0, sizeof(FTPSESSION));
        session->ftp_data_port = FTP_DATA_PORT;
        session->ftp_maxseg = sock->so_mss;
        session->ftp_sock = sock;
        session->ftp_istream = istream;
        session->ftp_ostream = ostream;

        /* Set initial working directory. */
        if ((session->ftp_cwd = NutHeapAlloc(2)) == 0) {
            NutHeapFree(session);
            session = 0;
        } else {
            session->ftp_cwd[0] = '/';
            session->ftp_cwd[1] = 0;
        }
    }
    return session;
}

/*!
 * \brief Close an FTP server session.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 */
void NutFtpCloseSession(FTPSESSION * session)
{
    if (session) {
        if (session->ftp_cwd) {
            NutHeapFree(session->ftp_cwd);
        }
        NutHeapFree(session);
    }
}

/*!
 * \brief Process an FTP client's CWD command.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param path    Full absolute path name of the directory.
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpProcessCwd(FTPSESSION * session, char *path)
{
    char *cp = path + strlen(ftp_root);
#ifdef FTPD_FILE
    struct stat st;

    if (*cp && strcmp(cp, "/")) {
        /*
         * Check, if the path exists and if this is a directory. 
         */
        if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
            return NutFtpRespondBad(session, 550);
        }
    }
#endif

    /*
     * Store the new working directory excluding our root part.
     */
    if (*cp == 0) {
        cp = "/";
    }
    if (session->ftp_cwd) {
        NutHeapFree(session->ftp_cwd);
    }
    if ((session->ftp_cwd = NutHeapAlloc(strlen(cp) + 1)) == 0) {
        return NutFtpRespondBad(session, 550);
    }
    strcpy(session->ftp_cwd, cp);

    return NutFtpRespondOk(session, 250);
}

/*!
 * \brief Process an FTP client's DELE command.
 *
 * Causes the specified file to be deleted.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param path    Full absolute path name of the file.
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpProcessDelete(FTPSESSION * session, char *path)
{
#ifdef FTPD_FILE
    if (unlink(path)) {
        return NutFtpRespondBad(session, 550);
    }
#endif
    return NutFtpRespondOk(session, 250);
}

/*!
 * \brief Transfer a file to or from the FTP client.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param path    Full absolute path name of the file.
 * \param mode    If set to zero, the server will accept the data 
 *                transferred via the data connection and to store the 
 *                data as a file. If the file exists, then its contents 
 *                will be replaced by the data being transferred. A new 
 *                file is created, if the file does not already exist.
 *                If this parameter is not equal zero, then the server 
 *                will transfer a copy of the specified file.
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpTransferFile(FTPSESSION * session, char *path, int mode)
{
    TCPSOCKET *sock;
    int ec = 550;
    int fh = 0;

#ifdef FTPD_FILE
    /* Open the file to send. */
    if (mode) {
        fh = _open(path, _O_BINARY | _O_RDONLY);
    }
    /* Open the file to receive. */
    else {
        fh = _open(path, _O_CREAT | _O_TRUNC);
    }
#endif
    if (fh != -1) {
        /* File status OK, opening data connection */
        NutFtpSendMode(session, session->ftp_tran_mode);
        if ((sock = NutFtpDataConnect(session)) != 0) {
            u_short mss = sock->so_mss;
            u_char *buf;

            if (mss < 256) {
                mss = 256;
            }
            if ((buf = NutHeapAlloc(mss)) != 0) {
                int got;

                ec = 0;

                /* Send a file. */
                if (mode) {
#ifdef FTPD_FILE
                    while ((got = _read(fh, buf, mss)) > 0) {
                        if (NutTcpSend(sock, buf, got) != got) {
                            ec = 551;
                            break;
                        }
                    }
#endif
                }

                /* Receive a file. */
                else {
#ifdef FTPD_FILE
                    while ((got = NutTcpReceive(sock, buf, mss)) > 0) {
                        int x;
                        if ((x = _write(fh, buf, got)) != got) {
                            ec = 552;
                            break;
                        }
                    }
#else
                    NETRXBUF *rxbuf;
                    while ((rxbuf = NetRxCreate()) == 0)
                        NutSleep(100);
                    rxbuf->sock = sock;
                    while ((got = NutTcpReceive(sock, buf, mss)) > 0) {
                        NetRxDataIn(buf, got, rxbuf);
                    }
                    rxbuf->sock = 0;
                    if (rxbuf->length == 0)
                        NetRxDestroy(rxbuf);
#endif
                }
                NutHeapFree(buf);
            }
            NutTcpCloseSocket(sock);
        }
#ifdef FTPD_FILE
        _close(fh);

        /* Remove files received with an error. */
        if (mode == 0 && ec) {
            unlink(path);
        }
#endif
    }
    if (ec) {
        return NutFtpRespondBad(session, ec);
    }
    return NutFtpRespondOk(session, 226);
}

/*!
 * \brief Process an FTP client's LIST or NLST command.
 *
 * Causes a directory listing to be sent to the client.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param path    Full absolute path name of the directory.
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpTransferDirectory(FTPSESSION * session, char *path)
{
#ifdef FTPD_FILE
    TCPSOCKET *sock;
    FILE *fp;

    struct stat st;
    DIR *dir;
    struct dirent *d_ent;
    struct tm *gmt;
    u_long size;
    int ec = 550;
    char *name;

    dir = opendir(path);
    if (dir) {
        NutFtpSendMode(session, 0);
        if ((sock = NutFtpDataConnect(session)) != 0) {
            if ((fp = _fdopen((int) sock, "r+b")) != 0) {
                ec = 0;
                while ((d_ent = readdir(dir)) != 0) {
                    if (d_ent->d_name[0] == '.') {
                        continue;
                    }

                    if ((name = NutHeapAlloc(strlen(path) + strlen(d_ent->d_name) + 2)) != 0) {
                        sprintf(name, "%s/%s", path, d_ent->d_name);
                        if (stat(name, &st) == 0) {
                            if (S_ISDIR(st.st_mode)) {
                                fputc('d', fp);
                                size = 0;
                            } else {
                                fputc('-', fp);
                                size = st.st_size;
                            }
                            fprintf(fp, "rw-rw-rw-  1 0 0 %6lu ", size);
                            gmt = gmtime(&st.st_mtime);
                            //fprintf(fp, "%s %u %u ", mon_name[gmt->tm_mon], gmt->tm_mday, 1900 + gmt->tm_year);
                            fprintf(fp, "%.3s %u ", mon_name + gmt->tm_mon * 3, gmt->tm_mday);
                            //fprintf(fp, "%02u:%02u:%02u ", gmt->tm_hour, gmt->tm_min, gmt->tm_sec);
                            fprintf(fp, "%02u:%02u ", gmt->tm_hour, gmt->tm_min);
                            fputs(d_ent->d_name, fp);
                            fputs("\r\n", fp);
                        }
                        NutHeapFree(name);
                    }
                }
                fclose(fp);
            }
            NutTcpCloseSocket(sock);
        }
        closedir(dir);
    }
    if (ec) {
        return NutFtpRespondBad(session, ec);
    }
#endif
    return NutFtpRespondOk(session, 226);
}

/*!
 * \brief Process an FTP client's MKD command.
 *
 * Causes the specified directory to be created.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param path    Full absolute path name of the directory.
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpProcessMkd(FTPSESSION * session, char *path)
{
#ifdef FTPD_FILE
    if (mkdir(path, 0777)) {
        return NutFtpRespondBad(session, 550);
    }
#endif
    return NutFtpRespondOk(session, 257);
}

/*!
 * \brief Process an FTP client's PASS command.
 *
 * Only one login per session is accepted.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param pass    Password.
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpProcessPass(FTPSESSION * session, char *pass)
{
    if (ftp_pass && *ftp_pass) {
        if (session->ftp_login != 1 || strcmp(ftp_pass, pass)) {
            session->ftp_login = 0;
            return NutFtpRespondBad(session, 550);
        }
    }
    session->ftp_login = 2;
    return NutFtpRespondOk(session, 230);
}

/*!
 * \brief Process an FTP client's PASV command.
 *
 * This command requests the server to listen on a data port and to wait 
 * for a connection rather than initiate one upon receipt of a transfer 
 * command.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpProcessPassiv(FTPSESSION * session)
{
    u_long ip = session->ftp_sock->so_local_addr;
    u_short port = FTP_DATA_PORT;

    fprintf(session->ftp_ostream, "227 Passive (%u,%u,%u,%u,%u,%u).\r\n",        /* */
            (u_char) ip, (u_char) (ip >> 8), (u_char) (ip >> 16), (u_char) (ip >> 24),  /* */
            (u_char) (port >> 8), (u_char) port);
    fflush(session->ftp_ostream);
    session->ftp_passive = 1;

    return 0;
}

/*!
 * \brief Process an FTP client's PORT command.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param args    Command argument, which is the concatenation of the
 *                32-bit internet host address and a 16-bit TCP port 
 *                address. This address information is broken into 8-bit 
 *                fields and the value of each field is transmitted as 
 *                a decimal number.
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpProcessPort(FTPSESSION * session, char *args)
{
    if (ParseIpPort(args, &session->ftp_data_ip, &session->ftp_data_port) == 6) {
        if (session->ftp_sock->so_remote_addr == session->ftp_data_ip) {
            return NutFtpRespondOk(session, 200);
        }
        return NutFtpRespondBad(session, 425);
    }
    return NutFtpRespondBad(session, 501);;
}

/*!
 * \brief Process an FTP client's PWD command.
 *
 * Causes the name of the current working directory to be 
 * returned in the reply.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpProcessPwd(FTPSESSION * session)
{
#ifdef FTPD_DEBUG
    printf("\n<'257 \"%s\"' ", session->ftp_cwd);
#endif
    fprintf(session->ftp_ostream, "257 \"%s\"\r\n", session->ftp_cwd);
    return 0;
}

/*!
 * \brief Process an FTP client's RMD command.
 *
 * Causes the specified directory to be removed.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param path    Full absolute path name of the directory.
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpProcessRmd(FTPSESSION * session, char *path)
{
#ifdef FTPD_FILE
    if (rmdir(path)) {
        return NutFtpRespondBad(session, 451);
    }
#endif
    return NutFtpRespondOk(session, 257);
}

/*!
 * \brief Process an FTP client's SYST command.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpProcessSystem(FTPSESSION * session)
{
#ifdef FTPD_DEBUG
    printf("\n<'215 UNIX Type: L8' ");
#endif
    fputs("215 UNIX Type: L8\r\n", session->ftp_ostream);
    return 0;
}

/*!
 * \brief Process an FTP client's TYPE command.
 *
 * The type is not fully checked. Any argument starting with
 * the letters 'A' or 'a' will switch the transfer mode to
 * ASCII. Otherwise binary mode is set.
 *
 * \param session  Pointer to an \ref FTPSESSION structure, obtained by a 
 *                 previous call to NutFtpOpenSession().
 * \param typecode Command arguments.
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpProcessType(FTPSESSION * session, char *typecode)
{
    session->ftp_tran_mode = (*typecode != 'A') && (*typecode != 'a');
    return NutFtpRespondOk(session, 200);
}

/*!
 * \brief Process an FTP client's USER command.
 *
 * Only one login per session is accepted.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param user    User name.
 *
 * \return 0 if the command has been processed or -1 if the
 *         session has been aborted.
 */
int NutFtpProcessUser(FTPSESSION * session, char *user)
{
    if (ftp_user && *ftp_user) {
        if (session->ftp_login && strcmp(ftp_user, user)) {
            session->ftp_login = 0;
            return NutFtpRespondBad(session, 550);
        }
    }

    /* Need a password too. */
    if (ftp_pass && *ftp_pass) {
        session->ftp_login = 1;
        return NutFtpRespondOk(session, 331);
    }

    /* No password required. */
    session->ftp_login = 2;
    return NutFtpRespondOk(session, 230);
}

/*!
 * \brief Process an FTP request.
 *
 * This routine implements the protocol interpreter of the FTP server.
 *
 * \param session Pointer to an \ref FTPSESSION structure, obtained by a 
 *                previous call to NutFtpOpenSession().
 * \param request Request string.
 *
 * \return -1 if the session ended. Otherwise 0 is returned.
 */
int NutFtpProcessRequest(FTPSESSION * session, char *request)
{
    int rc = 0;
    char *cmd;
    char *args;

    /* Split the line into command and argument part. */
    SplitCmdArg(request, &cmd, &args);
#ifdef FTPD_DEBUG
    printf("\n>'%s %s' ", cmd, args);
#endif

    /* QUIT - Terminate session. */
    if (strcmp(cmd, cmd_quit) == 0) {
        NutFtpRespondOk(session, 221);
        rc = -1;
    }

    /* USER <username> - Check user name. */
    else if (strcmp(cmd, cmd_user) == 0) {
        rc = NutFtpProcessUser(session, args);
    }

    /* PASS <password> - Check user password. */
    else if (strcmp(cmd, cmd_pass) == 0) {
        rc = NutFtpProcessPass(session, args);
    } else if (strcmp(cmd, cmd_noop) == 0) {
        NutFtpRespondOk(session, 200);
    }
    /* Anything else requires a successful login. */
    else if (session->ftp_login < 2) {
        rc = NutFtpRespondBad(session, 530);
    }

    /* Valid login. */
    else {

        /* PASV - Prepare passive transfer. */
        if (strcmp(cmd, cmd_pasv) == 0) {
            rc = NutFtpProcessPassiv(session);
        }

        /* PORT <host-port> - Set data connection. */
        else if (strcmp(cmd, cmd_port) == 0) {
            rc = NutFtpProcessPort(session, args);
        }

        /* [X]PWD - Send name of current working directory. */
        else if (strcmp(cmd, cmd_pwd) == 0 || strcmp(cmd, cmd_xpwd) == 0) {
            rc = NutFtpProcessPwd(session);
        }

        /* SYST - Send system identifier. */
        else if (strcmp(cmd, cmd_syst) == 0) {
            rc = NutFtpProcessSystem(session);
        }

        /* TYPE <type-code> - Receive transfer mode. */
        else if (strcmp(cmd, cmd_type) == 0) {
            rc = NutFtpProcessType(session, args);
        }

        /* Commands with path name argument. */
        else {
            char *path;

            if ((path = CreateFullPathName(ftp_root, session->ftp_cwd, args)) == 0) {
                rc = NutFtpRespondBad(session, 451);
            }

            /* CWD <pathname> - Change working directory. */
            else if (strcmp(cmd, cmd_cwd) == 0) {
                rc = NutFtpProcessCwd(session, path);
            }

            /* DELE <pathname> - Delete a file. */
            else if (strcmp(cmd, cmd_dele) == 0) {
                rc = NutFtpProcessDelete(session, path);
            }

            /* LIST | NLST [<pathname>] - Send list of files in a directory. */
            else if (strcmp(cmd, cmd_list) == 0 || strcmp(cmd, cmd_nlst) == 0) {
                rc = NutFtpTransferDirectory(session, path);
            }

            /* MKD <pathname> - Make a directory. */
            else if (strcmp(cmd, cmd_mkd) == 0 || strcmp(cmd, cmd_xmkd) == 0) {
                rc = NutFtpProcessMkd(session, path);
            }

            /* RETR <pathname> - Send a file to the client. */
            else if (strcmp(cmd, cmd_retr) == 0) {
                rc = NutFtpTransferFile(session, path, 1);
            }

            /* RMD <pathname> - Remove a directory. */
            else if (strcmp(cmd, cmd_rmd) == 0 || strcmp(cmd, cmd_xrmd) == 0) {
                rc = NutFtpProcessRmd(session, path);
            }

            /* STOR <pathname> - Receive a file from the client. */
            else if (strcmp(cmd, cmd_stor) == 0) {
                rc = NutFtpTransferFile(session, path, 0);
            }

            /* Anything else is an unknown command. */
            else {
                rc = NutFtpRespondBad(session, 502);
            }

            if (path) {
                NutHeapFree(path);
            }
        }
    }
    return rc;
}

/*!
 * \brief Process an FTP sever session.
 *
 * Processes FTP requests on an established connection with an FTP client.
 * This routine completely implements an FTP server except TCP connect
 * and disconnect.
 *
 * For file transfers, the same maximum segment size and timeouts as set 
 * for this socket will be used for the data connection.
 *
 * \param sock    Socket of an established TCP connection.
 * \param istream Stream of the socket connection, previously opened for 
 *                binary read.
 * \param ostream Stream of the socket connection, previously opened for 
 *                binary write.
 *
 * \return 0 if the session ended successfully or -1 if the session
 *         has been aborted.
 */
int NutFtpServerSession(TCPSOCKET * sock, FILE * istream, FILE * ostream)
{
    int rc = 0;
    FTPSESSION *session;
    char *buff;
    time_t now;
    struct tm *tip;
    char ch;

    /* Set the root path if not yet done. */
    if (NutRegisterFtpRoot(ftp_root)) {
        return -1;
    }

    /* Allocate the command line buffer. */
    if ((buff = NutHeapAlloc(FTP_MAX_CMDBUF)) == 0) {
        return -1;
    }

    /* Create a session structure and open a stream. */
    if ((session = NutFtpOpenSession(sock, istream, ostream)) == 0) {
        NutHeapFree(buff);
        return -1;
    }

    /* Send a welcome banner including system time. */
    time(&now);
    tip = gmtime(&now);
#ifdef FTPD_DEBUG
    printf("\n<'");
    printf(rep_banner, "4.8.7", &mon_name[tip->tm_mon * 3],        /* */
             tip->tm_mday, tip->tm_hour, tip->tm_min, tip->tm_sec);
#endif
    fprintf(session->ftp_ostream, rep_banner, "4.8.7",      /* */
              &mon_name[tip->tm_mon * 3],       /* */
              tip->tm_mday, tip->tm_hour, tip->tm_min, tip->tm_sec);

    /* 
     * Loop for requests. 
     */
    while (rc == 0) {

        /* Flush any previous output and read a new command line. */
        fflush(session->ftp_ostream);
        if (fgets(buff, FTP_MAX_CMDBUF, session->ftp_istream) == 0) {
            rc = -1;
            break;
        }

        /* Skip command lines, which are too long. */
        if ((ch = *(buff + strlen(buff) - 1)) != '\n' && ch != '\r') {
            do {
                if (fgets(buff, FTP_MAX_CMDBUF, session->ftp_istream) == 0) {
                    rc = -1;
                    break;
                }
            } while ((ch = *(buff + strlen(buff) - 1)) != '\n' && ch != '\r');
            if (rc == 0) {
                rc = NutFtpRespondBad(session, 500);
            }
        }

        /* Process the request. */
        else {
            rc = NutFtpProcessRequest(session, buff);
        }
    }

    /* Cleanup and return. */
    NutFtpCloseSession(session);
    NutHeapFree(buff);
    return rc;
}

#endif

ftpd.h                     。。。。。。。。。。。。。。。。。。。。。。。。。。。

#ifndef FTPD_H

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

#define FTPD_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         *
 *                                                                            *
 ******************************************************************************/

/******************************************************************************
 *                                                                            *
 *            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                         *
 *                                                                            *
 ******************************************************************************/

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

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

/*!
 * \brief FTP session information structure.
 */
typedef struct {
    /*! \brief Telnet socket of this session. */
    TCPSOCKET *ftp_sock;
    /*! \brief Input stream associated to the Telnet socket. */
    FILE *ftp_istream;
    /*! \brief Output stream associated to the Telnet socket. */
    FILE *ftp_ostream;
    /*! \brief Current working directory for this session. */
    char *ftp_cwd;
    /*! \brief Target IP for data transfer. */
    u_long ftp_data_ip;
    /*! \brief TCP port for data transfer. */
    u_short ftp_data_port;
    /*! \brief Maximum TCP segment size for data transfer. */
    u_short ftp_maxseg;
    /*! \brief Login status.
     *
     *  - 0: Not logged in.
     *  - 1: Got user's name, but no password.
     *  - 2: Logged in.
     */
    reg_t ftp_login;
    /*! \brief FTP data transfer mode.
     *
     * - 0: ASCII mode.
     * - 1: Binary mode.
     */
    reg_t ftp_tran_mode;
    /*! \brief FTP data transfer connection type.
     *
     * - 0: Active.
     * - 1: Passive.
     */
    reg_t ftp_passive;
} FTPSESSION;

/******************************************************************************
 *                                                                            *
 *    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                    *
 *                                                                            *
 ******************************************************************************/

/* Function prototypes. */
extern int NutRegisterFtpRoot(CONST char *path);
extern int NutFtpServerSession(TCPSOCKET * sock, FILE * istream, FILE * ostream);

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

#endif

httpd.c                    。。。。。。。。。。。。。。。。。。。。。。。。。。。。

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

#define HTTPD_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 <ctype.h>
#include <stdlib.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"
#include "dencode.h"
#include "httpd_p.h"

#if defined(NUTNET)

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

/*! \brief Local major HTTP version. */
#ifndef HTTP_MAJOR_VERSION  
#define HTTP_MAJOR_VERSION  1
#endif

/*! \brief Local minor HTTP version. */
#ifndef HTTP_MINOR_VERSION  
#define HTTP_MINOR_VERSION  1
#endif

/*! \brief Maximum number of requests per connection. */
#ifndef HTTP_KEEP_ALIVE_REQ
#define HTTP_KEEP_ALIVE_REQ 0
#endif

/*! \brief Maximum size of an incoming request line. */
#ifndef HTTP_MAX_REQUEST_SIZE
#define HTTP_MAX_REQUEST_SIZE 256
#endif

/*! \brief Chunk size while sending files. */
#ifndef HTTP_FILE_CHUNK_SIZE
#define HTTP_FILE_CHUNK_SIZE 512
#endif

/******************************************************************************
 *                                                                            *
 *                        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     *
 *                                                                            *
 ******************************************************************************/

/*!
 * \brief Known mime types. 
 */
MIMETYPES mimeTypes[] = {
    {
    ".txt", "text/plain", NULL}, {
    ".html", "text/html", NULL}, {
    ".shtml", "text/html", NULL}, {    
    ".asp", "text/html", NULL}, {
    ".htm", "text/html", NULL}, {
    ".gif", "image/gif", NULL}, {
    ".jpg", "image/jpeg", NULL}, {
    ".png", "image/png", NULL}, {    
    ".pdf", "application/pdf", NULL}, {
    ".js",  "application/x-javascript", NULL}, {
    ".jar", "application/x-java-archive", NULL}, {
    ".css", "text/css", NULL}, {
    ".xml", "text/xml", NULL}, {
    NULL, NULL, 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     *
 *                                                                            *
 ******************************************************************************/

static u_long http_optflags;

/*!
 * \brief Send top lines of a standard HTML header.
 *
 * Sends HTTP and Server version lines.
 *
 * \param stream Stream of the socket connection, previously opened for 
 *               binary read and write.
 * \param req    The associated client request.
 * \param status Response status, error code or 200, if no error occurred.
 * \param title  Error text, or OK, if no error occurred.
 */
void NutHttpSendHeaderTop(FILE * stream, REQUEST * req, int status, char *title)
{
    static const char fmt[] = "HTTP/%d.%d %d %s\r\nServer: Ethernut %s\r\n";

    fprintf(stream, fmt, HTTP_MAJOR_VERSION, HTTP_MINOR_VERSION, status, title, "4.8.7.0");
}

/*!
 * \brief Send bottom lines of a standard HTML header.
 *
 * Sends Content-Type and Content-Length.
 *
 * \deprecated Use NutHttpSendHeaderBottom().
 *
 * \param stream    Stream of the socket connection, previously opened 
 *                  for  binary read and write.
 * \param mime_type Points to a string that specifies the content type. 
 *                  Examples are "text/html", "image/png", 
 *                  "image/gif", "video/mpeg" or "text/css".
 *                  A null pointer is ignored.
 * \param bytes     Content length of the data following this
 *                  header. Ignored, if negative.
 */
void NutHttpSendHeaderBot(FILE * stream, char *mime_type, long bytes)
{
    NutHttpSendHeaderBottom(stream, 0, mime_type, bytes);
}

/*!
 * \brief Send bottom lines of a standard HTML header.
 *
 * Sends Content-Type, Content-Lenght and Connection lines.
 *
 * \param stream    Stream of the socket connection, previously opened 
 *                  for  binary read and write.
 * \param mime_type Points to a string that specifies the content type. 
 *                  Examples are "text/html", "image/png", 
 *                  "image/gif", "video/mpeg" or "text/css".
 *                  A null pointer is ignored.
 * \param bytes     Content length of the data following this
 *                  header. Ignored, if negative.
 */
void NutHttpSendHeaderBottom(FILE * stream, REQUEST * req, char *mime_type, long bytes)
{
    static const char typ_fmt[] = "Content-Type: %s\r\n";
    static const char len_fmt[] = "Content-Length: %ld\r\n";
    static const char con_str[] = "Connection: ";
    static const char ccl_str[] = "close\r\n\r\n";

    if (mime_type)
        fprintf(stream, typ_fmt, mime_type);
    if (bytes >= 0)
        fprintf(stream, len_fmt, bytes);
    fputs(con_str, stream);
#if HTTP_KEEP_ALIVE_REQ
    if ( req && req->req_connection == HTTP_CONN_KEEP_ALIVE) {
        static const char cka_str[] = "Keep-Alive\r\n\r\n";
        fputs(cka_str, stream);
    }
    else {
        fputs(ccl_str, stream);
    }
#else
    fputs(ccl_str, stream);
#endif
}

/*!
 * \brief Send a HTTP error response.
 *
 * A canned error file is used.
 *
 * \param stream Stream of the socket connection, previously opened for 
 *               binary read and write.
 * \param req    Contains the HTTP request.
 * \param status Error code to be returned.
 */
void NutHttpSendError(FILE * stream, REQUEST * req, int status)
{
    static const char err_fmt[] = "<HTML><HEAD><TITLE>%d %s</TITLE></HEAD><BODY>%d %s</BODY></HTML>\r\n";
    static const char auth_fmt[] = "WWW-Authenticate: Basic realm=\"%s\"\r\n";
    char *title;

    switch (status) {
    case 304:
        title = "Not Modified";
        break;
    case 400:
        title = "Bad Request";
        break;
    case 401:
        title = "Unauthorized";
        break;
    case 404:
        title = "Not Found";
        break;
    case 500:
        title = "Internal Error";
        break;
    case 501:
        title = "Not Implemented";
        break;
    default:
        title = "Error";
        break;
    }
#if HTTP_KEEP_ALIVE_REQ
    if (status >= 400) {
        req->req_connection = HTTP_CONN_CLOSE;
    }
#endif
    NutHttpSendHeaderTop(stream, req, status, title);
    if (status == 401) {
        char *cp = 0;
        char *realm = req->req_url;

        if ((cp = strrchr(realm, '/')) != NULL)
            *cp = 0;
        else
            realm = ".";
        if (strcmp(realm, ".") == 0)
            fprintf(stream, auth_fmt, "User Login");
        else if (strstr(realm, "admin") != NULL)
            fprintf(stream, auth_fmt, "Administrator Login");
        else
            fprintf(stream, auth_fmt, realm);
        if (cp)
            *cp = '/';
    }
    NutHttpSendHeaderBottom(stream, req, "text/html", -1);
    fprintf(stream, err_fmt, status, title, status, title);
}

static MIMETYPES *GetMimeEntry(char *name)
{
    MIMETYPES *rc;
    int fl;

    if (name == NULL || (fl = strlen(name)) == 0) {
        return &mimeTypes[1];
    }
    for (rc = mimeTypes; rc->mtyp_ext; rc++) {
        if (strcmp(&(name[fl - strlen(rc->mtyp_ext)]), rc->mtyp_ext) == 0) {
            return rc;
        }
    }
    return &mimeTypes[0];
}

/*!
 * \brief Return the mime type description of a specified file name.
 *
 * The mime type returned is based on the file extension.
 *
 * \param name Name of the file.
 *
 * \return A pointer to a static string, containing the
 *         associated mime type description. If the extension
 *         is not registered, "text/plain; charset=iso-8859-1"
 *         is returned. If the filename is empty, then
 *         "text/html; charset=iso-8859-1" is returned.
 */
char *NutGetMimeType(char *name)
{
    return GetMimeEntry(name)->mtyp_type;
}

/*!
 * \brief Return the mime type handler of a specified file name.
 *
 * This is the function that handles / sends a specific file type to the 
 * client. Specially used for server side includes (shtml files)
 *
 * \param name Name of the file.
 *
 * \return A pointer to a function of the type void (u_char * filename)
 *         If the extension is not registered, the handler for 
 *         "text/plain; charset=iso-8859-1" is returned. 
 *         If the filename is empty, then the handler for 
 *         "text/html; charset=iso-8859-1" is returned.
 */

MIMEHANDLER NutGetMimeHandler(char *name)
{
    return GetMimeEntry(name)->mtyp_handler;
}

/*!
 * \brief URLDecodes a string
 *
 * Takes a url-encoded string and decodes it.
 *
 * \param str String to decode. This is overwritten with
 * the decoded string
 * 
 * \warning To save RAM, the str parameter will be 
 *         overwritten with the encoded string.
 */
void NutHttpURLDecode(char *str)
{
    register char *ptr1, *ptr2, ch;
    char hexstr[3] = { 0, 0, 0 };
    for (ptr1 = ptr2 = str; *ptr1; ptr1++) {
        if (*ptr1 == '+')
            *ptr2++ = ' ';
        else if (*ptr1 == '%') {
            hexstr[0] = ptr1[1];
            hexstr[1] = ptr1[2];
            ch = strtol(hexstr, 0, 16);
            *ptr2++ = ch;
            ptr1 += 2;
        } else
            *ptr2++ = *ptr1;
    }
    *ptr2 = 0;
}

/*!
 * \brief Parses the QueryString
 *
 * Reads the QueryString from a request, and parses it into
 * name/value table. To save RAM, this method overwrites the
 * contents of req_query, and creates a table of pointers
 * into the req_query buffer.
 *
 * \param req Request object to parse
 */
void NutHttpProcessQueryString(REQUEST * req)
{
    register int i;
    register char *ptr;

    if (!req->req_query)
        return;

    req->req_numqptrs = 1;
    for (ptr = req->req_query; *ptr; ptr++)
        if (*ptr == '&')
            req->req_numqptrs++;

    req->req_qptrs = (char **) NutHeapAlloc(sizeof(char *) * (req->req_numqptrs * 2));
    if (req->req_qptrs == NULL) {
        /* Out of memory */
        req->req_numqptrs = 0;
        return;
    }
    req->req_qptrs[0] = req->req_query;
    req->req_qptrs[1] = NULL;
    for (ptr = req->req_query, i = 2; *ptr; ptr++) {
        if (*ptr == '&') {
            req->req_qptrs[i] = ptr + 1;
            req->req_qptrs[i + 1] = NULL;
            *ptr = 0;
            i += 2;
        }
    }

    for (i = 0; i < req->req_numqptrs; i++) {
        for (ptr = req->req_qptrs[i * 2]; *ptr; ptr++) {
            if (*ptr == '=') {
                req->req_qptrs[i * 2 + 1] = ptr + 1;
                *ptr = 0;
                NutHttpURLDecode(req->req_qptrs[i * 2 + 1]);
                break;
            }
        }
        NutHttpURLDecode(req->req_qptrs[i * 2]);
    }
}

static void NutHttpProcessFileRequest(FILE * stream, REQUEST * req)
{
    int fd;
    int n;
    char *data;
    long file_len;
    MIMEHANDLER handler;
    char *mime_type;
    char *filename = NULL;
    char *modstr = NULL;
    
    /*
     * Validate authorization.
     */
    if (NutHttpAuthValidate(req)) {
        NutHttpSendError(stream, req, 401);
        return;
    }

    /*
     * Process CGI.
     */
    if (NutCgiCheckRequest(stream, req)) {
        return;
    }    

    /* Check if request is a card device. */
    if (strncmp(req->req_url, "card-dev/", 9) == 0) {
        if ((filename = NutHeapAlloc(strlen(req->req_url) + 10)) == NULL) {
            NutHttpSendError(stream, req, 500);
            return;
        }
        strcpy(filename, "CARD:");
        strcat(filename, req->req_url + 9);
        if ((fd = _open(filename, _O_BINARY | _O_RDONLY)) == -1) {
            NutHeapFree(filename);
        }
    } else

    for (n = 0, fd = -1; default_files[n]; n++) {
        filename = CreateFilePath(req->req_url, default_files[n]);
        if (filename == NULL) {
            NutHttpSendError(stream, req, 500);
            return;
        }
        /*
         * Note, that simple file systems may not provide stat() or access(),
         * thus trying to open the file is the only way to check for existence.
         * Another problem is, that PHAT allows to open directories. We use
         * the file length to ensure, that we got a normal file.
         */
        if ((fd = _open(filename, _O_BINARY | _O_RDONLY)) != -1) {
            if (_filelength(fd)) {
                break;
            }
            _close(fd);
        }
        NutHeapFree(filename);
    }
    if (fd == -1) {
        NutHttpSendError(stream, req, 404);
        return;
    }

    /* Check for mime type and handler. */
    mime_type = NutGetMimeType(filename);
    handler = NutGetMimeHandler(filename);

    /* Filename no longer used. */
    NutHeapFree(filename);

    NutHttpSendHeaderTop(stream, req, 200, "Ok");
    if (modstr) {
        fprintf(stream, "Last-Modified: %s GMT\r\n", modstr);
        NutHeapFree(modstr);
    }

    file_len = _filelength(fd);
    /* Use mime handler, if one has been registered. */
    if (handler) {
        NutHttpSendHeaderBottom(stream, req, mime_type, -1);
        handler(stream, fd, file_len, http_root, req);
    }
    /* Use default transfer, if no registered mime handler is available. */
    else {
        NutHttpSendHeaderBottom(stream, req, mime_type, file_len);
        if (req->req_method != METHOD_HEAD) {
            size_t size = HTTP_FILE_CHUNK_SIZE;

            if ((data = NutHeapAlloc(size)) != NULL) {
                while (file_len) {
                    if (file_len < HTTP_FILE_CHUNK_SIZE)
                        size = (size_t) file_len;
    
                    n = _read(fd, data, size);
                    if (n <= 0) {
                        /* We can't do much here, the header is out already. */
                        break;
                    }
                    if (fwrite(data, 1, n, stream) == 0) {
                        break;
                    }
                    file_len -= (long) n;
                }
                NutHeapFree(data);
            }
        }
    }
    _close(fd);
}

/*!
 *
 */
static char *NextWord(char *str)
{
    while (*str && *str != ' ' && *str != '\t')
        str++;
    if (*str)
        *str++ = 0;
    while (*str == ' ' || *str == '\t')
        str++;
    return str;
}

/*!
 * \brief Create a new request info structure.
 *
 * \return Pointer to an allocated structure or NULL if out of memory.
 */
static REQUEST *CreateRequestInfo(void)
{
    REQUEST *req;

    if ((req = NutHeapAllocClear(sizeof(REQUEST))) != NULL) {
        req->req_version = HTTP_MAJOR_VERSION * 10 + HTTP_MINOR_VERSION;
    }
    return req;
}

/*!
 * \brief Register the HTTP server's root directory.
 *
 * Only one root directory is supported. Subsequent calls will
 * override previous settings.
 *
 * \param path Pathname of the root directory. Must include the
 *             device name followed by a colon followed by a
 *             directory path followed by a trailing slash.
 *
 * \return 0 on success, -1 otherwise.
 */
int NutRegisterHttpRoot(char *path)
{
    int len;

    if (http_root)
        NutHeapFree(http_root);
    if (path && (len = strlen(path)) != 0) {
        if ((http_root = NutHeapAlloc(len + 1)) != NULL)
            strcpy(http_root, path);
        else
            return -1;
    } else
        http_root = NULL;

    return 0;
}

/*!
 * \brief Set HTTP option flags.
 *
 * \param flags Option flags to set. Any of the following may be or'ed:
 * - HTTP_OF_USE_HOST_TIME Date header will be included in response.
 * - HTTP_OF_USE_FILE_TIME Handle file modification time.
 *
 */
void NutHttpSetOptionFlags(u_long flags)
{
    http_optflags = flags;
}

/*!
 * \brief Retrieve HTTP option flags.
 *
 * \return Option flags.
 */
u_long NutHttpGetOptionFlags(void)
{
    return http_optflags;
}

/*!
 * \brief Allocate a buffer for a header field value.
 *
 * \param hfvp Points to the character pointer variable that will receive 
 *             the pointer to the header field value. If the variable does
 *             not contain a NULL pointer upon entry, the routine will
 *             return immediately and will not extract any value. If it
 *             contains a NULL pointer on entry, the routine will allocate
 *             heap memory to store a copy of the extracted value. In this
 *             case the caller is responsible to release the allocation.
 * \param str  Points into a request line, right after the colon. Leading
 *             spaces will be skipped before creating a copy.
 *
 * \return -1 if out of memory, otherwise 0.
 */
static int HeaderFieldValue(char **hfvp, CONST char *str)
{
    /* Do not override existing values. */
    if (*hfvp == NULL) {
        /* Skip spaces. */
        while (*str == ' ' || *str == '\t')
            str++;
        /* Allocate a string copy. */
        if ((*hfvp = NutHeapAlloc(strlen(str) + 1)) == NULL)
            return -1;
        strcpy(*hfvp, str);
    }
    return 0;
}

/*!
 * \brief Process the next HTTP request.
 *
 * Waits for the next HTTP request on an established connection
 * and processes it.
 *
 * \param istream Stream of the socket connection, previously opened for 
 *                binary read.
 * \param ostream Stream of the socket connection, previously opened for 
 *                binary write.
 */
void NutHttpProcessRequest(FILE * istream, FILE * ostream)
{
    REQUEST *req = NULL;
    char *method = NULL;
    char *path;
    char *line;
    char *protocol;
    char *cp;
#if HTTP_KEEP_ALIVE_REQ
    int keep_alive_max = HTTP_KEEP_ALIVE_REQ;
#endif

    for(;;) {
        /* Release resources used on the previous connect. */
        DestroyRequestInfo(req);
        if ((req = CreateRequestInfo()) == NULL)
            break;
        if (method)
            NutHeapFree(method);

        /* The first line contains method, path and protocol. */
        if ((method = NutHeapAlloc(HTTP_MAX_REQUEST_SIZE)) == NULL) {
            break;
        }
        if (fgets(method, HTTP_MAX_REQUEST_SIZE, istream) == NULL) {
            break;
        }
        if ((cp = strchr(method, '\r')) != NULL)
            *cp = 0;
        if ((cp = strchr(method, '\n')) != NULL)
            *cp = 0;

        /*
        * Parse remaining request header lines.
        */
        if ((line = NutHeapAlloc(HTTP_MAX_REQUEST_SIZE)) == NULL) {
            break;
        }
        for (;;) {
            /* Read a line and chop off CR/LF. */
            if (fgets(line, HTTP_MAX_REQUEST_SIZE, istream) == NULL)
                break;
            if ((cp = strchr(line, '\r')) != 0)
                *cp = 0;
            if ((cp = strchr(line, '\n')) != 0)
                *cp = 0;
            if (*line == 0)
                /* Empty line marks the end of the request header. */
                break;
            if (strncmp(line, "Authorization:", 14) == 0) {
                if (HeaderFieldValue(&req->req_auth, line + 14))
                    break;
            } else if (strncmp(line, "Content-Length:", 15) == 0) {
                req->req_length = atol(line + 15);
            } else if (strncmp(line, "Content-Type:", 13) == 0) {
                if (HeaderFieldValue(&req->req_type, line + 13))
                    break;
            } else if (strncmp(line, "Cookie:", 7) == 0) {
                if (HeaderFieldValue(&req->req_cookie, line + 7))
                    break;
            } else if (strncmp(line, "User-Agent:", 11) == 0) {
                if (HeaderFieldValue(&req->req_agent, line + 11))
                    break;
            } else if (strncmp(line, "Referer:", 8) == 0) {
                if (HeaderFieldValue(&req->req_referer, line + 8))
                    break;
            } else if (strncmp(line, "Host:", 5) == 0) {
                if (HeaderFieldValue(&req->req_host, line + 5))
                    break;
            }
#if HTTP_KEEP_ALIVE_REQ
            else if (strncmp(line, "Connection:", 11) == 0) {
                if (strncmp(line + 12, "close", 5) == 0) {
                    req->req_connection = HTTP_CONN_CLOSE;
                }
                else if (strncmp(line + 12, "Keep-Alive", 10) == 0) {
                    req->req_connection = HTTP_CONN_KEEP_ALIVE;
                }
            }
#endif
        }
        if (line) {
            NutHeapFree(line);
        }
        path = NextWord(method);
        protocol = NextWord(path);
        NextWord(protocol);

        /* Determine the request method. */
        if (strcmp(method, "GET") == 0)
            req->req_method = METHOD_GET;
        else if (strcmp(method, "HEAD") == 0)
            req->req_method = METHOD_HEAD;
        else if (strcmp(method, "POST") == 0)
            req->req_method = METHOD_POST;
        else {
            NutHttpSendError(ostream, req, 501);
            break;
        }
        if (*path == 0 || *protocol == 0) {
            NutHttpSendError(ostream, req, 400);
            break;
        }

        /* Determine the client's HTTP version. */
        if (strcmp(protocol, "HTTP/1.0") == 0) {
            req->req_version = 10;
#if HTTP_KEEP_ALIVE_REQ
            if (req->req_connection != HTTP_CONN_KEEP_ALIVE) {
                req->req_connection = HTTP_CONN_CLOSE;
            }
#endif
        }
#if HTTP_KEEP_ALIVE_REQ
        else if (req->req_connection != HTTP_CONN_CLOSE) {
            req->req_connection = HTTP_CONN_KEEP_ALIVE;
        }

        /* Limit the number of requests per connection. */
        if (keep_alive_max > 0) {
            keep_alive_max--;
        }
        else {
            req->req_connection = HTTP_CONN_CLOSE;
        }
#else
        req->req_connection = HTTP_CONN_CLOSE;
#endif

        if ((cp = strchr(path, '?')) != 0) {
            *cp++ = 0;
            if ((req->req_query = NutHeapAlloc(strlen(cp) + 1)) == NULL) {
                break;
            }
            strcpy(req->req_query, cp);

            NutHttpProcessQueryString(req);
        }
        NutHttpProcessPostQuery(istream, req);

        if ((req->req_url = NutHeapAlloc(strlen(path) + 1)) == NULL) {
            break;
        }
        strcpy(req->req_url, path);

        if (NutDecodePath(req->req_url) == 0) {
            NutHttpSendError(ostream, req, 400);
        } else {
            NutHttpProcessFileRequest(ostream, req);
        }
        fflush(ostream);

        if (req->req_connection == HTTP_CONN_CLOSE) {
            break;
        }
    }
    DestroyRequestInfo(req);
    if (method)
        NutHeapFree(method);
}

#endif

httpd.h                      。。。。。。。。。。。。。。。。。。。。。。。。。。。。

#ifndef HTTPD_H

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

#define HTTPD_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>
#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             *
 *                                                                            *
 ******************************************************************************/

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

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

#define METHOD_GET  1
#define METHOD_POST 2
#define METHOD_HEAD 3

#define HTTP_CONN_CLOSE         1
#define HTTP_CONN_KEEP_ALIVE    2

#define HTTP_OF_USE_HOST_TIME   0x00000001UL
#define HTTP_OF_USE_FILE_TIME   0x00000002UL

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

typedef struct _REQUEST REQUEST;

/*!
 * \struct _REQUEST httpd.h pro/httpd.h
 * \brief HTTP request information structure.
 */
struct _REQUEST {
    int req_method;             /*!< \brief Request method. */
    int req_version;            /*!< \brief 11 = HTTP/1.1, 10 = HTTP/1.0, 9 = HTTP/0.9 */
    long req_length;            /*!< \brief Content length */
    char *req_url;              /*!< \brief URI portion of the GET or POST request line */
    FILE *req_stream;           /*!< \brief Argument stream. */
    char *req_query;            /*!< \brief Argument string. */
    char *req_type;             /*!< \brief Content type. */
    char *req_cookie;           /*!< \brief Cookie. */
    char *req_auth;             /*!< \brief Authorization info. */
    char *req_agent;            /*!< \brief User agent. */
    char **req_qptrs;           /*!< \brief Table of request parameters */
    int req_numqptrs;           /*!< \brief Number of request parameters */
    time_t req_ims;             /*!< \brief If-modified-since condition. */
    char *req_referer;          /*!< \brief Misspelled HTTP referrer. */
    char *req_host;             /*!< \brief Server host. */
    int req_connection;         /*!< \brief Connection type, HTTP_CONN_. */
};

typedef void (*MIMEHANDLER)(FILE *stream, int fd, int file_len, char *http_root, REQUEST *req);

typedef struct _MIMETYPES MIMETYPES;

struct _MIMETYPES {
    char *mtyp_ext;
    char *mtyp_type;
    MIMEHANDLER mtyp_handler;
};

/*
 * Authorization
 */
typedef struct _AUTHINFO AUTHINFO;

/*!
 * \struct _AUTHINFO httpd.h pro/httpd.h
 * \brief HTTP authorization information structure.
 */
struct _AUTHINFO {
    AUTHINFO *auth_next;    /*!< \brief Link to next AUTHINFO structure */
    char *auth_dirname;     /*!< \brief Pathname of protected directory */
    char *auth_login;       /*!< \brief Login user and password, separated by a colon. */
};

/*
 * CGI
 */
typedef struct _CGIFUNCTION CGIFUNCTION;

/*!
 * \struct _CGIFUNCTION httpd.h pro/httpd.h
 * \brief Registered CGI function.
 */
struct _CGIFUNCTION {
    CGIFUNCTION *cgi_next;      /*!< \brief Link to next CGIFUNCTION structure */
    CONST char *cgi_name;       /*!< \brief Name of this function */
    int (*cgi_func) (FILE *, REQUEST *);        /*!< \brief Pointer to function code. */
};

/******************************************************************************
 *                                                                            *
 *    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 void NutHttpProcessRequest(FILE * istream, FILE * ostream);
extern void NutHttpProcessQueryString(REQUEST * req);
extern void NutHttpSendHeaderTop(FILE * stream, REQUEST * req, int status, char *title);
extern void NutHttpSendHeaderBottom(FILE * stream, REQUEST * req, char *mime_type, long bytes);
extern void NutHttpSendHeaderBot(FILE * stream, char *mime_type, long bytes);
extern void NutHttpSendError(FILE * stream, REQUEST * req, int status);
extern char *NutGetMimeType(char *name);
extern MIMEHANDLER NutGetMimeHandler(char *name);
extern u_char NutSetMimeHandler(char *extension, MIMEHANDLER handler);

/* auth.c */
extern int NutHttpAuthValidate(REQUEST * req);
extern int NutRegisterAuth(CONST char *dirname, CONST char *login);
extern void NutClearAuth(void);

extern void NutHttpSetOptionFlags(u_long flags);
extern u_long NutHttpGetOptionFlags(void);
extern int NutRegisterHttpRoot(char *path);
extern void NutRegisterCgiBinPath(char *path);
extern int NutRegisterCgi(char *name, int (*func) (FILE *, REQUEST *));
extern int NutCgiCheckRequest(FILE * stream, REQUEST * req);
extern void NutCgiProcessRequest(FILE * stream, REQUEST * req, int name_pos);
extern void NutHttpProcessPostQuery(FILE *stream, REQUEST * req);
extern char *NutHttpURLEncode(char *str);
extern void NutHttpURLDecode(char *str);
extern char *NutHttpGetParameter(REQUEST * req, char *name);
extern int NutHttpGetParameterCount(REQUEST * req);
extern char *NutHttpGetParameterName(REQUEST * req, int index);
extern char *NutHttpGetParameterValue(REQUEST * req, int index);

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

#endif

httpd_p.c                  。。。。。。。。。。。。。。。。。。。。。。。。。。。。

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

#define HTTPD_P_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_p.h"

#if defined(NUTNET)

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

/*
 *  W A R N I N G
 *  -------------
 *
 * This file is not part of the Ethernut API.  It exists purely as an
 * implementation detail.  This header file may change from version to
 * version without notice, or even be removed.
 *
 * We mean it.
*/

/*!
 * \brief Default index files.
 *
 * The first entry must contain an empty string.
 */

/*! \brief Default file system. */
#ifndef HTTP_DEFAULT_ROOT
#define HTTP_DEFAULT_ROOT   "UROM:"
#endif

/******************************************************************************
 *                                                                            *
 *                        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     *
 *                                                                            *
 ******************************************************************************/

char *default_files[] = {
    "",
    "/index.html",
    "/index.htm",
    "/default.html",
    "/default.htm",
    "/index.shtml",
    "/index.xhtml",
    "/index.asp",
    "/default.asp",
    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     *
 *                                                                            *
 ******************************************************************************/

char *http_root;


/*!
* \brief Create a file path from an URL.
* \internal
* \param url Pointer to the URL string
* \param addon Filename to be appended to the path
*/
char *CreateFilePath(CONST char *url, CONST char *addon)
{
    char *root = http_root ? http_root : HTTP_DEFAULT_ROOT;
    size_t urll = strlen(url);
    char *path = NutHeapAlloc(strlen(root) + urll + strlen(addon) + 1);

    if (path) {
        strcpy(path, root);
        strcat(path, url);
        if (*addon) {
            strcat(path, addon + (urll == 0 || url[urll - 1] == '/'));
        }
    }
    return path;
}

/*!
* \brief Release request info structure.
* \internal
* \param req Pointer to the info structure. If NULL, nothing
*            is released.
*/
void DestroyRequestInfo(REQUEST * req)
{
    if (req) {
        if (req->req_url)
            NutHeapFree(req->req_url);
        if (req->req_query)
            NutHeapFree(req->req_query);
        if (req->req_type)
            NutHeapFree(req->req_type);
        if (req->req_cookie)
            NutHeapFree(req->req_cookie);
        if (req->req_auth)
            NutHeapFree(req->req_auth);
        if (req->req_agent)
            NutHeapFree(req->req_agent);
        if (req->req_qptrs)
            NutHeapFree(req->req_qptrs);
        if (req->req_referer)
            NutHeapFree(req->req_referer);
        if (req->req_host)
            NutHeapFree(req->req_host);
        NutHeapFree(req);
    }
}

#endif

httpd_p.h              。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

#ifndef HTTPD_P_H

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

#define HTTPD_P_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             *
 *                                                                            *
 ******************************************************************************/

/*
*  W A R N I N G
*  -------------
*
* This file is not part of the Ethernut API.  It exists purely as an
* implementation detail.  This header file may change from version to
* version without notice, or even be removed.
*
* We mean it.
*/

#include "httpd.h"

#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     *
 *                                                                            *
 ******************************************************************************/

extern char *http_root;
extern char *default_files[];

/******************************************************************************
 *                                                                            *
 *       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                    *
 *                                                                            *
 ******************************************************************************/

char *CreateFilePath(CONST char *url, CONST char *addon);
void DestroyRequestInfo(REQUEST * req);

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

#endif

httpopt.c                                      。。。。。。。。。。。。。。。。。。。。。。。。。

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

#define HTTPOPT_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 <ctype.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 "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     *
 *                                                                            *
 ******************************************************************************/

extern MIMETYPES mimeTypes[];
extern char *http_root;

/******************************************************************************
 *                                                                            *
 *    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 Set the mime type handler for a specified file extension.
 *
 * This is the function that handles / sends a specific file type to the
 * client. Specially used for server side includes (shtml files)
 *
 * \param extension Filename extension the handler should be registered for
 * \param handler pointer to a function of the type void (u_char filename)
 * \return 1 on error or 0 on success
 */
u_char NutSetMimeHandler(char *extension, MIMEHANDLER handler)
{
    size_t i;

    if (extension == NULL)
        return 1;
    for (i = 0; mimeTypes[i].mtyp_ext; i++)
        if (strcmp(extension, mimeTypes[i].mtyp_ext) == 0) {
            mimeTypes[i].mtyp_handler = handler;
            return 0;
        }
    return 1;
}

/*!
 * \brief URLEncodes a string
 *
 * \param str String to encode
 *
 * \return A new allocated encoded string, or NULL if str is null, or
 *         if there's not enough memory for the new string.
 *
 * \note Remember to free() to the returned string.
 */
char *NutHttpURLEncode(char *str)
{
    static char *hexdigits = "0123456789ABCDEF";
    register char *ptr1, *ptr2;
    char *encstring;
    int numEncs = 0;

    if (!str)
        return NULL;

    /* Calculate how many characters we need to encode */
    for (ptr1 = str; *ptr1; ptr1++) {
        if (!isalnum((unsigned char)*ptr1) || *ptr1 == '%' || *ptr1 == '&'|| *ptr1 == '+' ||
        *ptr1 == ',' || *ptr1 == ':' || *ptr1 == ';'|| *ptr1 == '='|| *ptr1 == '?'|| *ptr1 == '@')
            numEncs++;
    }
    /* Now we can calculate the encoded string length */
    encstring = (char *) NutHeapAlloc(strlen(str) + 2 * numEncs + 1);
    if (encstring) {
        /* Encode the string. ptr1 refers to the original string,
         * and ptr2 refers to the new string. */
        ptr2 = encstring;
        for (ptr1 = str; *ptr1; ptr1++) {
            if (isalnum((unsigned char)*ptr1) || *ptr1 == '%' || *ptr1 == '&'|| *ptr1 == '+' ||
            *ptr1 == ',' || *ptr1 == ':' || *ptr1 == ';'|| *ptr1 == '='|| *ptr1 == '?'|| *ptr1 == '@')
                *ptr2++ = *ptr1;
            else {
                *ptr2++ = '%';
                *ptr2++ = hexdigits[(*ptr1 >> 4)];
                *ptr2++ = hexdigits[*ptr1 & 0x0F];
            }
        }
        *ptr2++ = 0;
    }
    return encstring;
}

/*!
 * \brief Parses the QueryString
 *
 * Reads the query from input stream and parses it into
 * name/value table. To save RAM, this method allocated ram and
 * uses req_query to store the input data. Then it creates a table
 * of pointers into the req_query buffer.
 *
 * \param stream Input stream
 * \param req Request object to parse
 */
void NutHttpProcessPostQuery(FILE *stream, REQUEST * req)
{
    int got;
    register int i;
    register char *ptr;

    if (req->req_query != NULL)
        return;

    if (!stream)
        return;

    if (req->req_method != METHOD_POST)
        return;

    if (req->req_type && strstr(req->req_type, "multipart/form-data") != NULL) {
        req->req_numqptrs = 0;
        req->req_stream = stream;
        return;
    }

    req->req_query = NutHeapAllocClear(req->req_length + 1);
    if (req->req_query == NULL) {
        /* Out of memory */
        req->req_numqptrs = 0;
        return;
    }

    i = 0;
    while (i < req->req_length) {
        got = fread(req->req_query + i, sizeof(char), req->req_length - i, stream);
        if (got <= 0) {
            NutHeapFree(req->req_query);
            req->req_numqptrs = 0;
            req->req_query = NULL;
            return;
        }
        i += got;
    }

    req->req_numqptrs = 1;
    for (ptr = req->req_query; *ptr; ptr++)
        if (*ptr == '&')
            req->req_numqptrs++;

    req->req_qptrs = (char **) NutHeapAlloc(sizeof(char *) * (req->req_numqptrs * 2));
    if (!req->req_qptrs) {
        /* Out of memory */
        NutHeapFree(req->req_query);
        req->req_numqptrs = 0;
        req->req_query = NULL;
        return;
    }
    req->req_qptrs[0] = req->req_query;
    req->req_qptrs[1] = 0;
    for (ptr = req->req_query, i = 2; *ptr; ptr++) {
        if (*ptr == '&') {
            req->req_qptrs[i] = ptr + 1;
            req->req_qptrs[i + 1] = NULL;
            *ptr = 0;
            i += 2;
        }
    }

    for (i = 0; i < req->req_numqptrs; i++) {
        for (ptr = req->req_qptrs[i * 2]; *ptr; ptr++) {
            if (*ptr == '=') {
                req->req_qptrs[i * 2 + 1] = ptr + 1;
                *ptr = 0;
                NutHttpURLDecode(req->req_qptrs[i * 2 + 1]);
                break;
            }
        }
        NutHttpURLDecode(req->req_qptrs[i * 2]);
    }
}

/*!
 * \brief Gets a request parameter value by name
 *
 * \param req Request object
 * \param name Name of parameter
 *
 * \return Pointer to the parameter value.
 */
char *NutHttpGetParameter(REQUEST * req, char *name)
{
    int i;
    for (i = 0; i < req->req_numqptrs; i++)
        if (strcmp(req->req_qptrs[i * 2], name) == 0)
            return req->req_qptrs[i * 2 + 1];
    return NULL;
}

/*!
 * \brief Gets the number of request parameters
 *
 * The following code fragment retrieves all name/value pairs of the
 * request.
 *
 * \code
 * #include &lt;pro/httpd.h&gt;
 *
 * char *name;
 * char *value;
 * int i;
 * int n = NutHttpGetParameterCount(req);
 *
 * for (i = 0; i &lt; n; i++) {
 *     name = NutHttpGetParameterName(req, i);
 *     value = NutHttpGetParameterValue(req, i);
 * }
 * \endcode
 *
 * \param req Request object
 *
 * \return The number of request parameters
 */
int NutHttpGetParameterCount(REQUEST * req)
{
    return req->req_numqptrs;
}

/*!
 * \brief Gets the name of a request parameter
 *
 * \param req Request object
 * \param index Index of the requested parameter.
 *
 * \return Pointer to the parameter name at the given index,
 *         or NULL if index is out of range.
 */
char *NutHttpGetParameterName(REQUEST * req, int index)
{
    if (index < 0 || index >= NutHttpGetParameterCount(req))
        return NULL;
    return req->req_qptrs[index * 2];
}

/*!
 * \brief Get the value of a request parameter
 *
 * \param req Request object
 * \param index Index to the requested parameter.
 *
 * \return Pointer to the parameter value at the given index,
 *         or NULL if index is out of range.
 */
char *NutHttpGetParameterValue(REQUEST * req, int index)
{
    if (index < 0 || index >= NutHttpGetParameterCount(req))
        return NULL;
    return req->req_qptrs[index * 2 + 1];
}

#endif

resolv.c                             。。。。。。。。。。。。。。。。。。。。。。。。。

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

#define RESOLV_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 <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             *
 *                                                                            *
 ******************************************************************************/

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

#include "..\net\confnet.h"
#include "..\net\inet.h"
#include "..\net\if_var.h"
#include "..\net\socket.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                         *
 *                                                                            *
 ******************************************************************************/

typedef struct {
    u_char *doc_hostname;
    u_char *doc_domain;
    u_long doc_ip1;
    u_long doc_ip2;
} DNSCONFIG;

typedef struct {
    u_short doh_id;
    u_short doh_flags;
    u_short doh_quests;
    u_short doh_answers;
    u_short doh_authrr;
    u_short doh_addrr;
} DNSHEADER;

typedef struct {
    u_char *doq_name;
    u_short doq_type;
    u_short doq_class;
} DNSQUESTION;

typedef struct {
    u_char *dor_name;
    u_short dor_type;
    u_short dor_class;
    u_long dor_ttl;
    u_short dor_len;
    u_char *dor_data;
} DNSRESOURCE;

/******************************************************************************
 *                                                                            *
 *             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     *
 *                                                                            *
 ******************************************************************************/

static DNSCONFIG doc;


#ifdef NUTDEBUG
void DumpDnsHeader(FILE * stream, DNSHEADER * doh)
{
    fprintf(stream,
            "HEADER: id=%u flg=%04X #q=%u #an=%u #au=%u #ad=%u\r\n",
            doh->doh_id, doh->doh_flags, doh->doh_quests, doh->doh_answers, doh->doh_authrr, doh->doh_addrr);
}

void DumpDnsQuestion(FILE * stream, DNSQUESTION * doq)
{
    fprintf(stream, "QUESTION: name='%s' type=%u class=%u\r\n", doq->doq_name, doq->doq_type, doq->doq_class);
}

void DumpDnsResource(FILE * stream, DNSRESOURCE * dor)
{
    u_short i;

    fprintf(stream, "RESOURCE: name='%s' type=%u class=%u ttl=%lu len=%u ",
            dor->dor_name, dor->dor_type, dor->dor_class, dor->dor_ttl, dor->dor_len);
    for (i = 0; i < dor->dor_len; i++)
        fprintf(stream, "%02X ", dor->dor_data[i]);
    fputc('\n', stream);
}
#endif

static u_short AddShort(u_char * cp, u_short val)
{
    *cp++ = (u_char) (val >> 8);
    *cp++ = (u_char) val;

    return 2;
}

static u_short AddName(u_char * cp, CONST u_char * name)
{
    u_char *lcp;
    u_short rc = strlen((char *)name) + 2;

    lcp = cp++;
    *lcp = 0;
    while (*name) {
        if (*name == '.') {
            lcp = cp++;
            *lcp = 0;
            name++;
        } else {
            *cp++ = *name++;
            (*lcp)++;
        }
    }
    *cp = 0;

    return rc;
}

static u_short ScanShort(u_char * cp, u_short * val)
{
    *val = (u_short)(*cp++) << 8;
    *val |= *cp;

    return 2;
}

static u_short ScanLong(u_char * cp, u_long * val)
{
    *val = *cp++;
    *val <<= 8;
    *val |= *cp++;
    *val <<= 8;
    *val |= *cp++;
    *val <<= 8;
    *val |= *cp;

    return 4;
}

static u_short ScanName(u_char * cp, u_char ** npp)
{
    u_char len;
    u_short rc;
    u_char *np;

    if (*npp) {
        NutHeapFree(*npp);
        *npp = 0;
    }

    if ((*cp & 0xC0) == 0xC0)
        return 2;

    rc = strlen((char *)cp) + 1;
    np = *npp = NutHeapAlloc(rc);
    len = *cp++;
    while (len) {
        while (len--)
            *np++ = *cp++;
        if ((len = *cp++) != 0)
            *np++ = '.';
    }
    *np = 0;

    return rc;
}

static u_short ScanBinary(u_char * cp, u_char ** npp, u_short len)
{
    if (*npp)
        NutHeapFree(*npp);
    *npp = NutHeapAlloc(len);
    memcpy(*npp, cp, len);

    return len;
}

static DNSHEADER *CreateDnsHeader(DNSHEADER * doh, u_short id)
{
    if (doh == 0)
        doh = NutHeapAllocClear(sizeof(DNSHEADER));
    if (doh) {
        doh->doh_id = id;
        doh->doh_flags = 0x0100;
        doh->doh_quests = 1;
    }
    return doh;
}

static void ReleaseDnsHeader(DNSHEADER * doh)
{
    if (doh)
        NutHeapFree(doh);
}

static u_short EncodeDnsHeader(u_char * buf, DNSHEADER * doh)
{
    u_short rc;

    rc = AddShort(buf, doh->doh_id);
    rc += AddShort(buf + rc, doh->doh_flags);
    rc += AddShort(buf + rc, doh->doh_quests);
    rc += AddShort(buf + rc, doh->doh_answers);
    rc += AddShort(buf + rc, doh->doh_authrr);
    rc += AddShort(buf + rc, doh->doh_addrr);

    return rc;
}

static u_short DecodeDnsHeader(DNSHEADER * doh, u_char * buf)
{
    u_short rc;

    rc = ScanShort(buf, &doh->doh_id);
    rc += ScanShort(buf + rc, &doh->doh_flags);
    rc += ScanShort(buf + rc, &doh->doh_quests);
    rc += ScanShort(buf + rc, &doh->doh_answers);
    rc += ScanShort(buf + rc, &doh->doh_authrr);
    rc += ScanShort(buf + rc, &doh->doh_addrr);

    return rc;
}

static DNSQUESTION *CreateDnsQuestion(DNSQUESTION * doq, CONST u_char * name, u_short type)
{
    if (doq == 0)
        doq = NutHeapAllocClear(sizeof(DNSQUESTION));
    if (doq) {
        if (doq->doq_name)
            NutHeapFree(doq->doq_name);
        doq->doq_name = NutHeapAlloc(strlen((char *)name) + 1);
        strcpy((char *)doq->doq_name, (char *)name);
        doq->doq_type = type;
        doq->doq_class = 1;
    }
    return doq;
}

static void ReleaseDnsQuestion(DNSQUESTION * doq)
{
    if (doq) {
        if (doq->doq_name)
            NutHeapFree(doq->doq_name);
        NutHeapFree(doq);
    }
}

static u_short EncodeDnsQuestion(u_char * buf, DNSQUESTION * doq)
{
    u_short rc;

    rc = AddName(buf, doq->doq_name);
    rc += AddShort(buf + rc, doq->doq_type);
    rc += AddShort(buf + rc, doq->doq_class);

    return rc;
}

static u_short DecodeDnsQuestion(DNSQUESTION * doq, u_char * buf)
{
    u_short rc;

    rc = ScanName(buf, &doq->doq_name);
    rc += ScanShort(buf + rc, &doq->doq_type);
    rc += ScanShort(buf + rc, &doq->doq_class);

    return rc;
}

static DNSRESOURCE *CreateDnsResource(DNSRESOURCE * dor)
{
    if (dor == 0)
        dor = NutHeapAllocClear(sizeof(DNSRESOURCE));
    return dor;
}

static void ReleaseDnsResource(DNSRESOURCE * dor)
{
    if (dor) {
        if (dor->dor_name)
            NutHeapFree(dor->dor_name);
        if (dor->dor_data)
            NutHeapFree(dor->dor_data);
        NutHeapFree(dor);
    }
}

static u_short DecodeDnsResource(DNSRESOURCE * dor, u_char * buf)
{
    u_short rc;

    rc = ScanName(buf, &dor->dor_name);
    rc += ScanShort(buf + rc, &dor->dor_type);
    rc += ScanShort(buf + rc, &dor->dor_class);
    rc += ScanLong(buf + rc, &dor->dor_ttl);
    rc += ScanShort(buf + rc, &dor->dor_len);
    rc += ScanBinary(buf + rc, &dor->dor_data, dor->dor_len);

    return rc;
}

/*!
 * \brief Set DNS configuration.
 *
 * \param hostname DNS name of the local host.
 * \param domain Name of the domain of the local host.
 * \param pdnsip IP address of the primary DNS server.
 * \param sdnsip IP address of the secondary DNS server.
 */
void NutDnsConfig2(u_char * hostname, u_char * domain, u_long pdnsip, u_long sdnsip)
{
    if (doc.doc_hostname) {
        NutHeapFree(doc.doc_hostname);
        doc.doc_hostname = 0;
    }
    if (doc.doc_domain) {
        NutHeapFree(doc.doc_domain);
        doc.doc_domain = 0;
    }
    if (hostname) {
        doc.doc_hostname = NutHeapAlloc(strlen((char *)hostname) + 1);
        strcpy((char *)doc.doc_hostname, (char *)hostname);
    }
    if (domain) {
        doc.doc_domain = NutHeapAlloc(strlen((char *)domain) + 1);
        strcpy((char *)doc.doc_domain, (char *)domain);
    }
    doc.doc_ip1 = pdnsip;
    doc.doc_ip2 = sdnsip;

    confnet.cdn_pdns = pdnsip;
    confnet.cdn_sdns = sdnsip;

    NutNetSaveConfig();
}

/*!
 * \brief Sets DNS configuration.
 *
 * \deprecated New applications should use NutDnsConfig2().
 *
 * \param hostname DNS name of the local host.
 * \param domain Name of the domain of the local host.
 * \param dnsip IP address of the DNS server.
 */
void NutDnsConfig(u_char * hostname, u_char * domain, u_long dnsip)
{
    NutDnsConfig2(hostname, domain, dnsip, 0);
}

void NutDnsGetConfig2(char ** hostname, char ** domain, u_long *pdnsip, u_long *sdnsip)
{
    if (hostname) {
        *hostname = (char *)doc.doc_hostname;
    }
    if (domain) {
        *domain = (char *)doc.doc_domain;
    }
    if (pdnsip) {
        *pdnsip = doc.doc_ip1;
    }
    if (sdnsip) {
        *sdnsip = doc.doc_ip2;
    }
}

/*!
 * \brief Retrieves IP-address corresponding to a host name.
 *
 * This is a very simple implementation, which will not return
 * any other resource information than the IP address.
 *
 * \param hostname Fully qualified domain name of the host.
 * \param type     Request type.
 *
 * \return IP address, which is zero, if the name could not
 *         be resolved.
 */
u_long NutDnsGetResource(CONST u_char * hostname, CONST u_short type);

u_long NutDnsGetHostByName(CONST u_char * hostname)
{
    return NutDnsGetResource(hostname, 1);
}

u_long NutDnsGetMxByDomain(CONST u_char * hostname)
{
    return NutDnsGetResource(hostname, 0x0F);
}

u_long NutDnsGetResource(CONST u_char * hostname, CONST u_short type)
{
    u_long ip = 0;
    u_char *pkt;
    u_short len;
    u_short id = 0;
    UDPSOCKET *sock;
    DNSHEADER *doh = 0;
    DNSQUESTION *doq = 0;
    DNSRESOURCE *dor = 0;
    int n;
    int retries;
    u_long raddr;
    u_short rport;

    /*
     * We need a configured DNS address.
     */
    if (doc.doc_ip1 == 0 && doc.doc_ip2 == 0)
        return 0;

    /*
     * Create client socket and allocate
     * a buffer for the UDP packet.
     */
    if ((sock = NutUdpCreateSocket(0)) == 0)
        return 0;
    pkt = NutHeapAlloc(512);

    for (retries = 0; retries < 6; retries++) {

        /*
         * Create standard header info structures.
         */
        doh = CreateDnsHeader(doh, ++id);
        doq = CreateDnsQuestion(doq, hostname, type);

#ifdef NUTDEBUG
        //DumpDnsHeader(doh);
        //DumpDnsQuestion(doq);
#endif

        /*
         * Encode the header info into the packet buffer
         * and send it to the DNS server.
         */
        len = EncodeDnsHeader(pkt, doh);
        len += EncodeDnsQuestion(pkt + len, doq);

        if ((retries & 1) == 0 || doc.doc_ip2 == 0) {
            if (NutUdpSendTo(sock, doc.doc_ip1, 53, pkt, len) < 0)
                break;
        } else {
            if (NutUdpSendTo(sock, doc.doc_ip2, 53, pkt, len) < 0)
                break;
        }

        /*
         * Loop until we receive a response with the
         * expected id or until timeout.
         */
        for (;;) {
            len = 0;
            n = NutUdpReceiveFrom(sock, &raddr, &rport, pkt, 512, 1000);
            if (n <= 0)
                break;
            if (n > 12) {
                len = DecodeDnsHeader(doh, pkt);
#ifdef NUTDEBUG
                //DumpDnsHeader(doh);
#endif
                if (doh->doh_id == id)
                    break;
            }
        }

        /*
         * Decode the answer.
         */
        if (len && doh->doh_quests == 1) {
            len += DecodeDnsQuestion(doq, pkt + len);
#ifdef NUTDEBUG
            //DumpDnsQuestion(doq);
#endif
            if (doh->doh_answers < 1)
                break;
            else {
                for (n = 1; n <= (int) doh->doh_answers; n++) {
                    dor = CreateDnsResource(dor);
                    len += DecodeDnsResource(dor, pkt + len);
#ifdef NUTDEBUG
                    //DumpDnsResource(dor);
#endif
                    if (dor->dor_type == 1)
                        break;
                }
                if (dor->dor_len == 4) {
                    ip = *dor->dor_data;
                    ip += (u_long)(*(dor->dor_data + 1)) << 8;
                    ip += (u_long)(*(dor->dor_data + 2)) << 16;
                    ip += (u_long)(*(dor->dor_data + 3)) << 24;
                    break;
                }
                /* TBD: 18.3.2004 - for MX requests authoritative rrs should be skipped + additional rrs should be searched for IP address */
            }
        }
    }

    /*
     * Clean up.
     */
    ReleaseDnsHeader(doh);
    ReleaseDnsQuestion(doq);
    ReleaseDnsResource(dor);

    NutHeapFree(pkt);
    NutUdpDestroySocket(sock);

    return ip;
}

#endif

snmp.c                     。。。。。。。。。。。。。。。。。。。。。。。。。。。

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

#define SNMP_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 <stdlib.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 "snmp.h"
#include "snmp_api.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     *
 *                                                                            *
 ******************************************************************************/

/*!
 * \brief Parse an SNMP variable.
 *
 * \param data  Pointer to start of the name/value pair.
 * \param dlen  Contains the number of valid bytes following the 
 *              start of the variable. On exit, it is returned as 
 *              the number of valid bytes following the end of 
 *              this variable.
 * \param name  Pointer to a buffer that receives the name (OID).
 * \param nlen  On entry, this contains the maximum number of 
 *              sub IDs accepted for the name. On exit, it is 
 *              returned as the actual number sub IDs found in
 *              the name.
 * \param type  Pointer to the variable that receives the ASN 
 *              type of the value.
 * \param value Pointer to variable that receives a pointer to
 *              the ASN1 encoded value of variable.
 * \param vlen  Pointer to the variable that receives the length
 *              of the value.
 *
 * \return Pointer to the first byte past the end of this name/value pair. 
 *         Returns NULL on any error.
 */
CONST u_char *SnmpVarParse(CONST u_char * data, size_t * dlen, OID * name, size_t * nlen, u_char * type,
                           u_char ** value, size_t * vlen)
{
    CONST u_char *dp;
    u_char vtype = ASN_SEQUENCE | ASN_CONSTRUCTOR;
    size_t len = *dlen;

    /* Get the object's length and check its type. */
    if ((dp = AsnSequenceParse(data, &len, vtype)) == NULL) {
        return NULL;
    }
    /* Get type, value and length of the name. */
    if ((dp = AsnOidParse(dp, &len, &vtype, name, nlen)) == NULL) {
        return NULL;
    }
    /* Check the name's type. */
    if (vtype != (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID)) {
        return NULL;
    }
    /* Return a pointer to the value. */
    *value = (u_char *) dp;
    /* Find out what type of object this is. */
    if ((dp = AsnHeaderParse(dp, &len, type)) == NULL) {
        return NULL;
    }
    *vlen = len;
    dp += *vlen;
    *dlen -= dp - data;

    return dp;
}

/*!
 * \brief Build an SNMP variable.
 *
 * \param data  Pointer to start of the output buffer.
 * \param dlen  Contains the number of valid bytes following the 
 *              start of the variable. On exit, it is returned as 
 *              the number of valid bytes following the end of 
 *              this variable.
 * \param name  Name (OID).
 * \param nlen  Number of sub IDs of the name.
 * \param type  ASN type of the value.
 * \param value Pointer to the value.
 * \param vlen  Length of the value.
 *
 * \return Pointer to the first byte past the end of this name/value pair. 
 *         Returns NULL on any error.
 */
u_char *SnmpVarBuild(u_char * data, size_t * dlen, CONST OID * name, size_t nlen, u_char type, CONST u_char * value, size_t vlen)
{
    size_t headerLen = 4;
    u_char *dp;

    /* 
     * The final length is not known now, thus the header will have to 
     * be build later. 
     */
    if (*dlen < headerLen) {
        SnmpStatsInc(SNMP_STAT_OUTTOOBIGS);
        return NULL;
    }
    *dlen -= headerLen;
    dp = data + headerLen;

    /* Build the name. */
    if ((dp = AsnOidBuild(dp, dlen, ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OBJECT_ID, name, nlen)) == NULL) {
        SnmpStatsInc(SNMP_STAT_OUTTOOBIGS);
        return NULL;
    }
    /* Build the value. */
    switch (type) {
    case ASN_INTEGER:
        dp = AsnIntegerBuild(dp, dlen, type, (long *) value);
        break;
    case ASN_GAUGE:
    case ASN_COUNTER:
    case ASN_TIMETICKS:
    case ASN_UINTEGER:
        dp = AsnUnsignedBuild(dp, dlen, type, (u_long *) value);
        break;
    case ASN_COUNTER64:
        dp = AsnUnsigned64Build(dp, dlen, type, (UNSIGNED64 *) value);
        break;
    case ASN_OCTET_STR:
    case ASN_IPADDRESS:
    case ASN_OPAQUE:
    case ASN_NSAP:
        dp = AsnOctetStringBuild(dp, dlen, type, value, vlen);
        break;
    case ASN_OBJECT_ID:
        dp = AsnOidBuild(dp, dlen, type, (OID *) value, vlen / sizeof(OID));
        break;
    case ASN_NULL:
        dp = AsnNullBuild(dp, dlen, type);
        break;
    case ASN_BIT_STR:
        dp = AsnBitStringBuild(dp, dlen, type, value, vlen);
        break;
    case SNMP_NOSUCHOBJECT:
    case SNMP_NOSUCHINSTANCE:
    case SNMP_ENDOFMIBVIEW:
        dp = AsnNullBuild(dp, dlen, type);
        break;
    default:
        SnmpStatsInc(SNMP_STAT_OUTBADVALUES);
        return NULL;
    }
    /* Now build the header. */
    if (dp) {
        size_t dummyLen = (dp - data) - headerLen;
        AsnSequenceBuild(data, &dummyLen, ASN_SEQUENCE | ASN_CONSTRUCTOR, dummyLen);
    } else {
        SnmpStatsInc(SNMP_STAT_OUTTOOBIGS);
    }
    return dp;
}

#endif

snmp.h                         。。。。。。。。。。。。。。。。。。。。。。。。。

#ifndef SNMP_H

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

#define SNMP_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             *
 *                                                                            *
 ******************************************************************************/

#include "asn1.h"

#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 SNMP_PORT
/*! \brief Standard UDP port for SNMP agents to receive requests messages. 
 */
#define SNMP_PORT       161
#endif

#ifndef SNMP_TRAP_PORT
/*! \brief Standard UDP port for SNMP managers to receive notificaion messages. 
 */
#define SNMP_TRAP_PORT  162
#endif

#ifndef SNMP_MAX_LEN
/*! \brief Default maximum message size. 
 */
#define SNMP_MAX_LEN    768
#endif

/*! \brief SNMPv1.
 *
 * The original version, defined by RFC 1157.
 */
#define SNMP_VERSION_1        0

/*! \brief SNMPv2c.
 *
 * Community string-based SNMPv2, which was an attempt to combine the 
 * protocol operations of SNMPv2 with the security of SNMPv1, defined 
 * by RFCs 1901, 1905, and 1906.
 *
 * Partly supplied by this code, work is in progress.
 */
#define SNMP_VERSION_2C        1

/*! \brief SNMPv3.
 *
 * An attempt by the IETF working group to merge the SNMPv2u and SNMPv2* 
 * proposals into a more widely accepted SNMPv3. The original version, 
 * defined by RFC 1157.
 *
 * Not yet supported by this code.
 */
#define SNMP_VERSION_3      3

/*
 * PDU types in SNMPv1, SNMPv2c and SNMPv3.
 */
#define SNMP_MSG_GET        (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x0)
#define SNMP_MSG_GETNEXT    (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x1)
#define SNMP_MSG_RESPONSE   (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x2)
#define SNMP_MSG_SET        (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x3)

/*
 * PDU types in SNMPv1. 
 */
#define SNMP_MSG_TRAP       (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x4)

/*
 * PDU types in SNMPv2c and SNMPv3 
 */
#define SNMP_MSG_GETBULK    (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x5)
#define SNMP_MSG_INFORM     (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x6)
#define SNMP_MSG_TRAP2      (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x7)

/*
 * PDU types in SNMPv3 
 */
#define SNMP_MSG_REPORT     (ASN_CONTEXT | ASN_CONSTRUCTOR | 0x8)

/*
 * Exception values for SNMPv2c and SNMPv3 
 */
#define SNMP_NOSUCHOBJECT    (ASN_CONTEXT | ASN_PRIMITIVE | 0x0)
#define SNMP_NOSUCHINSTANCE  (ASN_CONTEXT | ASN_PRIMITIVE | 0x1)
#define SNMP_ENDOFMIBVIEW    (ASN_CONTEXT | ASN_PRIMITIVE | 0x2)

/*
 * Error codes in SNMPv1, SNMPv2c and SNMPv3 PDUs.
 */
#define SNMP_ERR_NOERROR            0
#define SNMP_ERR_TOOBIG                1
#define SNMP_ERR_NOSUCHNAME         2
#define SNMP_ERR_BADVALUE           3
#define SNMP_ERR_READONLY           4
#define SNMP_ERR_GENERR                5

/*
 * Error codes in SNMPv2c and SNMPv3 PDUs.
 */
#define SNMP_ERR_NOACCESS               6
#define SNMP_ERR_WRONGTYPE              7
#define SNMP_ERR_WRONGLENGTH            8
#define SNMP_ERR_WRONGENCODING          9
#define SNMP_ERR_WRONGVALUE             10
#define SNMP_ERR_NOCREATION             11
#define SNMP_ERR_INCONSISTENTVALUE      12
#define SNMP_ERR_RESOURCEUNAVAILABLE    13
#define SNMP_ERR_COMMITFAILED           14
#define SNMP_ERR_UNDOFAILED             15
#define SNMP_ERR_AUTHORIZATIONERROR     16
#define SNMP_ERR_NOTWRITABLE            17
#define SNMP_ERR_INCONSISTENTNAME    18

/*
 * Values of the generic-trap field in trap PDUs.
 */
#define SNMP_TRAP_COLDSTART        0
#define SNMP_TRAP_WARMSTART        1
#define SNMP_TRAP_LINKDOWN        2
#define SNMP_TRAP_LINKUP        3
#define SNMP_TRAP_AUTHFAIL        4
#define SNMP_TRAP_EGPNEIGHBORLOSS    5
#define SNMP_TRAP_ENTERPRISESPECIFIC    6

/*
 * Basic OID values 
 */
#define SNMP_OID_INTERNET       1, 3, 6, 1
#define SNMP_OID_ENTERPRISES    SNMP_OID_INTERNET, 4, 1
#define SNMP_OID_MIB2           SNMP_OID_INTERNET, 2, 1
#define SNMP_OID_SNMPV2         SNMP_OID_INTERNET, 6
#define SNMP_OID_SNMPMODULES    SNMP_OID_SNMPV2, 3


#define SNMP_PARSE_ERROR    -1
#define SNMP_BUILD_ERROR    -2

/*! \brief Maximum length of a community name. */
#define MAX_SID_LEN    32
/*! \brief Maximum number of sub IDs in an OID. */
#define MAX_NAME_LEN    128

#define SNMP_ACT_RESERVE1    0
#define SNMP_ACT_RESERVE2    1
#define SNMP_ACT_COMMIT      2
#define SNMP_ACT_ACTION      3
#define SNMP_ACT_FREE        4

/******************************************************************************
 *                                                                            *
 *                 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 CONST u_char *SnmpVarParse(CONST u_char *, size_t *, OID *, size_t *, u_char *, u_char **, size_t *);
extern u_char *SnmpVarBuild(u_char *, size_t *, CONST OID *, size_t, u_char , CONST u_char *, size_t);

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

#endif

snmp_agent.c                        。。。。。。。。。。。。。。。。。。。。。。。。。。

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

#define SNMP_AGENT_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 "..\net\inet.h"

#include "snmp_config.h"
#include "snmp.h"
#include "snmp_api.h"
#include "snmp_auth.h"
#include "snmp_mib.h"
#include "snmp_agent.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     *
 *                                                                            *
 ******************************************************************************/


/*
 * Using this as a global had been derived from the original CMU code.
 * It is very ugly (shiffer), but may require some effort to transform
 * it into something local.
 */
static u_char *packet_end;

static void SetVariable(CONST u_char * var_val, u_char var_val_type, u_char * statP, size_t statLen)
{
    size_t buffersize = 1000;

    switch (var_val_type) {
    case ASN_INTEGER:
    case ASN_COUNTER:
    case ASN_GAUGE:
    case ASN_TIMETICKS:
        AsnIntegerParse(var_val, &buffersize, &var_val_type, (long *) statP);
        break;
    case ASN_OCTET_STR:
    case ASN_IPADDRESS:
    case ASN_OPAQUE:
        AsnOctetStringParse(var_val, &buffersize, &var_val_type, statP, &statLen);
        break;
    case ASN_OBJECT_ID:
        AsnOidParse(var_val, &buffersize, &var_val_type, (OID *) statP, &statLen);
        break;
    }
}

/*!
 * \brief Parse a list of variables.
 *
 * \param data       Pointer to the start of the list.
 * \param length     Contains the number of valid bytes following the 
 *                   start of the list.
 * \param out_data   Pointer to the output buffer.
 * \param out_length Number of bytes available in the output buffer.
 * \param index      Error index.
 * \param msgtype    Type of the incoming packet.
 * \param action     Action to perform, either SNMP_ACT_RESERVE1, 
 *                   SNMP_ACT_RESERVE2, SNMP_ACT_COMMIT, SNMP_ACT_ACTION 
 *                   or SNMP_ACT_FREE.
 *
 * \return 0 on success. Otherwise an error code is returned.
 *
 */
static int SnmpVarListParse(SNMP_SESSION * sess, CONST u_char * data, size_t length, u_char * out_data, size_t out_length,
                            long *index, int msgtype, int action)
{
    OID var_name[MAX_OID_LEN];
    size_t var_name_len;
    size_t var_val_len;
    u_char var_val_type;
    u_char *var_val;
    u_char statType;
    u_char *statP;
    size_t statLen;
    u_short acl;
    int exact, err;
    WMETHOD *wmethod;
    u_char *headerP;
    u_char *var_list_start;
    size_t dummyLen;
    int noSuchObject = 0;

    exact = (msgtype != SNMP_MSG_GETNEXT);
    /* Check if the list starts with a sequence header and get its length. */
    if ((data = AsnSequenceParse(data, &length, ASN_SEQUENCE | ASN_CONSTRUCTOR)) == NULL) {
        return SNMP_PARSE_ERROR;
    }

    /* Build ASN header. */
    headerP = out_data;
    if ((out_data = AsnSequenceBuild(out_data, &out_length, ASN_SEQUENCE | ASN_CONSTRUCTOR, 0)) == NULL) {
        return SNMP_BUILD_ERROR;
    }
    var_list_start = out_data;

    *index = 1;
    while (length > 0) {
        /* Get name and ASN1 encoded value of the next variable. */
        var_name_len = MAX_OID_LEN;
        if ((data = SnmpVarParse(data, &length, var_name, &var_name_len, &var_val_type, &var_val, &var_val_len)) == NULL) {
            return SNMP_PARSE_ERROR;
        }

        /* Now attempt to retrieve the variable on the local entity. */
        statP = SnmpMibFind(var_name, &var_name_len, &statType, &statLen, &acl, exact, &wmethod, &noSuchObject);

        /* Check access. */
        if (msgtype == SNMP_MSG_SET) {
            /* Make sure we have write access. */
            if (acl != ACL_RWRITE) {
                return sess->sess_version == SNMP_VERSION_1 ? SNMP_ERR_NOSUCHNAME : SNMP_ERR_NOTWRITABLE;
            }
            if (wmethod == NULL) {
                if (statP == NULL) {
                    return sess->sess_version == SNMP_VERSION_1 ? SNMP_ERR_NOSUCHNAME : SNMP_ERR_NOCREATION;
                }
                /* Check if the type and value is consistent with this entity's variable. */
                if (var_val_len > statLen || var_val_type != statType) {
                    return sess->sess_version == SNMP_VERSION_1 ? SNMP_ERR_BADVALUE : SNMP_ERR_WRONGTYPE;
                }
                /* Actually do the set if necessary. */
                if (action == SNMP_ACT_COMMIT) {
                    SetVariable(var_val, var_val_type, statP, statLen);
                }
            } else {
                err = (*wmethod) (action, var_val, var_val_type, var_val_len, var_name, var_name_len);

                /*
                 * Map the SNMPv2 error codes to SNMPv1 error codes (RFC 2089).
                 */
                if (err && sess->sess_version == SNMP_VERSION_1) {
                    switch (err) {
                    case SNMP_ERR_WRONGVALUE:
                    case SNMP_ERR_WRONGENCODING:
                    case SNMP_ERR_WRONGTYPE:
                    case SNMP_ERR_WRONGLENGTH:
                    case SNMP_ERR_INCONSISTENTVALUE:
                        err = SNMP_ERR_BADVALUE;
                        break;
                    case SNMP_ERR_NOACCESS:
                    case SNMP_ERR_NOTWRITABLE:
                    case SNMP_ERR_NOCREATION:
                    case SNMP_ERR_INCONSISTENTNAME:
                    case SNMP_ERR_AUTHORIZATIONERROR:
                        err = SNMP_ERR_NOSUCHNAME;
                        break;
                    default:
                        err = SNMP_ERR_GENERR;
                        break;
                    }
                    return err;
                }
            }
        } else {
            /* Retrieve the value and place it into the outgoing packet. */
            if (statP == NULL) {
                statLen = 0;
                if (exact) {
                    if (noSuchObject) {
                        statType = SNMP_NOSUCHOBJECT;
                    } else {
                        statType = SNMP_NOSUCHINSTANCE;
                    }
                } else {
                    statType = SNMP_ENDOFMIBVIEW;
                }
            }
            out_data = SnmpVarBuild(out_data, &out_length, var_name, var_name_len, statType, statP, statLen);
            if (out_data == NULL) {
                return SNMP_ERR_TOOBIG;
            }
        }
        (*index)++;
    }

    if (msgtype != SNMP_MSG_SET) {
        /*
         * Save a pointer to the end of the packet and
         * rebuild header with the actual lengths
         */
        packet_end = out_data;
        dummyLen = packet_end - var_list_start;
        if (AsnSequenceBuild(headerP, &dummyLen, ASN_SEQUENCE | ASN_CONSTRUCTOR, dummyLen) == NULL) {
            return SNMP_ERR_TOOBIG;
        }
    }
    *index = 0;

    return 0;
}

/*!
 * \brief Clone input packet.
 *
 * Creates a packet identical to the input packet, except for the error 
 * status and the error index which are set according to the specified
 * parameters.
 *
 * \return 0 upon success and -1 upon failure.
 */
static int SnmpCreateIdentical(SNMP_SESSION * sess, CONST u_char * snmp_in, u_char * snmp_out, size_t snmp_length, long errstat,
                               long errindex)
{
    u_char *data;
    u_char type;
    long dummy;
    size_t length;
    size_t headerLength;
    u_char *headerPtr;
    CONST u_char *reqidPtr;
    u_char *errstatPtr;
    u_char *errindexPtr;
    CONST u_char *varListPtr;

    /* Copy packet contents. */
    memcpy(snmp_out, snmp_in, snmp_length);
    length = snmp_length;
    if ((headerPtr = (u_char *) SnmpAuthParse(snmp_out, &length, sess->sess_id, &sess->sess_id_len, &dummy)) == NULL) {
        return -1;
    }
    sess->sess_id[sess->sess_id_len] = 0;

    if ((reqidPtr = AsnHeaderParse(headerPtr, &length, (u_char *) & dummy)) == NULL) {
        return -1;
    }
    headerLength = length;

    /* Request id. */
    if ((errstatPtr = (u_char *) AsnIntegerParse(reqidPtr, &length, &type, &dummy)) == NULL) {
        return -1;
    }
    /* Error status. */
    if ((errindexPtr = (u_char *) AsnIntegerParse(errstatPtr, &length, &type, &dummy)) == NULL) {
        return -1;
    }
    /* Error index. */
    if ((varListPtr = AsnIntegerParse(errindexPtr, &length, &type, &dummy)) == NULL) {
        return -1;
    }

    if ((data = AsnHeaderBuild(headerPtr, &headerLength, SNMP_MSG_RESPONSE, headerLength)) == NULL) {
        return -1;
    }
    length = snmp_length;
    type = (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
    if ((data = AsnIntegerBuild(errstatPtr, &length, type, &errstat)) != errindexPtr) {
        return -1;
    }
    if ((data = AsnIntegerBuild(errindexPtr, &length, type, &errindex)) != varListPtr) {
        return -1;
    }
    packet_end = snmp_out + snmp_length;

    return 0;
}

/*!
 * \brief Parse incoming and create outgoing packet.
 *
 * \param in_data  Pointer to the incoming packet.
 * \param in_len   Number of valid bytes in the incoming packet.
 * \param out_data Pointer to a buffer for the outgoing packet.
 * \param out_len  Pointer to the variable that receives the number of
 *                 bytes in the outgoing packet.
 * \param out_len  Pointer to a variable which contains the size of the
 *                 output buffer on entry. On exit, it is returned 
 *                 as the number of valid bytes in the output buffer.
 *
 * \return 0 upon success and -1 upon failure.
 */
int SnmpAgentProcessRequest(SNMP_SESSION * sess, CONST u_char * in_data, size_t in_len, u_char * out_data, size_t * out_len)
{
    long zero = 0;
    u_char msgtype;
    u_char type;
    long reqid;
    long errstat;
    long errindex;
    long dummyindex;
    u_char *out_auth;
    u_char *out_header;
    u_char *out_reqid;
    CONST u_char *data;
    size_t len;

    SnmpStatsInc(SNMP_STAT_INPKTS);

    /* Get version and community from the packet header. */
    len = in_len;
    sess->sess_id_len = sizeof(sess->sess_id) - 1;
    if ((data = SnmpAuthParse(in_data, &len, sess->sess_id, &sess->sess_id_len, &sess->sess_version)) == NULL) {
        SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
        return -1;
    }

    /* Check authentication. */
    if (sess->sess_version == SNMP_VERSION_1 || sess->sess_version == SNMP_VERSION_2C) {
        if (SnmpCommunityFind((char *) sess->sess_id, &sess->sess_read_view, &sess->sess_write_view)) {
            /* TODO: Create SNMPv2 report. */
            SnmpStatsInc(SNMP_STAT_INBADCOMMUNITYNAMES);
            return -1;
        }
    } else {
        /* Unsupported SNMP version. */
        SnmpStatsInc(SNMP_STAT_INBADVERSIONS);
        return -1;
    }

    /* Parse request header and check type. */
    if ((data = AsnHeaderParse(data, &len, &msgtype)) == NULL) {
        SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
        return -1;
    }
    if (msgtype == SNMP_MSG_GETBULK) {
        /* SNMPv2 bulk requests are not yet supported. */
        return -1;
    } else if (msgtype != SNMP_MSG_GET && msgtype != SNMP_MSG_GETNEXT && msgtype != SNMP_MSG_SET) {
        /* Bad request type. */
        return -1;
    }

    /* Parse request ID. */
    if ((data = AsnIntegerParse(data, &len, &type, &reqid)) == NULL) {
        SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
        return -1;
    }
    /* Parse error status. */
    if ((data = AsnIntegerParse(data, &len, &type, &errstat)) == NULL) {
        SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
        return -1;
    }
    /* Parse error index. */
    if ((data = AsnIntegerParse(data, &len, &type, &errindex)) == NULL) {
        SnmpStatsInc(SNMP_STAT_INASNPARSEERRS);
        return -1;
    }

    /*
     * Now start cobbling together what is known about the output packet. 
     * The final lengths are not known now, so they will have to be 
     * recomputed later.
     */
    out_auth = out_data;
    if ((out_header = SnmpAuthBuild(sess, out_auth, out_len, 0)) == NULL) {
        return -1;
    }
    if ((out_reqid = AsnSequenceBuild(out_header, out_len, SNMP_MSG_RESPONSE, 0)) == NULL) {
        return -1;
    }
    /* Return identical request ID. */
    type = (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER);
    if ((out_data = AsnIntegerBuild(out_reqid, out_len, type, &reqid)) == NULL) {
        return -1;
    }
    /* Assume that error status will be zero. */
    if ((out_data = AsnIntegerBuild(out_data, out_len, type, &zero)) == NULL) {
        return -1;
    }
    /* Assume that error index will be zero. */
    if ((out_data = AsnIntegerBuild(out_data, out_len, type, &zero)) == NULL) {
        return -1;
    }

    /*
     * Walk through the list of variables and retrieve each one, 
     * placing its value in the output packet.
     *
     * TODO: Handle bulk requests.
     */
    errstat = SnmpVarListParse(sess, data, len, out_data, *out_len, &errindex, msgtype, SNMP_ACT_RESERVE1);

    /*
     * Sets require 3 to 4 passes through the var_op_list. The first two 
     * passes verify that all types, lengths, and values are valid and 
     * may reserve resources and the third does the set and a fourth 
     * executes any actions. Then the identical GET RESPONSE packet is
     * returned.
     *
     * If either of the first two passes returns an error, another pass 
     * is made so that any reserved resources can be freed.
     */
    if (msgtype == SNMP_MSG_SET) {
        if (errstat == 0) {
            errstat = SnmpVarListParse(sess, data, len, out_data, *out_len, &errindex, msgtype, SNMP_ACT_RESERVE2);
        }
        if (errstat == 0) {
            errstat = SnmpVarListParse(sess, data, len, out_data, *out_len, &dummyindex, msgtype, SNMP_ACT_COMMIT);
            SnmpVarListParse(sess, data, len, out_data, *out_len, &dummyindex, msgtype, errstat ? SNMP_ACT_FREE : SNMP_ACT_ACTION);
            if (SnmpCreateIdentical(sess, in_data, out_auth, in_len, 0L, 0L)) {
                return -1;
            }
            *out_len = packet_end - out_auth;
            return 0;
        } else {
            SnmpVarListParse(sess, data, len, out_data, *out_len, &dummyindex, msgtype, SNMP_ACT_FREE);
        }
    }

    if (errstat) {
        /* Create an error response. */
        if (SnmpCreateIdentical(sess, in_data, out_auth, in_len, errstat, errindex)) {
            return -1;
        }
        *out_len = packet_end - out_auth;
        return 0;
    }

    /* 
     * Re-encode the headers with the real lengths.
     */
    *out_len = packet_end - out_header;
    out_data = AsnSequenceBuild(out_header, out_len, SNMP_MSG_RESPONSE, packet_end - out_reqid);
    if (out_data != out_reqid) {
        return -1;
    }
    *out_len = packet_end - out_auth;
    out_data = SnmpAuthBuild(sess, out_auth, out_len, packet_end - out_header);
    *out_len = packet_end - out_auth;

    return 0;
}

/*!
 * \brief Run SNMP agent.
 *
 * Normally runs in an endless loop, which is only left in case of an error.
 *
 * \param sock UDP socket to use.
 *
 * \return Always -1.
 */
int SnmpAgent(UDPSOCKET * sock)
{
    int rc = -1;
    u_long raddr;
    u_short rport;
    size_t out_len;
    u_char *in_data = NutHeapAlloc(SNMP_MAX_LEN);
    u_char *out_data = NutHeapAlloc(SNMP_MAX_LEN);
    SNMP_SESSION *sess = NutHeapAlloc(sizeof(SNMP_SESSION));

    if (in_data && out_data && sess) {
        for (;;) {
            rc = NutUdpReceiveFrom(sock, &raddr, &rport, in_data, SNMP_MAX_LEN, 0);
            out_len = SNMP_MAX_LEN;
            memset(sess, 0, sizeof(SNMP_SESSION));
            if (SnmpAgentProcessRequest(sess, in_data, (size_t) rc, out_data, &out_len) == 0) {
                if (NutUdpSendTo(sock, raddr, rport, out_data, out_len) == 0) {
                    SnmpStatsInc(SNMP_STAT_OUTPKTS);
                }
            }
        }
    } else {
        rc = -1;
    }
    if (in_data) {
        NutHeapFree(in_data);
    }
    if (out_data) {
        NutHeapFree(out_data);
    }
    if (sess) {
        NutHeapFree(sess);
    }
    return rc;
}

#endif

snmp_agent.h                        。。。。。。。。。。。。。。。。。。。。。。。。。。。。

#ifndef SNMP_AGENT_H

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

#define SNMP_AGENT_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             *
 *                                                                            *
 ******************************************************************************/

#include "..\net\socket.h"

#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                  *
 *                                                                            *
 ******************************************************************************/

typedef struct {
    long sess_version;
    size_t sess_id_len;
    u_char sess_id[MAX_SID_LEN + 1];
    int sess_read_view;
    int sess_write_view;
} SNMP_SESSION;

/******************************************************************************
 *                                                                            *
 *    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 SnmpAgent(UDPSOCKET *);

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

#endif

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值