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 <pro/httpd.h>
*
* char *name;
* char *value;
* int i;
* int n = NutHttpGetParameterCount(req);
*
* for (i = 0; i < 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