Commit 52a0ce82 authored by Benoit Steiner's avatar Benoit Steiner

Ajout d'un serveur telnet qui permet d'administrer le bridge par telnet
(les fonctions d'administration restent a ecrite)

Factorisation des fonctions de gestion du reseau dans network.c

Corrections de bugs mineurs et ameliorations du code en tout genres.

Benny
parent 0f826d98
......@@ -16,7 +16,7 @@ CFLAGS += -DDEBUG
CFLAGS += -g
# Profile with gprof. Don't strip the executable file !
CFLAGS += -pg
#CFLAGS += -pg
# Compiler parameters style
CFLAGS += -Wall
......@@ -32,7 +32,7 @@ CFLAGS += -D_REENTRANT
LIB += -lpthread
# Use electric fence to detect illegal memory access
#LIB += -lefence
LIB += -lefence
# Use dmalloc to detect memory leaks
#LIB += -ldmalloc
......@@ -49,6 +49,8 @@ OBJ = \
listener.o \
sender.o \
performer.o \
admin.o \
command.o \
log.o \
fifo.o \
base.o \
......@@ -59,6 +61,7 @@ OBJ = \
arp.o \
connect.o \
cfgfile.o \
network.o \
convert.o
SRC := $(OBJ:%.o=%.c)
......
......@@ -11,7 +11,7 @@ BEGIN Interfaces # Config du performer
VLANcount = 16 # Nombre de VLANs
VLANdefault = 2 # VLAN par defaut
VLAN2 = eth0 # Mapping VLAN / If
VLAN3 = eth1
# VLAN3 = eth1
# VLAN4 = eth2
# VLAN15 = eth2
# VLAN16 = eth3
......
/* VideoLAN VLANbridge: Remote administration interface
*
* Benoit Steiner, ECP, <benny@via.ecp.fr>
*
* TO DO: a real telnet server
*/
/* ATTENTION: Il faudra absolument passer par le manager pour les requetes
parce que la base est pas lockee */
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "debug.h"
#include "log.h"
#include "network.h"
#include "admin.h"
#include "command.h"
#define MOD_NAME MOD_ADMIN
#define CMDLINE_MAX_SIZE 200 /* 200 chars max */
#define CMDLINE_TIMEOUT 600 /* 10 minutes */
/***************************************************************************/
/* Definition of the commands used by the parser */
/* Warning: Usage string must end by a ' ' */
/***************************************************************************/
struct s_Command ascCommand[] =
{
{
"help", "", Help,
"Display a list of available commands or help on a particular command",
"[cmd]",
"[string] " },
{
"essai", "", Essai,
"Essai a 2 balles du parseur",
"<toto> [tutu] [-opt {ta mere}]",
"<string> [int] [-opt {ip}] " },
{
"logout", "", Logout,
"Exit the current session",
"",
" " },
{
"", "", NULL, "", "", "" }
};
/***************************************************************************/
/* Convert the value of an arg stored as a string into the given type */
/* We assume the initial string is pointed out by the uVal.strValue member */
/***************************************************************************/
int ConvertArg(struct s_Argument* psaArg, int iType)
{
int iRc = 0;
switch(iType)
{
case ARG_TYPE_INT:
{
psaArg->iArgType = ARG_TYPE_INT;
if (strspn(psaArg->uVal.strValue, "0123456789") !=
strlen(psaArg->uVal.strValue))
{
iRc = CMD_BAD;
}
else
{
psaArg->uVal.iValue = atoi(psaArg->uVal.strValue);
// printf("Type = int, val = %d\n", psaArg->uVal.iValue);
}
break;
}
case ARG_TYPE_STRING:
{
psaArg->iArgType = ARG_TYPE_STRING;
/* We need to allocate a string to definitly store the value in the struct */
psaArg->uVal.strValue = strdup(psaArg->uVal.strValue);
// printf("Type = string, val = %s, addr = %p\n", psaArg->uVal.strValue, psaArg->uVal.strValue);
break;
}
case ARG_TYPE_IP:
{
psaArg->iArgType = ARG_TYPE_IP;
iRc = SetHost(&psaArg->uVal.saValue, psaArg->uVal.strValue);
// printf("Type = ip, val = %s\n", inet_ntoa(psaArg->uVal.saValue.sin_addr));
if (iRc)
{
iRc = CMD_BAD;
}
break;
}
default:
{
Log(LOG_ERROR, MOD_NAME, "Unknown or unhandled type: %d", iType);
iRc = CMD_BAD;
}
}
return iRc;
}
/***************************************************************************/
/* Extract the token standing between any of the chars of strStart and of */
/* strEnd */
/***************************************************************************/
char* Extract(const char* strSrc, const char* strStart, const char* strEnd)
{
char* strToken = NULL;
int iStartOffset = 0;
int iTokenSize = 0;
ASSERT(strSrc);
/* Look for the start char */
// printf("Extract -> Debut: %d\n", strcspn(strSrc, strStart));
iStartOffset = strcspn(strSrc, strStart);
if (iStartOffset >= strlen(strSrc))
{
/* Not found or found at the last position */
return NULL;
}
/* Look for the end char */
// printf("Extract -> Size: %d\n", strcspn(strSrc+iStartOffset+1, strEnd));
iTokenSize = strcspn(strSrc+iStartOffset+1, strEnd);
if (iTokenSize > strlen(strSrc+iStartOffset+1))
{
/* Not found */
return NULL;
}
/* Copy the part of the string that is between the two delimiters */
strToken = malloc(iTokenSize+1);
strncpy(strToken, strSrc+iStartOffset+1, iTokenSize);
strToken[iTokenSize] = '\0';
// printf("Extract -> Token: %s\n", strToken);
return strToken;
}
/***************************************************************************/
/* Lex a string to extract the arguments it contains and return their */
/* number */
/* Beware: he initial string is modified */
/***************************************************************************/
int LexArgs(int iSockFd, char* strArgs, struct s_Argument** pasaArgs)
{
int iArgc = 0;
int iPos = 0;
ASSERT(strArgs);
ASSERT(pasaArgs);
// printf("Lexing: <BEG>%s<END>\n", strArgs);
/* Skip the begining ' ' chars if any */
strArgs = strArgs + strspn(strArgs, " ");
/* Test if the string is not empty for strtok don't do it */
if (!(strcmp(strArgs, "") > 0))
{
// printf("No Arg");
return 0;
}
/* Isolate the first argument in the string to initialise the following loop */
strArgs = strtok(strArgs, " ");
/* Build a table of s_Argument based on the arguments contained in the
given string */
while(strArgs)
{
/* Extend the table of s_Argument to store the one we extracted */
iArgc++;
*pasaArgs = realloc(*pasaArgs, iArgc*sizeof(struct s_Argument));
/* Read the current argument */
// printf("Current Arg: %s\n", strArgs);
if (strArgs[0] == '-')
{
/* It's an optional argument that must be followed by a value,
get it too and store it temporaly as a string */
// printf("Option name: %s\n", &strArgs[1]);
(*pasaArgs)[iArgc-1].strName = strdup(&strArgs[1]);
(*pasaArgs)[iArgc-1].iArgType = ARG_NAMED;
if (strtok(NULL, " "))
{
iPos = strlen(strArgs)+1;
strArgs += iPos;
if (strArgs[0] == '-')
{
SendResult(iSockFd, "Missing parameter for option %s\n\r",
strArgs - iPos);
iArgc = CMD_BAD;
break;
}
else
{
// printf("Arg for option %s is: %s\n", strArgs - iPos, strArgs);
/* Don't store the argument value for now now but just a pointer
to the place where its value stands in the string of arguments */
(*pasaArgs)[iArgc-1].uVal.strValue = strArgs;
}
}
else
{
SendResult(iSockFd, "Missing parameter for option %s\n\r",
strArgs - iPos);
iArgc = CMD_BAD;
break;
}
}
else
{
/* It's a standalone argument (mandatory or optional one) */
(*pasaArgs)[iArgc-1].uVal.strValue = strArgs;
(*pasaArgs)[iArgc-1].iArgType = ARG_STANDALONE;
// printf("Standalone arg value: %s\n", (*pasaArgs)[iArgc-1].uVal.strValue);
}
/* Bypass the last argument and isolate the next one */
// printf("Next strtok\n");
strArgs = strtok(NULL, " ");
}
return iArgc;
}
/***************************************************************************/
/* */
/***************************************************************************/
int ParseCmd(int iSockFd, const char* strCmd, struct s_Command** ppscCommand,
struct s_Argument** pasaArgs)
{
int iArgc;
int iIndex;
char strCopy[CMDLINE_MAX_SIZE];
int iPos;
int iFlag;
char* strArg = NULL;
char* strType = NULL;
ASSERT(strCmd);
/* Initialisation */
*ppscCommand = NULL;
iArgc = 0;
*pasaArgs = NULL;
strncpy(strCopy, strCmd, CMDLINE_MAX_SIZE);
/* Delete the final FF, LF and CR if any */
iPos = strcspn(strCopy, "\f\n\r");
strCopy[iPos] = '\0';
/* Skip the begining ' ' chars if any */
iPos = strspn(strCopy, " ");
strArg = &strCopy[iPos];
// printf("Command: <BEG>%s<END>\n", strArg);
/* Jump empty commands */
if (!strcmp(strArg, ""))
{
// printf("Empty command\n");
return CMD_NONE;
}
/* Try to find the command name in our table of commands */
iIndex = 0;
iPos = strcspn(strArg, " ");
while (strcmp(ascCommand[iIndex].strName, "") > 0)
{
if(!strncmp(ascCommand[iIndex].strName, strArg, iPos))
break;
else
iIndex++;
}
if (iIndex >= sizeof(ascCommand)/sizeof(*ascCommand)-1)
{
/* The command is unknown */
SendResult(iSockFd, "Unknown command: %s\n\r", strArg);
return CMD_BAD;
}
else
{
// printf("Cmd found at index: %d\n", iIndex);
*ppscCommand = &ascCommand[iIndex];
}
/* Lex the command line and build a list of arguments */
iArgc = LexArgs(iSockFd, strArg+iPos, pasaArgs);
/* Check that the standalone arguments are all at the beginning of the command
line (Unless there was no argument (iArgc == 0) or an error (iArgc < 0)) */
iFlag = 0;
for (iIndex = 0; iIndex < iArgc; iIndex++)
{
if ((*pasaArgs)[iIndex].iArgType == ARG_STANDALONE && iFlag)
{
/* An optional argument was reached before */
SendResult(iSockFd, "Misplaced %s argument\n\r",
(*pasaArgs)[iIndex].uVal.strValue);
iArgc = -1;
}
else if ((*pasaArgs)[iIndex].iArgType == ARG_NAMED)
iFlag = 1;
}
/* Now compare the arguments we got with the command definition and convert
them to the right format (Unless there was no argument (iArgc == 0) or
an error (iArgc < 0)) */
strArg = (*ppscCommand)->strUsage;
for (iIndex = 0; iIndex < iArgc; iIndex++)
{
/* Find the token describing the type of the argument in the usage string,
and store it in strType */
if ((*pasaArgs)[iIndex].iArgType == ARG_STANDALONE)
{
/* Try to find a mandatory arg */
strType = Extract(strArg, "<", ">");
if (!strType)
{
/* Try to find an optional one */
strType = Extract(strArg, "[", "]");
if (!strType)
{
SendResult(iSockFd, "Error: invalid extra parameter %s\n\n",
(*pasaArgs)[iIndex].uVal.strValue);
iArgc = CMD_BAD;
break;
}
else
{
/* Check wether we didn't extract the definition of an option [-opt {val}] */
if (strType[0] == '-')
{
SendResult(iSockFd, "Error: invalid extra parameter %s\n\r",
(*pasaArgs)[iIndex].uVal.strValue);
iArgc = CMD_BAD;
break;
}
}
}
/* Position strArg on the next arg of the strUsage string (We won't be
annoyed by the named arg because they are after the standalone ones) */
strArg = strArg+strlen(strType) + 3;
}
else
{
/* This should be a named argument: try to find it in our list of
possible arguments */
strArg = strstr((*ppscCommand)->strUsage, (*pasaArgs)[iIndex].strName);
// printf("Argument found in list of argument at %p", strArg);
if (!strArg)
{
SendResult(iSockFd, "Unknown option: %s\n\r", (*pasaArgs)[iIndex].strName);
iArgc = CMD_BAD;
break;
}
else
{
/* Get the type of the arg in the Usage string */
strType = Extract(strArg, "{", "}");
}
}
/* Now we have the beginning and the end of the part of the usage string
that describe the type of the argument: convert the arg */
if (!strcmp(strType, "int"))
{
if (ConvertArg(&(*pasaArgs)[iIndex], ARG_TYPE_INT))
{
iArgc = CMD_BAD;
SendResult(iSockFd, "Error: %s is not a valid integer\n\r",
(*pasaArgs)[iIndex].uVal.strValue);
}
}
else if (!strcmp(strType, "string"))
{
if (ConvertArg(&(*pasaArgs)[iIndex], ARG_TYPE_STRING))
{
iArgc = CMD_BAD;
SendResult(iSockFd, "Error: %s is not a valid string\n\r",
(*pasaArgs)[iIndex].uVal.strValue);
}
}
else if (!strcmp(strType, "ip"))
{
if (ConvertArg(&(*pasaArgs)[iIndex], ARG_TYPE_IP))
{
iArgc = CMD_BAD;
SendResult(iSockFd, "Error: %s is not a valid ip address\n\r",
(*pasaArgs)[iIndex].uVal.strValue);
}
}
else
{
SendResult(iSockFd, "Bad type for %s\n", (*pasaArgs)[iIndex].uVal.strValue);
iArgc = CMD_BAD;
}
/* Free memory allocated by the Extract function */
free(strType);
}
return iArgc;
}
/***************************************************************************/
/* */
/***************************************************************************/
int ExecCommand(int iSockFd, const char* strCmd)
{
struct s_Command* pscCommand;
int iArgc;
struct s_Argument* asaArgv;
int iRc = 0;
ASSERT(strCmd);
#ifdef DEBUG
Log(LOG_NOTE, MOD_NAME, "Received cmd: %s", strCmd);
#endif
/* Extract the command name and its options */
iArgc = ParseCmd(iSockFd, strCmd, &pscCommand, &asaArgv);
/* Stop here if the command was empty or not found */
if(iArgc < 0)
{
#ifdef DEBUG
Log(LOG_WARN, MOD_NAME, "Bad command received: %s", strCmd);
#endif
}
else
{
/* Execute command */
Log(LOG_NOTE, MOD_NAME, "Executing cmd: %s", strCmd);
ASSERT(pscCommand);
iRc = pscCommand->handler(iSockFd, iArgc, asaArgv);
/* Handle return code */
/* TO DO */
}
/* Free the memory used to store the options of the cmd */
/* TO DO */
return iRc;
}
/***************************************************************************/
/* */
/***************************************************************************/
#define admin ((struct s_Admin *)arg)
void* AdminThread(void* arg)
{
int iRc = 0;
char strBuff[CMDLINE_MAX_SIZE];
ASSERT(arg);
#ifdef DEBUG
Log (LOG_NOTE, MOD_NAME, "Admin thread is running");
#endif
/* Welcome message */
iRc = send(admin->iSockFd, "\n\r\n\rWelcome to VLANbridge\n\rType help for a list of available commands\n\r\n\r", 74, 0);
if (iRc < 0)
{
Log(LOG_ERROR, MOD_NAME, "Could not send walcome message: %s, aborting connection",
strerror(errno));
iRc = CMD_CLOSE;
}
/* Command loop */
while (iRc != CMD_CLOSE)
{
/* Send prompt */
iRc = send(admin->iSockFd, "VLANbridge> ", 13, 0);
if (iRc != 13)
{
if (iRc < 0)
{
Log(LOG_ERROR, MOD_NAME, "Could not send prompt: %s, aborting connection",
strerror(errno));
break;
}
else
Log(LOG_ERROR, MOD_NAME, "Prompt not completely sent, trying to continue");
}
/* Receive command */
iRc = RecvCmd(admin->iSockFd, strBuff, CMDLINE_MAX_SIZE-1, CMDLINE_TIMEOUT);
if (iRc <= 0)
{
/* Stop here this command */
Log(LOG_WARN, MOD_NAME, "Cannot receive commands anymore: aborting session\n");
break;
}
else
{
/* Add a NULL char at the end of the buffer to be sure to be able to deal
with it as with any string */
strBuff[iRc] = '\0';
}
/* Exec command */
iRc = ExecCommand(admin->iSockFd, strBuff);
}
/* Exit */
/* Getpeername(admin->iSockFd) */
send(admin->iSockFd, "\n\rClosing connection...\n\r\n\r", 28, 0);
Log(LOG_NOTE, MOD_NAME, "Closing connection with: %s", "toto");
close(admin->iSockFd);
pthread_exit(NULL);
}
/* VideoLAN VLANbridge: Remote administration
*
* Definition file
*
* You need to include <pthread.h> before including this file
*/
struct s_Admin
{
/* thread id */
pthread_t tId;
/* Handle the sockfd used to communicate with the administrator */
int iSockFd;
};
/* Parameter storage */
union u_Value
{
char* strValue;
int iValue;
float fValue;
struct sockaddr_in saValue;
};
/* Command argument */
struct s_Argument
{
char* strName;
int iArgType;
union u_Value uVal;
};
/* Argument types */
#define ARG_STANDALONE 128 /* Standalone arg, of form <Val> or [Val] */
#define ARG_NAMED 0 /* Optional named arg, of form [-strName {Val}] */
/* Type of the value stored in this arg */
#define ARG_TYPE_NONE 0 /* Option has no associated value */
#define ARG_TYPE_STRING 1 /* Option must be followed by a string */
#define ARG_TYPE_INT 2 /* Option must be followed by an integer */
#define ARG_TYPE_FLOAT 4 /* Option must be followed by a float */
#define ARG_TYPE_IP 8 /* Option must be followed by an IP address */
/* Command definition */
struct s_Command
{
/* Command definition */
char* strName;
char* strArgDescr;
/* Execution */
int (*handler)(int iSockFd, int iArgNumber, struct s_Argument* asaArg);
/* Help information */
char* strSummary;
char* strOptions;
char* strUsage;
};
/* Commands return code */
#define CMD_CLOSE -2
#define CMD_TIMEOUT -3
#define CMD_NONE -4
#define CMD_BAD -5
void* AdminThread(void* arg);
......@@ -99,7 +99,8 @@ int DestroyBase(struct s_Base* psbBase)
/***************************************************************************/
/* Add an element at the end of the list (before the 'last' element) *//***************************************************************************/
/* Add an element at the end of the list (before the 'last' element) */
/***************************************************************************/
int AddToBase(struct s_Base* psbBase, struct s_Record* psrNewMember)
{
int iRc = 0;
......
......@@ -187,7 +187,7 @@ int CloseCfg (FILE* pfdCfgFile)
ASSERT(pfdCfgFile);
if (fclose(pfdCfgFile) == EOF)
if (fclose(pfdCfgFile) != 0)
{
Log (LOG_ERROR, MOD_NAME, "Unable to close the %s config file: %s",
"XXX", strerror (errno));
......
/* VideoLAN VLANbridge: Remote administration commands
*
* Benoit Steiner, ECP, <benny@via.ecp.fr>
*
* TO DO: everything
*
* Note: the commands must be declared in the ascCommand variable of
* the admin.c file in order to let the perser know about them
*/
/* ATTENTION: Il faudra absolument passer par le manager pour les requetes
parce que la base est pas lockee */
#include <pthread.h>
#include <stdio.h>
#include <netinet/in.h>
#include "debug.h"
#include "network.h"
#include "admin.h"
extern struct s_Command ascCommand[];
/***************************************************************************/
/* */
/***************************************************************************/
int Logout(int iSockFd, int i, struct s_Argument* asaArg)
{
return CMD_CLOSE;
}
/***************************************************************************/
/* */
/***************************************************************************/
int Help(int iSockFd, int iArgCount, struct s_Argument* asaArg)
{
int iIndex = 0;
// printf("In help function: %d\n", iArgCount);
if (iArgCount == 0)
{
/* Display a list of available command */
while (strcmp(ascCommand[iIndex].strName, "") > 0)
{
SendResult(iSockFd, "%s: %s\n\r", ascCommand[iIndex].strName,
ascCommand[iIndex].strSummary);
iIndex++;
}
}
else
{