/*
* Tcp4u v 3.31 Created may 93 - Last Revision 20/10/1997 3.20
*
*===========================================================================
*
* Project: Tcp4u, Library for tcp protocol
* File: tcp4u.c
* Purpose: main functions for tcp management
*
*===========================================================================
*
* This software is Copyright (c) 1996-1998 by Philippe Jounin
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*
* If you make modifications to this software that you feel
* increases it usefulness for the rest of the community, please
* email the changes, enhancements, bug fixes as well as any and
* all ideas to me. This software is going to be maintained and
* enhanced as deemed necessary by the community.
*
*
* Philippe Jounin (ph.jounin@computer.org)
*/
#include "build.h"
#define TCP4U_SENDTIMEOUT 3600l /* one hour */
/* ******************************************************************* */
/* */
/* Partie I : constructeurs /Destructeurs */
/* */
/* */
/* Tcp4uInit, Tcp4uCleanup */
/* */
/* */
/* ******************************************************************* */
/* ------------------------------------------------------------------ */
/* Initialisation */
/* ------------------------------------------------------------------ */
int API4U Tcp4uInit (void)
{
int Rc=0;
Tcp4uLog (LOG4U_PROC, "Tcp4uInit");
Skt4uInit ();
# ifdef _WINDOWS
{
/* enregistrement aupres de Winsock.Dll */
WSADATA WSAData;
Tcp4uLog (LOG4U_CALL, "WSAStartup, Winsock 1.1 required");
Rc = WSAStartup (MAKEWORD (1,1), & WSAData);
}
# endif
Tcp4uLog (LOG4U_EXIT, "Tcp4uInit");
return Rc==0 ? TCP4U_SUCCESS : TCP4U_ERROR;
} /* Tcp4uInit */
/* ------------------------------------------------------------------ */
/* Destructeurs */
/* ------------------------------------------------------------------ */
int API4U Tcp4uCleanup (void)
{
int Rc;
Tcp4uLog (LOG4U_PROC, "Tcp4uCleanup");
Rc = Skt4uCleanup ();
Tcp4uLog (LOG4U_EXIT, "Tcp4uCleanup");
return Rc;
} /* Tcp4wCleanup */
/* ------------------------------------------------------------------ */
/* WINDOWS : Fin de vie de la DLL */
/* ------------------------------------------------------------------ */
#ifdef TCP4W_DLL
int API4U WEP (int nExitType)
{
Tcp4uLog (LOG4U_PROC, "WEP");
Skt4uCleanup ();
nExitType=0; /* suppress warning */
Tcp4uLog (LOG4U_EXIT, "WEP");
return 1; /* definition Windows */
} /* WEP */
#endif
/* ------------------------------------------------------------------ */
/* Aliases : Tcp4uxInit/Tcp4uxCleanup ; Tcp4wInit/Tcp4wCleanup */
/* ------------------------------------------------------------------ */
#ifdef _WINDOWS
int API4U Tcp4wInit (void) { return Tcp4uInit (); }
int API4U Tcp4wCleanup (void) { return Tcp4uCleanup (); }
#endif
#ifdef UNIX
int API4U Tcp4uxInit (void) { return Tcp4uInit (); }
int API4U Tcp4uxCleanup (void) { return Tcp4uCleanup (); }
#endif
/* ******************************************************************* */
/* */
/* Partie I : Fonctions de bas niveau TCP */
/* */
/* */
/* TcpAbort */
/* TcpAccept */
/* TcpConnect */
/* TcpClose */
/* TcpFlush */
/* TcpGetListenSocket */
/* TcpRecv */
/* TcpSend */
/* */
/* ******************************************************************* */
/* ------------------------------------------------------------ */
/* TcpAbort: Stoppe un appel bloquant */
/* Retourne TCP4U_SUCCESS */
/* ------------------------------------------------------------ */
int API4U TcpAbort (void)
{
Tcp4uLog (LOG4U_PROC, "TcpAbort");
if (WSAIsBlocking ())
{
Tcp4uLog (LOG4U_CALL, "WSACancelBlockingCall");
WSACancelBlockingCall ();
}
else Tcp4uLog (LOG4U_ERROR, "TcpAbort: No calling process to cancel");
Tcp4uLog (LOG4U_EXIT, "TcpAbort");
return TCP4U_SUCCESS;
} /* TcpAbort */
/* ------------------------------------------------------------ */
/* TcpAccept : itablissement d'une connexion TCP avec Timeout */
/* Retourne TCP4U_ERROR, TCP4U_TIMEOUT, TCP4U_SUCCESS */
/* ou TCP4U_BUFFERFREED si pConnectSock n'existe plus */
/* En cas de succes la variable pConnectSock est */
/* renseignee. */
/* ------------------------------------------------------------ */
int API4U TcpAccept (SOCKET far *pCSock, SOCKET ListenSock, UINT nTO)
{
struct timeval TO; /* Time Out structure */
fd_set ReadMask; /* select mask */
SOCKET ConSock;
struct sockaddr_in saSockAddr; /* specifications pour le Accept */
struct linger sling = { TRUE, 5 }; /* 5-seconds timeout */
int nAddrLen = sizeof saSockAddr;
int Rc;
Tcp4uLog (LOG4U_PROC, "TcpAccept. Listen socket %d, Timeout %d", ListenSock, nTO);
/* prepare select */
FD_ZERO (& ReadMask); /* mise a zero du masque */
FD_SET (ListenSock, & ReadMask); /* Attente d'evenement en lecture */
TO.tv_sec = (long) nTO; /* secondes */
TO.tv_usec = 0; /* microsecondes */
/* s+1 normally unused but better for a lot of bugged TCP Stacks */
Tcp4uLog (LOG4U_CALL, "select on socket %d", ListenSock);
Rc = select (1+ListenSock, & ReadMask, NULL, NULL, nTO==0 ? NULL : & TO);
if (Rc<0)
{
Tcp4uLog (LOG4U_ERROR, "select on socket %d", ListenSock);
return IsCancelled() ? TCP4U_CANCELLED : TCP4U_ERROR; /* erreur reseau */
}
if (Rc==0)
{
Tcp4uLog (LOG4U_ERROR, "select on socket %d: Timeout", ListenSock);
return TCP4U_TIMEOUT;
}
Tcp4uLog (LOG4U_CALL, "accept on listen socket %d", ListenSock);
ConSock = accept (ListenSock, (struct sockaddr far *) &saSockAddr, &nAddrLen);
if (ConSock==INVALID_SOCKET)
{
Tcp4uLog (LOG4U_ERROR, "accept on socket %d", ListenSock);
return IsCancelled() ? TCP4U_CANCELLED : TCP4U_ERROR;
}
Skt4uRcd (ConSock, STATE_SERVER);
Tcp4uLog (LOG4U_CALL, "setsockopt SOL_LINGER on socket %d", ConSock);
setsockopt (ConSock, SOL_SOCKET, SO_LINGER,(LPSTR) &sling, sizeof sling);
/* validite des pointeurs */
if (IsBadWritePtr (pCSock, sizeof *pCSock))
{
TcpClose (& ConSock);
Tcp4uLog (LOG4U_ERROR, "TcpAccept. invalid pointer");
return TCP4U_BUFFERFREED;
}
else
{
*pCSock = ConSock;
Tcp4uLog (LOG4U_EXIT, "TcpAccept");
return TCP4U_SUCCESS;
}
} /* TcpAccept */
/* ------------------------------------------------------------ */
/* TcpClose - Fermeture d'une socket */
/* La socket pointie par pS est fermie, *pS est */
/* mise ` INVALID_SOCKET. */
/* - Retourne 1 en cas de succhs, -1 sinon (en giniral */
/* du ` l'erreur WSAEINTR) */
/* constantes (TCP4U_SUCCESS et TCP4U_ERROR) */
/* ------------------------------------------------------------ */
int API4U TcpClose (SOCKET far *pS)
{
SOCKET s;
struct timeval TO; /* Time Out structure */
struct S_HistoSocket *pStat; /* stats about socket */
Tcp4uLog (LOG4U_PROC, "TcpClose socket %d", *pS);
if (*pS==(unsigned) INVALID_SOCKET) return TCP4U_SUCCESS; /* bien passe */
s = *pS;
pStat = Skt4uGetStats (s);
if (pStat!=NULL && pStat->nState==STATE_LISTEN)
{
/* Ici une petite tempo pour les winsockets qui n'apprecient */
/* pas la succession trop rapide Accept/Close */
TO.tv_sec = 0;
TO.tv_usec = 100000l; /* microsecondes */
select (s+1, NULL, NULL, NULL, & TO);
} /* pause */
/* Si on effectue un shutdown (s, 2), certaines winsocket */
/* n'envoie plus la trame de longueur 0 caracteristique */
/* de la fin de connexion. */
/* On le remplace donc par le TcpFlush */
if (pStat!=NULL && pStat->nState!=STATE_LISTEN) TcpFlush (s);
Tcp4uLog (LOG4U_CALL, "closesocket socket %d", s);
if (CloseSocket (s)==0)
{
Skt4uUnRcd (s);
if (!IsBadWritePtr(pS, sizeof *pS)) *pS =(unsigned) INVALID_SOCKET;
else Tcp4uLog (LOG4U_ERROR, "TcpClose: Invalid pointer");
Tcp4uLog (LOG4U_EXIT, "TcpClose");
return TCP4U_SUCCESS;
}
Tcp4uLog (LOG4U_ERROR, "closesocket socket %d", s);
return TCP4U_ERROR;
} /* TcpClose */
/* --------------------------------------------------------------------- */
/* TcpConnect : Tentative d'etablissemnt d'une connexion TCP */
/* Retourne TCP4U_ERROR, TCP4U_SUCCESS, TCP4U_HOSTUNKNONW */
/* --------------------------------------------------------------------- */
int API4U TcpConnect (SOCKET far *pS, LPCSTR szHost,
LPCSTR szService, unsigned short far *lpPort)
{
int Rc;
struct sockaddr_in saSockAddr;
struct servent far * lpServEnt;
SOCKET connect_skt;
unsigned short Zero = 0;
Tcp4uLog (LOG4U_PROC, "TcpConnect. Host %s, service %s, port %u",
szHost,
szService==NULL ? "none" : szService,
lpPort==NULL ? -1 : *lpPort);
*pS = INVALID_SOCKET; /* par defaut erreur */
if (lpPort==NULL) lpPort = & Zero; /* evite de tester sans arret */
/* --- 1er champ de saSockAddr : Port */
if (szService==NULL) lpServEnt = NULL ;
else
{
Tcp4uLog (LOG4U_DBCALL, "getservbyname %s/tcp", szService);
lpServEnt = getservbyname (szService, "tcp") ;
}
saSockAddr.sin_port = lpServEnt!=NULL ? lpServEnt->s_port : htons(*lpPort);
if (saSockAddr.sin_port == 0) return TCP4U_BADPORT; /* erreur dans port */
/* --- 2eme champ de saSockAddr : Addresse serveur */
saSockAddr.sin_addr = Tcp4uGetIPAddr (szHost);
if (saSockAddr.sin_addr.s_addr==INADDR_NONE) return TCP4U_HOSTUNKNOWN;
/* --- Dernier champ : liaison connectie */
saSockAddr.sin_family = AF_INET; /* on utilise le mode connecte TCP */
/* --- creation de la socket */
Tcp4uLog (LOG4U_CALL, "socket PF_INET, SOCK_STREAM");
if ( (connect_skt = socket (PF_INET, SOCK_STREAM, 0))==SOCKET_ERROR)
{
Tcp4uLog (LOG4U_ERROR, "socket");
return TCP4U_NOMORESOCKET;
}
/* --- connect retourne INVALID_SOCKET ou numero valide */
Tcp4uLog (LOG4U_CALL, "connect on host %s", inet_ntoa (saSockAddr.sin_addr));
Rc = connect (connect_skt,(struct sockaddr far *) & saSockAddr, sizeof saSockAddr);
/* --- enregistrement dans notre table */
if (Rc==SOCKET_ERROR)
{
Tcp4uLog (LOG4U_ERROR, "connect");
CloseSocket (connect_skt); /* release buffer */
}
else Skt4uRcd (connect_skt, STATE_CLIENT);
*lpPort = htons (saSockAddr.sin_port);
if (IsBadWritePtr (pS, sizeof *pS))
{
Tcp4uLog (LOG4U_ERROR, "TcpConnect: invalid pointer");
return TCP4U_BUFFERFREED;
}
else *pS = Rc==SOCKET_ERROR ? INVALID_SOCKET : connect_skt;
Tcp4uLog (LOG4U_EXIT, "TcpConnect");
return Rc==SOCKET_ERROR ? TCP4U_CONNECTFAILED : TCP4U_SUCCESS;
} /* TcpConnect */
/* ------------------------------------------------------------ */
/* TcpFlush: Vide le buffer relatif a une socket */
/* Retourne TCP4U_ERROR, TCP4U_SUCCESS, TCP4U_CLOSEDSOCKET */
/* ------------------------------------------------------------ */
int API4U TcpFlush (SOCKET s)
{
int nBR=-1;
char szBuf[64];
Tcp4uLog (LOG4U_PROC, "TcpFlush on socket %d", s);
if (s==INVALID_SOCKET) return TCP4U_ERROR;
while ( TcpIsDataAvail (s) && (nBR=recv (s,szBuf,sizeof szBuf,0)) > 0 )
Tcp4uDump (szBuf, nBR, "Flushed");
while ( TcpIsOOBDataAvail (s) &&(nBR=recv (s,szBuf,sizeof szBuf,MSG_OOB))>0 )
Tcp4uDump (szBuf, nBR, "Flushed");
Tcp4uLog (LOG4U_EXIT, "TcpFlush");
if (nBR<0) return IsCancelled() ? TCP4U_CANCELLED : TCP4U_ERROR;
if (nBR==0) return TCP4U_SOCKETCLOSED;
return TCP4U_SUCCESS;
} /* TcpFlush */
/* ------------------------------------------------------------ */
/* TcpGetListenSocket */
/* Retourne TCP4U_ERROR, TCP4U_SUCCESS */
/* ------------------------------------------------------------ */
int API4U TcpGetListenSocket (SOCKET far *pS, LPCSTR szService,
unsigned short far *lpPort, int nPendingConnection)
{
struct sockaddr_in saSockAddr; /* specifications pour le Accept */
int nAddrLen;
SOCKET ListenSock;
int One=1;
int Rc;
unsigned short Zero = 0;
struct servent far * lpServEnt;
Tcp4uLog (LOG4U_PROC, "TcpGetListenSocket on service %s, port %u",
szService==NULL ? "none" : szService,
lpPort==NULL ? 0 : *lpPort);
*pS = INVALID_SOCKET;
if (lpPort==NULL) lpPort = & Zero;
/* --- 1er champ de saSockAddr : Port */
if (szService==NULL) lpServEnt = NULL ;
else
{
Tcp4uLog (LOG4U_DBCALL, "getservbyname %s/tcp", szService);
lpServEnt = getservbyname (szService, "tcp") ;
}
saSockAddr.sin_port = (lpServEnt!=NULL) ? lpServEnt->s_port : htons(*lpPort);
#ifdef CONTROL_EN_TROP
if (saSockAddr.sin_port==0) return TCP4U_BADPORT; /* erreur attribution Port */
#endif
/* create socket */
Tcp4uLog (LOG4U_CALL, "socket AF_INET, SOCK_STREAM");
ListenSock = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListenSock==INVALID_SOCKET)
{
Tcp4uLog (LOG4U_ERROR, "socket");
return TCP4U_NOMORESOCKET;
}
saSockAddr.sin_family = AF_INET;
saSockAddr.sin_addr.s_addr=INADDR_ANY;
/* set options ReuseAddress and Linger */
Tcp4uLog (LOG4U_CALL, "setsockopt SO_REUSEADDR on sock %d", ListenSock);
setsockopt (ListenSock, SOL_SOCKET, SO_REUSEADDR, (char far *)&One, sizeof One);
Tcp4uLog (LOG4U_CALL, "setsockopt SO_LINGER on sock %d", ListenSock);
setsockopt (ListenSock, SOL_SOCKET, SO_LINGER,(char far *)&One, sizeof One);
/* Bind name to socket */
Tcp4uLog (LOG4U_CALL, "bind socket %d to %s", ListenSock, inet_ntoa (saSockAddr.sin_addr));
Rc = bind (ListenSock,(struct sockaddr far *) & saSockAddr, sizeof(struct sockaddr));
if (Rc != SOCKET_ERROR)
{
Tcp4uLog (LOG4U_CALL, "listen on socket %d. %d pendind connections", ListenSock, nPendingConnection);
Rc = listen (ListenSock, nPendingConnection);
}
if (Rc==SOCKET_ERROR)
{
Tcp4uLog (LOG4U_ERROR, "bind or listen on socket %d", WSAGetLastError (), ListenSock);
CloseSocket (ListenSock);
return TCP4U_BINDERROR;
}
/* Get port id with getsockname */
nAddrLen = sizeof saSockAddr;
memset (& saSockAddr, 0, sizeof(struct sockaddr) );
Tcp4uLog (LOG4U_DBCALL, "getsockname on socket %d", ListenSock);
getsockname (ListenSock, (struct sockaddr far *) &saSockAddr, &nAddrLen);
*lpPort = htons (saSockAddr.sin_port);
Skt4uRcd (ListenSock, STATE_LISTEN);
if (IsBadWritePtr (pS, sizeof *pS))
{
Tcp4uLog (LOG4U_ERROR, "TcpGetListenSocket: invalid pointer");
return TCP4U_BUFFERFREED;
}
else *pS = ListenSock;
Tcp4uLog (LOG4U_EXIT, "TcpGetListenSocket");
return TCP4U_SUCCESS;
} /* TcpGetListenSock */
/* ------------------------------------------------------------ */
/* TcpRecv - Lecture d'une trame avec Timeout */
/* TcpRecv recoit uBufSize octets de donnies */
/* la fonction s'arrjte avant si la socket est fermie */
/* ou si une timeout arrive. */
/* - Retourne le nombre d'octets lus, */
/* TCP4U_SOCKETCLOSED si la socket a iti fermie */
/* TCP4U_ERROR sur une erreur de lecture */
/* TCP4U_TIMEOUT sur un TO */
/* TCP4U_BUFFERFREED si libiration des buffers */
/* - Remarque : Pour iviter que le buffer appelant soit */
/* libere de manihre asynchrone, la fonction utilise */
/* ses propres buffers. */
/* - Ajout des modes TCP4U_WAITFOREVER et TCP4U_WAIT */
/* ------------------------------------------------------------ */
/* forme avec log */
int API4U TcpRecv (SOCKET s, LPSTR szBuf, unsigned uBufSize,
unsigned uTimeOut, HFILE hLogFile)
{
int Rc;
Tcp4uLog (LOG4U_PROC, "TcpRecv on socket %d. Timeout %d, bufsize %d", s, uTimeOut, uBufSize);
Rc = InternalTcpRecv (s, szBuf, uBufSize, uTimeOut, hLogFile);
if (Rc>=TCP4U_SUCCESS) Tcp4uDump (szBuf, Rc, DUMP4U_RCVD);
Tcp4uLog (LOG4U_EXIT, "TcpRecv");
return Rc;
} /* TcpRecv */
/* forme sans log */
int InternalTcpRecv (SOCKET s, LPSTR szBuf, unsigned uBufSize,
unsigned uTimeOut, HFILE hLogFile)
{
char cBuf;
LPSTR p = NULL;
int Rc, nUpRc; /* Return Code of select and recv */
struct timeval TO; /* Time Out structure */
struct timeval *pTO; /* Time Out structure */
fd_set ReadMask; /* select mask */
if (s==INVALID_SOCKET) return TCP4U_ERROR;
FD_ZERO (& ReadMask); /* mise a zero du masque */
FD_SET (s, & ReadMask); /* Attente d'evenement en lecture */
/* detail des modes */
switch (uTimeOut)
{
case TCP4U_WAITFOREVER : pTO = NULL;
break;
case TCP4U_DONTWAIT : TO.tv_sec = TO.tv_usec=0 ;
pTO = & TO;
break;
/* Otherwise uTimeout is really the Timeout */
default : TO.tv_sec = (long) uTimeOut;
TO.tv_usec=0;
pTO = & TO;
break;
}
/* s+1 normally unused but better for a lot of bugged TCP Stacks */
Tcp4uLog (LOG4U_CALL, "select on socket %d", s);
Rc = select (s+1, & ReadMask, NULL, NULL, pTO);
if (Rc<0)
{
Tcp4uLog (LOG4U_ERROR, "select on socket %d", s);
return IsCancelled() ? TCP4U_CANCELLED : TCP4U_ERROR;
}
if (Rc==0)
{
Tcp4uLog (LOG4U_ERROR, "select on socket %d: Timeout", s);
return TCP4U_TIMEOUT; /* timeout en reception */
}
if (szBuf==NULL || uBufSize==0)
{
Tcp4uLog (LOG4U_EXIT, "TcpRecv");
return TCP4U_SUCCESS;
}
if (uBufSize==1) /* cas frequent : lecture caractere par caractere */
p = & cBuf;
else
{
p = Calloc (uBufSize, 1);
if (p==NULL) return TCP4U_INSMEMORY;
}
Tcp4uLog (LOG4U_CALL, "recv socket %d, buffer %d bytes", s, uBufSize);
Rc = recv (s, p, uBufSize, 0); /* chgt 11/01/95 */
switch (Rc)
{
case SOCKET_ERROR :
Tcp4uLog (LOG4U_ERROR, "recv socket %d", s);
nUpRc = IsCancelled() ? TCP4U_CANCELLED : TCP4U_ERROR ;
break;
case 0 :
Tcp4uLog (LOG4U_ERROR,"socket %d: Host has closed connection", s);
nUpRc = TCP4U_SOCKETCLOSED ;
break;
default :
if (hLogFile!=HFILE_ERROR) Write (hLogFile, p, Rc);
if (IsBadWritePtr (szBuf, 1)) nUpRc = TCP4U_BUFFERFREED;
else memcpy (szBuf, p, nUpRc=Rc);
break;
} /* translation des codes d'erreurs */
if (p!=NULL && uBufSize!=1) Free (p);
return nUpRc;
} /* TcpRecv */
/* ------------------------------------------------------------ */
/* TcpSend : Envoi de uBufSize octets vers le distant */
/* Retourne TCP4U_ERROR ou TCP4U_SUCCESS */
/* ------------------------------------------------------------ */
/* forme avec log */
int API4U TcpSend (SOCKET s, LPCSTR szBuf, unsigned uBufSize,
BOOL bHighPriority, HFILE hLogFile)
{
int Rc;
Tcp4uLog (LOG4U_PROC,"TcpSend on socket %d. %d bytes to be sent",s,uBufSize);
Rc = InternalTcpSend (s, szBuf, uBufSize, bHighPriority, hLogFile);
if (Rc==TCP4U_SUCCESS) Tcp4uDump (szBuf, uBufSize, DUMP4U_SENT);
Tcp4uLog (LOG4U_EXIT, "TcpSend");
return Rc;
} /* TcpSend */
/* forme sans log */
int InternalTcpSend (SOCKET s, LPCSTR szBuf, unsigned uBufSize,
BOOL bHighPriority, HFILE hLogFile)
{
int Rc;
unsigned Total;
LPSTR p;
if (s==INVALID_SOCKET) return TCP4U_ERROR;
p = Calloc (uBufSize, 1);
if (p==NULL) return TCP4U_INSMEMORY;
memcpy (p, szBuf, uBufSize);
if (hLogFile!=HFILE_ERROR) Write (hLogFile, p, uBufSize);
for ( Total = 0, Rc = 1 ; Total < uBufSize && Rc > 0 ; Total += Rc)
{
Tcp4uLog (LOG4U_CALL, "send on socket %d. %d bytes to be sent",
s, uBufSize-Total);
Rc = send (s, & p[Total], uBufSize-Total, bHighPriority ? MSG_OOB : 0);
if (Rc<=0) Tcp4uLog (LOG4U_ERROR, "send on socket %d.", s);
}
Free(p);
return Total>=uBufSize ? TCP4U_SUCCESS : IsCancelled() ? TCP4U_CANCELLED : TCP4U_ERROR;
} /* TcpSend */
/* ******************************************************************* */
/* */
/* Partie I (suite) : Fonctions d'information */
/* */
/* TcpGetLocalID */
/* TcpGetRemoteID */
/* TcpIsDataAvail */
/* */
/* ******************************************************************* */
/* ------------------------------------------------------------ */
/* TcpGetLocalID: Retourne nom et adresse IP de la station */
/* Retourne TCP4U_SUCCESS, TCP4U_ERROR */
/* ------------------------------------------------------------ */
int API4U TcpGetLocalID (LPSTR szStrName, int uNameSize, DWORD far *lpAddress)
{
char szName[128];
struct hostent far *lpHostEnt;
int Rc;
Tcp4uLog (LOG4U_PROC, "TcpGetLocalID");
if (lpAddress!=NULL) memset (lpAddress, 0, sizeof (DWORD));
/* on envisage 2 methodes pour obtenir le nom du PC */
/* d'abord un appel a gethostname */
Tcp4uLog (LOG4U_DBCALL, "gethostname");
if (gethostname (szName, sizeof szName)==SOCKET_ERROR) return TCP4U_ERROR;
if (lpAddress != NULL)
{
Tcp4uLog (LOG4U_DBCALL, "gethostbyname on host %s", szName);
lpHostEnt = gethostbyname (szName);
if (lpHostEnt==NULL) return TCP4U_ERROR;
memcpy (lpAddress, lpHostEnt->h_addr_list[0], lpHostEnt->h_length);
}
if (szStrName!=NULL)
{
Strcpyn (szStrName, szName, uNameSize);
Rc = Strlen(szName)>Strlen(szStrName) ? TCP4U_OVERFLOW : TCP4U_SUCCESS;
}
else Rc = TCP4U_SUCCESS;
Tcp4uLog (LOG4U_EXIT, "TcpGetLocalID");
return TCP4U_SUCCESS;
} /* TcpGetLocalID */
/* ------------------------------------------------------------ */
/* TcpGetRemoteID: Retourne nom et adresse IP du distant */
/* Retourne TCP4U_SUCCESS, TCP4U_ERROR */
/* ------------------------------------------------------------ */
int API4U TcpGetRemoteID (SOCKET s, LPSTR szStrName, int uNameSize,
DWORD far *lpAddress)
{
struct sockaddr_in SockAddr;
int SockAddrLen=sizeof SockAddr;
struct hostent far *lpHostEnt;
Tcp4uLog (LOG4U_PROC, "TcpGetRemoteID on socket %d", s);
/* determiner l'adress IP de la station distante */
Tcp4uLog (LOG4U_DBCALL, "getpeername on socket %d", s);
if (getpeername (s, (struct sockaddr far *) & SockAddr, & SockAddrLen)==SOCKET_ERROR)
{
Tcp4uLog (LOG4U_ERROR, "getpeername on socket %d", s);
return TCP4U_ERROR;
}
if (lpAddress!=NULL)
memcpy (lpAddress, & SockAddr.sin_addr.s_addr,
sizeof SockAddr.sin_addr.s_addr);
if (szStrName!=NULL && uNameSize > 0)
{
/* determiner par gethostbyaddr le nom de la station */
szStrName[0]=0; /* si erreur */
Tcp4uLog (LOG4U_DBCALL, "gethostbyaddr on host %s", inet_ntoa (SockAddr.sin_addr));
lpHostEnt = gethostbyaddr ((LPSTR) & SockAddr.sin_addr.s_addr, 4, PF_INET);
if (lpHostEnt!=NULL) Strcpyn (szStrName, lpHostEnt->h_name, uNameSize);
else Tcp4uLog (LOG4U_ERROR, "gethostbyaddr");
}
Tcp4uLog (LOG4U_EXIT, "TcpGetRemoteID");
return TCP4U_SUCCESS;
} /* TcpGetRemoteID */
/* ------------------------------------------------------------- */
/* TcpIsDataAvail: Retourne VRAI si des donnees sont disponibles */
/* ------------------------------------------------------------- */
BOOL API4U TcpIsDataAvail (SOCKET s)
{
unsigned long ulData;
int Rc;
Tcp4uLog (LOG4U_PROC, "TcpIsDataAvail on socket %d", s);
Tcp4uLog (LOG4U_CALL, "Ioctl FIONREAD on socket %d", s);
Rc = IoctlSocket (s, FIONREAD, & ulData);
Tcp4uLog (LOG4U_EXIT, "TcpIsDataAvail");
return Rc==0 && ulData>0;
} /* TcpIsDataAvail */
/* ------------------------------------------------------------- */
/* TcpIsOOBDataAvail: Retourne VRAI si des donnees Out Of Band */
/* sont disponibles */
/* ------------------------------------------------------------- */
BOOL API4U TcpIsOOBDataAvail (SOCKET s)
{
unsigned long ulData;
int Rc;
Tcp4uLog (LOG4U_PROC, "TcpIsOOBDataAvail on socket %d", s);
Rc = IoctlSocket (s, SIOCATMARK, & ulData);
Tcp4uLog (LOG4U_EXIT, "TcpIsOOBDataAvail");
return Rc==0 && ! ulData;
} /* TcpIsOOBDataAvail */
/* ******************************************************************* */
/* */
/* Partie II : Gestion d'un mini- protocole */
/* */
/* */
/* TcpPPSend - TcpPPRecv */
/* */
/* */
/* ******************************************************************* */
/* ------------------------------------------------------------ */
/* TcpPPRecv - Lecture d'une trame avec Timeout */
/* TcpPPRecv recoit uBufSize octets de donnies */
/* la fonction s'arrjte avant si la socket est fermie */
/* ou si un timeout arrive. */
/* - Retourne le nombre d'octets lus, */
/* TCP4U_SOCKETCLOSED si la socket a iti fermie */
/* TCP4U_ERROR sur une erreur de lecture */
/* TCP4U_TIMEOUT sur un TO */
/* TCP4U_BUFFERFREED si libiration des buffers */
/* - Remarque : Pour iviter que le buffer appelant soit */
/* libiri de manihre asynchrone, la fonction utilise */
/* ses propres buffers. */
/* ------------------------------------------------------------ */
int API4U TcpPPRecv (SOCKET s, LPSTR szBuf, unsigned uBufSize,
unsigned uTimeOut, BOOL bExact, HFILE hLogFile)
{
LPSTR p, q;
int Rc, nUpRc; /* Return Code of select and recv */
unsigned short nToBeReceived, nReceived;
Tcp4uLog (LOG4U_PROC, "TcpPPRecv on socket %d. Timeout %d, buffer %d bytes", s, uTimeOut, uBufSize);
if (s==INVALID_SOCKET) return TCP4U_ERROR;
Rc = TcpRecv (s, (LPSTR) & nToBeReceived, 2, uTimeOut, hLogFile);
if (Rc<TCP4U_SUCCESS) return Rc;
/* remise dans l'ordre PC */
nToBeReceived = ntohs (nToBeReceived);
/* Si longueur connue a l'avance, sinon ne pas depasser le buffer donne */
if (bExact && nToBeReceived!=uBufSize) return TCP4U_UNMATCHEDLENGTH;
if (uBufSize < nToBeReceived) return TCP4U_OVERFLOW;
if (nToBeReceived==0) return TCP4U_EMPTYBUFFER;
/* On elimine une des causes de crash: on lit dans un buffer qui n'existe plus */
q = p = Calloc (nToBeReceived, 1);
if (p==NULL) return TCP4U_INSMEMORY;
nReceived = 0;
nUpRc=0;
do /* On boucle tant qu'on a pas eu les nToBeReceived octets */
{
nUpRc = TcpRecv (s, q, nToBeReceived - nReceived, uTimeOut/4 + 1, hLogFile);
nReceived += (short) nUpRc;
q += nUpRc;
}
while (nUpRc>0 && nReceived<nToBeReceived);
/* Analyse du code de retour */
if (nUpRc >= TCP4U_SUCCESS)
{
if (IsBadWritePtr (szBuf, nToBeReceived)) nUpRc = TCP4U_BUFFERFREED;
else memcpy (szBuf, p, nUpRc=nToBeReceived);
}
Free (p);
Tcp4uLog (LOG4U_EXIT, "TcpPPRecv");
return nUpRc;
} /* TcpPPRecv */
/* -------------------------------------------------------------- */
/* TcpPPSend : Envoi de uBufSize octets vers le distant */
/* Retourne TCP4U_ERROR ou TCP4U_SUCCESS */
/* -------------------------------------------------------------- */
int API4U TcpPPSend (SOCKET s, LPCSTR szBuf, unsigned uBufSize, HFILE hLogFile)
{
int Rc;
unsigned short Total;
Tcp4uLog (LOG4U_PROC, "TcpPPSend on socket %d. %d bytes to send", s, uBufSize);
if (s==INVALID_SOCKET) return TCP4U_ERROR;
/* Envoi de la longueur de la trame sur 2 octets */
Total = htons ((unsigned short) uBufSize);
Rc = TcpSend (s, (void far *) & Total, 2, FALSE, hLogFile);
/* cas d'une sequence vide -> envoi de 0 OK: retour OK */
if (Rc>=TCP4U_SUCCESS && uBufSize>0)
/* maintenant on envoie la sequence (qui n'est pas vide) */
Rc = TcpSend (s, szBuf, uBufSize, FALSE, hLogFile);
Tcp4uLog (LOG4U_EXIT, "TcpPPSend");
return Rc;
} /* TcpPPSend */
/* ******************************************************************* */
/* */
/* Partie III : Lecture Jusqu'a ... */
/* */
/* */
/* ******************************************************************* */
/* ------------------------------------------------------------ */
/* TcpRecvUntilStr */
/* Recois des donnees jusqu'a */
/* - Une erreur */
/* - Une Fermeture de socket */
/* - un timeout */
/* - la reception de la chaine szStop */
/* - le remplissage du buffer */
/* Le Retour soit un code d'erreur, soit TCP4U_SUCCESS */
/* Si c'est TCP4U_OVERFLOW, uBufSize octes ont iti lus, mais */
/* la chaine szStop n'a pas iti trouvie */
/* Si c'est TCP4U_SOCKETCLOSED, le distant a ferme la connexion */
/* La chaine szStop n'est pas ajoutie au buffer */
/* lpBufSize contient le nombre d'octets lus */
/* NOTE: Cette fonction est incompatible avec La pile de Novell */
/* LAN Workplace */
/* ------------------------------------------------------------ */
/* redeclaration de tolower: MSCV2 semble avoir des problemes avec */
static char ToLower (char c)
{
return (c>='A' && c<='Z') ? c + ('a' -'A') : c;
} /* ToLower */
/* compare les chaines s1 et s2 sur nLength caracteres */
/* retourne TRUE si les deux chaines sont identiques */
static BOOL uMemcmp (LPSTR s1, LPSTR s2, int nLength, BOOL bCaseSensitive)
{
int Evan;
if (bCaseSensitive)
return memcmp (s1, s2, nLength)==0;
else
{
for (Evan=0 ; Evan<nLength && ToLower(s1[Evan])==ToLower(s2[Evan]) ; Evan++);
return Evan==nLength;
}
} /* uMemcmp */
/* Sous procedure : Recherche de la sous chaine s2 dans s1 */
/* Renvoie l'index de s1 auquel on peut trouver s2 */
/* ou -1 si s2 n'est pas incluse dans s1, dans */
/* ce cas, *nProx est le nombre d'octets de */
/* s2 contenu ` la fin de s1 */
static int FindPos (LPSTR s1, int ns1, LPSTR s2, int ns2, int *npProx, BOOL bCaseSensitive)
{
int Ark;
BOOL bNotFound;
if (*npProx!=0) /* on compare aussi si l'inclusion ne continue pas */
{
if (ns1<ns2-*npProx && uMemcmp (s1, & s2[*npProx], ns1, bCaseSensitive))
{ *npProx+=ns1 ; return -1; }
if (ns1>=ns2-*npProx && uMemcmp (s1, & s2[*npProx], ns2-*npProx, bCaseSensitive))
{ return 0; }
} /* traitement des antecedents */
for ( Ark=0, bNotFound=TRUE;
Ark<=ns1-ns2 && (bNotFound= ! uMemcmp (& s1[Ark], s2, ns2, bCaseSensitive)) ;
Ark++) ; /* recherche de s2 */
if (bNotFound)
{
for ( *npProx = min (ns1, ns2-1) ;
*npProx>0 && !uMemcmp (& s1[ns1-*npProx], s2, *npProx, bCaseSensitive);
(*npProx)-- );
}
return bNotFound ? -1 : Ark; /* si bNotFounf, *npProx est positionne */
} /* FindPos */
int API4U TcpRecvUntilStr (SOCKET s, LPSTR szBuf,unsigned far *lpBufSize,
LPSTR szStop, unsigned uStopSize, BOOL bCaseSensitive,
unsigned uTimeOut, HFILE hLogFile)
{
int Rc;
Tcp4uLog ( LOG4U_PROC,
"TcpRecvUntilStr on socket %d. Timeout %d, buffer %d bytes",
s, uTimeOut, *lpBufSize);
Rc = InternalTcpRecvUntilStr (s, szBuf, lpBufSize, szStop, uStopSize,
bCaseSensitive, uTimeOut, hLogFile);
if (Rc==TCP4U_SUCCESS) Tcp4uDump (szBuf, *lpBufSize, DUMP4U_RCVD);
if (Rc==TCP4U_OVERFLOW) Tcp4uDump (szBuf, *lpBufSize, "Overflow");
Tcp4uLog (LOG4U_EXIT, "TcpRecvUntilStr");
return Rc;
} /* TcpRecvUntilStr */
int InternalTcpRecvUntilStr (SOCKET s, LPSTR szBuf,unsigned far *lpBufSize,
LPSTR szStop, unsigned uStopSize, BOOL bCaseSensitive,
unsigned uTimeOut, HFILE hLogFile)
{
unsigned uNbRecus;
static int nProxy; /* Proxy sert si clef partagee sur 2 trames */
LPSTR pTest = NULL;
int Rc;
int Idx;
#define XX_RETURN(x,s) \
{ if (pTest!=NULL) Free(pTest); \
*lpBufSize=uNbRecus; \
Tcp4uLog (LOG4U_ERROR, IsCancelled() ? "call cancelled" : s); \
return IsCancelled() ? TCP4U_CANCELLED : x; }
if (s==INVALID_SOCKET || *lpBufSize==0) return TCP4U_ERROR;
/* On prend un buffer qui permet de lire la trame, */
/* la chaine de stop et qui soit toujours 0-terminee */
/* Le recv suivant est sur de ne pas bloquer, vu que */
/* le PEEK a rendu OK */
pTest = Calloc (*lpBufSize+uStopSize+1, 1);
if (pTest==NULL) return TCP4U_INSMEMORY;
uNbRecus = 0;
do
{
Rc = InternalTcpRecv (s, NULL, 0, uTimeOut, HFILE_ERROR);
/* ereur select */
if (Rc != TCP4U_SUCCESS) XX_RETURN (Rc, "Timeout");
/* Lecture en MSG_PEEK */
Tcp4uLog (LOG4U_CALL, "recv on socket %d. MSG_PEEK, buffer %d bytes",
s, *lpBufSize + uStopSize - uNbRecus);
Rc = recv (s, & pTest[uNbRecus], *lpBufSize+uStopSize-uNbRecus, MSG_PEEK);
if (Rc<=0) XX_RETURN (Rc, "TcpRecvUntilStr");
/* --- Maintenant on distingue 2 cas: */
/* Cas 1 : la clef est dans le buffer, */
Idx = FindPos (& pTest[uNbRecus], Rc, szStop, uStopSize,
& nProxy, bCaseSensitive);
if (Idx!=-1) /* la clef est disponible -> pas de probleme */
{
if ( IsBadWritePtr (& szBuf[uNbRecus], Idx) )
XX_RETURN (TCP4U_BUFFERFREED, "TcpRecvUntilStr: invalid pointer")
else
{
if (Idx!=0)
{
InternalTcpRecv (s, & szBuf[uNbRecus], Idx, 0, hLogFile);
uNbRecus += Idx;
}
else uNbRecus -= nProxy;
*lpBufSize = uNbRecus;
InternalTcpRecv (s, pTest, uStopSize - (Idx==0 ? nProxy:0),
0, hLogFile);
nProxy = 0; /* pour la prochaine recherche */
Free (pTest);
return TCP4U_SUCCESS;
}
} /* chaine trouvee */
else
/* Cas 2 : la clef n'est pas dans le buffer */
{int nToBeReceived = min (Rc, (signed) (*lpBufSize-uNbRecus));
if (IsBadWritePtr (& szBuf[uNbRecus], nToBeReceived) )
{
Free (pTest);
Tcp4uLog (LOG4U_ERROR, "TcpRecvUntilStr: invalid pointer");
return TCP4U_BUFFERFREED;
}
else
{
InternalTcpRecv (s, & szBuf[uNbRecus], nToBeReceived , 0, hLogFile);
uNbRecus += nToBeReceived;
}
} /* chaine pas trouvee */
}
while (uNbRecus<*lpBufSize);
Free (pTest);
*lpBufSize = uNbRecus;
#undef XX_RETURN
return TCP4U_OVERFLOW;
} /* InternalTcpRecvUntilStr */
syntax highlighted by Code2HTML, v. 0.9.1