extended functionality/make more secure M.Z.
This commit is contained in:
318
conman.c
318
conman.c
@ -29,6 +29,11 @@
|
|||||||
appending outcode to text,
|
appending outcode to text,
|
||||||
Mark Koennecke, July 2004
|
Mark Koennecke, July 2004
|
||||||
|
|
||||||
|
Made use of unused connections secure (connections are not
|
||||||
|
freed, but reused on new connections).
|
||||||
|
Introduced new type SCStore and functions SCSave, SCLoad.
|
||||||
|
Introduced SCPrintf to avoid many of these pBueffel.
|
||||||
|
Markus Zolliker, Sept 2004.
|
||||||
|
|
||||||
Copyright: see copyright.h
|
Copyright: see copyright.h
|
||||||
-----------------------------------------------------------------------------*/
|
-----------------------------------------------------------------------------*/
|
||||||
@ -41,6 +46,8 @@
|
|||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include <tcl.h>
|
#include <tcl.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include "lld.h"
|
#include "lld.h"
|
||||||
#include "sics.h"
|
#include "sics.h"
|
||||||
#include "passwd.h"
|
#include "passwd.h"
|
||||||
@ -78,173 +85,143 @@ extern pServer pServ;
|
|||||||
/*------------- a number for generating automatic names --------------------*/
|
/*------------- a number for generating automatic names --------------------*/
|
||||||
static int iName = 0;
|
static int iName = 0;
|
||||||
static int SCNormalWrite(SConnection *self, char *buffer, int iOut);
|
static int SCNormalWrite(SConnection *self, char *buffer, int iOut);
|
||||||
|
static SConnection *freeConnections = NULL;
|
||||||
|
static long lastIdent = 0;
|
||||||
/*===========================================================================*/
|
/*===========================================================================*/
|
||||||
SConnection *SCreateConnection(SicsInterp *pSics,mkChannel *pSock, int iUser)
|
static char *ConName(long ident) {
|
||||||
|
static char name[32];
|
||||||
|
snprintf(name, sizeof(name), "CON%ld", ident);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
static void FreeConnection(SConnection *pCon)
|
||||||
|
{
|
||||||
|
memset(pCon,0,sizeof(SConnection));
|
||||||
|
pCon->ident = 0;
|
||||||
|
pCon->next = freeConnections;
|
||||||
|
freeConnections = pCon;
|
||||||
|
}
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
static SConnection *CreateConnection(SicsInterp *pSics)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
SConnection *pRes = NULL;
|
||||||
|
char pBueffel[253];
|
||||||
|
char pHost[132];
|
||||||
|
|
||||||
|
if (!freeConnections)
|
||||||
|
{ /* no more free connection: get one by malloc */
|
||||||
|
pRes = (SConnection *)malloc(sizeof(SConnection));
|
||||||
|
if(!pRes)
|
||||||
|
{
|
||||||
|
/* This is a serious, very serious error! */
|
||||||
|
SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(pRes,0,sizeof(SConnection));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ /* reuse an old connection */
|
||||||
|
pRes = freeConnections;
|
||||||
|
freeConnections = pRes->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{ /* loop until an unused ident is found */
|
||||||
|
if (lastIdent == LONG_MAX)
|
||||||
|
{
|
||||||
|
/* This is a serious, very serious error! */
|
||||||
|
SICSLogWrite("ERROR: Run out of connection identifiers!!",eInternal);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lastIdent++;
|
||||||
|
} while (FindCommand(pSics, ConName(lastIdent)));
|
||||||
|
pRes->ident = lastIdent;
|
||||||
|
|
||||||
|
/* a descriptor */
|
||||||
|
pRes->pDes = CreateDescriptor("Connection");
|
||||||
|
if(!pRes->pDes)
|
||||||
|
{
|
||||||
|
/* This is a serious, very serious error! */
|
||||||
|
SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
|
||||||
|
FreeConnection(pRes);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the callback registry */
|
||||||
|
pRes->iList = LLDcreate(sizeof(Item));
|
||||||
|
|
||||||
|
/* the command stack */
|
||||||
|
pRes->pStack = CreateCommandStack();
|
||||||
|
if( (pRes->iList <0) || (!pRes->pStack))
|
||||||
|
{
|
||||||
|
/* This is a serious, very serious error! */
|
||||||
|
SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
|
||||||
|
DeleteDescriptor(pRes->pDes);
|
||||||
|
FreeConnection(pRes);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pRes->iOutput = eInError; /* gets everything except internal messages */
|
||||||
|
pRes->iFiles = 0; /* default: no logfiles */
|
||||||
|
pRes->inUse = 0;
|
||||||
|
pRes->iMacro = 0;
|
||||||
|
pRes->iTelnet = 0;
|
||||||
|
pRes->pSics = pSics;
|
||||||
|
pRes->eInterrupt = eContinue;
|
||||||
|
pRes->lMagic = CONMAGIC;
|
||||||
|
pRes->iLogin = 0;
|
||||||
|
pRes->conStart = time(NULL);
|
||||||
|
pRes->write = SCNormalWrite;
|
||||||
|
for(i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
pRes->pFiles[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* install command */
|
||||||
|
AddCommand(pRes->pSics, ConName(pRes->ident), ConSicsAction, NULL,pRes);
|
||||||
|
return pRes;
|
||||||
|
|
||||||
|
}
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
SConnection *SCreateConnection(SicsInterp *pSics,mkChannel *pSock, int iUser)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
SConnection *pRes = NULL;
|
SConnection *pRes = NULL;
|
||||||
char pBueffel[253];
|
char pBueffel[253];
|
||||||
char pHost[132];
|
char pHost[132];
|
||||||
|
|
||||||
pRes = (SConnection *)malloc(sizeof(SConnection));
|
assert(pSock);
|
||||||
if(!pRes)
|
|
||||||
{
|
|
||||||
/* This is a serious, very serious error! */
|
|
||||||
SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(pRes,0,sizeof(SConnection));
|
|
||||||
|
|
||||||
/* a descriptor */
|
pRes = CreateConnection(pSics);
|
||||||
pRes->pDes = CreateDescriptor("Connection");
|
|
||||||
if(!pRes->pDes)
|
|
||||||
{
|
|
||||||
/* This is a serious, very serious error! */
|
|
||||||
SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
|
|
||||||
free(pRes);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* new name */
|
|
||||||
sprintf(pBueffel,"CON%4.4d",iName);
|
|
||||||
iName++;
|
|
||||||
if(iName > 9999)
|
|
||||||
{
|
|
||||||
iName = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the callback registry */
|
|
||||||
pRes->iList = LLDcreate(sizeof(Item));
|
|
||||||
|
|
||||||
/* the command stack */
|
|
||||||
pRes->pStack = CreateCommandStack();
|
|
||||||
if( (pRes->iList <0) || (!pRes->pStack))
|
|
||||||
{
|
|
||||||
/* This is a serious, very serious error! */
|
|
||||||
SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
|
|
||||||
DeleteDescriptor(pRes->pDes);
|
|
||||||
free(pRes);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
SetCommandStackMaxSize(pRes->pStack,MAXSTACK);
|
SetCommandStackMaxSize(pRes->pStack,MAXSTACK);
|
||||||
|
|
||||||
pRes->pName = strdup(pBueffel);
|
|
||||||
pRes->pSock = pSock;
|
pRes->pSock = pSock;
|
||||||
pRes->iUserRights = iUser;
|
pRes->iUserRights = iUser;
|
||||||
pRes->iOutput = eInError; /* gets everything except internal messages */
|
pRes->iGrab = TokenGrabActive();
|
||||||
pRes->iFiles = 0; /* default: no logfiles */
|
|
||||||
pRes->inUse = 0;
|
NETInfo(pRes->pSock,pHost,131);
|
||||||
pRes->iMacro = 0;
|
sprintf(pBueffel,"Accepted connection on socket %d from %s",
|
||||||
pRes->iGrab = TokenGrabActive();
|
pRes->pSock->sockid, pHost);
|
||||||
pRes->iTelnet = 0;
|
SICSLogWrite(pBueffel,eInternal);
|
||||||
pRes->pSics = pSics;
|
WriteToCommandLog("SYS >", pBueffel);
|
||||||
pRes->eInterrupt = eContinue;
|
|
||||||
pRes->lMagic = CONMAGIC;
|
|
||||||
pRes->iLogin = 0;
|
|
||||||
pRes->conStart = time(NULL);
|
|
||||||
pRes->write = SCNormalWrite;
|
|
||||||
for(i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
pRes->pFiles[i] = NULL;
|
|
||||||
}
|
|
||||||
if(pRes->pSock)
|
|
||||||
{
|
|
||||||
NETInfo(pRes->pSock,pHost,131);
|
|
||||||
sprintf(pBueffel,"Accepted connection on socket %d from %s",
|
|
||||||
pRes->pSock->sockid, pHost);
|
|
||||||
SICSLogWrite(pBueffel,eInternal);
|
|
||||||
WriteToCommandLog("SYS >", pBueffel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SICSLogWrite("Accepted dummy connection ",eInternal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* install command */
|
|
||||||
AddCommand(pRes->pSics, pRes->pName, ConSicsAction, NULL,pRes);
|
|
||||||
return pRes;
|
return pRes;
|
||||||
|
|
||||||
}
|
}
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
SConnection *SCCreateDummyConnection(SicsInterp *pSics)
|
SConnection *SCCreateDummyConnection(SicsInterp *pSics)
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
SConnection *pRes = NULL;
|
SConnection *pRes = NULL;
|
||||||
char pBueffel[132];
|
|
||||||
|
|
||||||
pRes = (SConnection *)malloc(sizeof(SConnection));
|
pRes = CreateConnection(pSics);
|
||||||
if(!pRes)
|
|
||||||
{
|
|
||||||
/* This is a serious, very serious error! */
|
|
||||||
SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(pRes,0,sizeof(SConnection));
|
|
||||||
|
|
||||||
/* a descriptor */
|
|
||||||
pRes->pDes = CreateDescriptor("Connection");
|
|
||||||
if(!pRes->pDes)
|
|
||||||
{
|
|
||||||
/* This is a serious, very serious error! */
|
|
||||||
SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
|
|
||||||
free(pRes);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* new name */
|
|
||||||
sprintf(pBueffel,"CON%4.4d",iName);
|
|
||||||
iName++;
|
|
||||||
if(iName > 9999)
|
|
||||||
{
|
|
||||||
iName = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* the callback registry */
|
|
||||||
pRes->iList = LLDcreate(sizeof(Item));
|
|
||||||
|
|
||||||
/* the command stack */
|
|
||||||
pRes->pStack = CreateCommandStack();
|
|
||||||
if( (pRes->iList <0) || (!pRes->pStack))
|
|
||||||
{
|
|
||||||
/* This is a serious, very serious error! */
|
|
||||||
SICSLogWrite("ERROR: No memory to allocate connection!!",eInternal);
|
|
||||||
DeleteDescriptor(pRes->pDes);
|
|
||||||
free(pRes);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
pRes->pName = strdup(pBueffel);
|
|
||||||
pRes->pSock = NULL;
|
pRes->pSock = NULL;
|
||||||
pRes->iUserRights = usInternal;
|
pRes->iUserRights = usInternal;
|
||||||
pRes->iOutput = eInError; /* gets everything except internal messages */
|
|
||||||
pRes->iFiles = 0; /* default: no logfiles */
|
|
||||||
pRes->inUse = 0;
|
|
||||||
pRes->iTelnet = 0;
|
|
||||||
pRes->iGrab = 0;
|
pRes->iGrab = 0;
|
||||||
pRes->iMacro = 0;
|
|
||||||
pRes->pSics = pSics;
|
|
||||||
pRes->lMagic = CONMAGIC;
|
|
||||||
pRes->eInterrupt = eContinue;
|
|
||||||
pRes->iLogin = 0;
|
|
||||||
pRes->conStart = time(NULL);
|
|
||||||
pRes->write = SCNormalWrite;
|
|
||||||
for(i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
pRes->pFiles[i] = NULL;
|
|
||||||
}
|
|
||||||
if(pRes->pSock)
|
|
||||||
{
|
|
||||||
sprintf(pBueffel,"Accepted connection on socket %d",pRes->pSock->sockid);
|
|
||||||
SICSLogWrite(pBueffel,eInternal);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SICSLogWrite("Accepted dummy connection ",eInternal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* install command */
|
SICSLogWrite("Accepted dummy connection ",eInternal);
|
||||||
AddCommand(pRes->pSics, pRes->pName, ConSicsAction, NULL,pRes);
|
|
||||||
return pRes;
|
return pRes;
|
||||||
}
|
}
|
||||||
/*--------------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------------*/
|
||||||
@ -434,11 +411,7 @@ extern pServer pServ;
|
|||||||
fclose(pVictim->pFiles[i]);
|
fclose(pVictim->pFiles[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pVictim->pName)
|
RemoveCommand(pVictim->pSics,ConName(pVictim->ident));
|
||||||
{
|
|
||||||
RemoveCommand(pVictim->pSics,pVictim->pName);
|
|
||||||
free(pVictim->pName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(pVictim->pDes)
|
if(pVictim->pDes)
|
||||||
{
|
{
|
||||||
@ -468,9 +441,9 @@ extern pServer pServ;
|
|||||||
{
|
{
|
||||||
DeleteCommandStack(pVictim->pStack);
|
DeleteCommandStack(pVictim->pStack);
|
||||||
}
|
}
|
||||||
pVictim->lMagic=0; /* make a write to a freed conn. harmless */
|
pVictim->lMagic=0; /* make a write to a freed connection harmless */
|
||||||
/* finally free pVictim*/
|
/* finally free pVictim*/
|
||||||
free(pVictim);
|
FreeConnection(pVictim);
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
static int HasNL(char *buffer)
|
static int HasNL(char *buffer)
|
||||||
@ -563,6 +536,32 @@ extern pServer pServ;
|
|||||||
}
|
}
|
||||||
return self->write(self,pBuffer,iOut);
|
return self->write(self,pBuffer,iOut);
|
||||||
}
|
}
|
||||||
|
/*-------------------------------------------------------------------------*/
|
||||||
|
int SCPrintf(SConnection *self, int iOut, char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
char buf[256];
|
||||||
|
char *dyn;
|
||||||
|
int l;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
l = vsnprintf(buf, sizeof buf, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
if (l >= sizeof buf) {
|
||||||
|
/* we have probably a C99 conforming snprintf and need a larger buffer */
|
||||||
|
dyn = malloc(l+1);
|
||||||
|
if (dyn != NULL) {
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsnprintf(dyn, l+1, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
res = SCWrite(self, dyn, iOut);
|
||||||
|
free(dyn);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SCWrite(self, buf, iOut);
|
||||||
|
}
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
writeFunc SCGetWriteFunc(SConnection *self)
|
writeFunc SCGetWriteFunc(SConnection *self)
|
||||||
{
|
{
|
||||||
@ -597,7 +596,7 @@ static int doSockWrite(SConnection *self, char *buffer)
|
|||||||
iRet = NETWrite(self->pSock,buffer,strlen(buffer));
|
iRet = NETWrite(self->pSock,buffer,strlen(buffer));
|
||||||
if(!HasNL(buffer))
|
if(!HasNL(buffer))
|
||||||
{
|
{
|
||||||
iRet = NETWrite(self->pSock,"\n",sizeof("\n"));
|
iRet = NETWrite(self->pSock,"\n",strlen("\n"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!iRet)
|
if(!iRet)
|
||||||
@ -1396,7 +1395,7 @@ static void writeToLogFiles(SConnection *self, char *buffer)
|
|||||||
}
|
}
|
||||||
else if(strcmp(argv[1],"myname") == 0)
|
else if(strcmp(argv[1],"myname") == 0)
|
||||||
{
|
{
|
||||||
sprintf(pBueffel,"MyName = %s",pCon->pName);
|
sprintf(pBueffel,"MyName = %s",ConName(pCon->ident));
|
||||||
SCWrite(pCon,pBueffel,eValue);
|
SCWrite(pCon,pBueffel,eValue);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1904,3 +1903,22 @@ int SCActive(SConnection *self)
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
void SCSave(SCStore *con, SConnection *pCon) {
|
||||||
|
con->pCon = pCon;
|
||||||
|
if (pCon) {
|
||||||
|
con->ident = pCon->ident;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*--------------------------------------------------------------------------*/
|
||||||
|
SConnection *SCLoad(SCStore *con) {
|
||||||
|
SConnection *pCon;
|
||||||
|
|
||||||
|
pCon = con->pCon;
|
||||||
|
if (pCon) {
|
||||||
|
if (con->ident != pCon->ident) {
|
||||||
|
con->pCon == NULL; /* connection has died */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pCon;
|
||||||
|
}
|
||||||
|
33
conman.h
33
conman.h
@ -30,8 +30,10 @@ typedef int (*writeFunc)(struct __SConnection *pCon,
|
|||||||
typedef struct __SConnection {
|
typedef struct __SConnection {
|
||||||
/* object basics */
|
/* object basics */
|
||||||
pObjectDescriptor pDes;
|
pObjectDescriptor pDes;
|
||||||
char *pName;
|
/* char *pName; now generated on the fly from ident */
|
||||||
long lMagic;
|
long lMagic;
|
||||||
|
long ident;
|
||||||
|
struct __SConnection *next;
|
||||||
|
|
||||||
/* I/O control */
|
/* I/O control */
|
||||||
mkChannel *pSock;
|
mkChannel *pSock;
|
||||||
@ -62,15 +64,16 @@ typedef int (*writeFunc)(struct __SConnection *pCon,
|
|||||||
/* callback registry */
|
/* callback registry */
|
||||||
int iList;
|
int iList;
|
||||||
|
|
||||||
/* Tasking Stuff */
|
/* Tasking Stuff */
|
||||||
int iEnd;
|
int iEnd;
|
||||||
/* for keeping track of the login
|
/* for keeping track of the login
|
||||||
process on a non telnet connection.
|
process on a non telnet connection.
|
||||||
Should only be used in SCTaskFunction
|
Should only be used in SCTaskFunction
|
||||||
*/
|
*/
|
||||||
int iLogin;
|
int iLogin;
|
||||||
time_t conStart;
|
time_t conStart;
|
||||||
}SConnection;
|
|
||||||
|
} SConnection;
|
||||||
|
|
||||||
#include "nserver.h"
|
#include "nserver.h"
|
||||||
|
|
||||||
@ -88,6 +91,7 @@ typedef int (*writeFunc)(struct __SConnection *pCon,
|
|||||||
int SCDelLogFile(SConnection *pCon, int iFile);
|
int SCDelLogFile(SConnection *pCon, int iFile);
|
||||||
void SCSetOutputClass(SConnection *self, int iClass);
|
void SCSetOutputClass(SConnection *self, int iClass);
|
||||||
int SCWrite(SConnection *self, char *pBuffer, int iOut);
|
int SCWrite(SConnection *self, char *pBuffer, int iOut);
|
||||||
|
int SCPrintf(SConnection *self, int iOut, char *fmt, ...);
|
||||||
int SCRead(SConnection *self, char *pBuffer, int iBufLen);
|
int SCRead(SConnection *self, char *pBuffer, int iBufLen);
|
||||||
int SCPrompt(SConnection *pCon, char *pPrompt, char *pResult, int iLen);
|
int SCPrompt(SConnection *pCon, char *pPrompt, char *pResult, int iLen);
|
||||||
int SCSendOK(SConnection *self);
|
int SCSendOK(SConnection *self);
|
||||||
@ -140,7 +144,16 @@ typedef int (*writeFunc)(struct __SConnection *pCon,
|
|||||||
int argc, char *argv[]);
|
int argc, char *argv[]);
|
||||||
int ConSicsAction(SConnection *pCon, SicsInterp *pSics, void *pData,
|
int ConSicsAction(SConnection *pCon, SicsInterp *pSics, void *pData,
|
||||||
int argc, char *argv[]);
|
int argc, char *argv[]);
|
||||||
|
/******************************** Store ************************************/
|
||||||
|
typedef struct {
|
||||||
|
SConnection *pCon;
|
||||||
|
long ident;
|
||||||
|
} SCStore;
|
||||||
|
|
||||||
|
void SCSave(SCStore *con, SConnection *pCon);
|
||||||
|
/* save a connection for later use. */
|
||||||
|
|
||||||
|
SConnection *SCLoad(SCStore *con);
|
||||||
|
/* check con and return SConnection if still valid or NULL otherwise. */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user