1 //####COPYRIGHTBEGIN####
3 // ----------------------------------------------------------------------------
4 // Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
6 // This program is part of the eCos host tools.
8 // This program is free software; you can redistribute it and/or modify it
9 // under the terms of the GNU General Public License as published by the Free
10 // Software Foundation; either version 2 of the License, or (at your option)
13 // This program is distributed in the hope that it will be useful, but WITHOUT
14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 // You should have received a copy of the GNU General Public License along with
19 // this program; if not, write to the Free Software Foundation, Inc.,
20 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 // ----------------------------------------------------------------------------
24 //####COPYRIGHTEND####
25 //=================================================================
31 //=================================================================
32 //=================================================================
33 //#####DESCRIPTIONBEGIN####
38 // Description: This class abstracts tcp/ip sockets for use in the testing infrastructure
41 //####DESCRIPTIONEND####
44 #include "eCosSocket.h"
45 #include "eCosSerial.h"
46 #include "eCosThreadUtils.h"
47 #include "eCosTrace.h"
50 enum {ERR_TIMEOUT=20000, ERR_READ_AFTER_CLOSE=20001};
52 // Blocking read on one or other of the data sources:
53 // Result: -1 - socket error occurred
54 // 1 - data read from socket
55 // -2 - serial error occurred
56 // 2 - data read from serial
58 CeCosSocket::SSReadResult CeCosSocket::SSRead (CeCosSerial &serial,CeCosSocket &socket,void *pBuf,unsigned int nSize,unsigned int &nRead,bool *pbStop)
60 SSReadResult rc=SS_STOPPED;
61 bool bBlocking=serial.GetBlockingReads();
62 bool bBlockingModified=false;
63 while(0==pbStop || !(*pbStop)){
64 if(!socket.Peek(nRead)){
68 nRead=MIN(nRead,nSize);
69 rc=socket.recv(pBuf,nRead)?SS_SOCKET_READ:SS_SOCKET_ERROR;
73 serial.SetBlockingReads(false);
74 bBlockingModified=true;
77 if(serial.Read(pBuf,nSize,nRead)){
87 CeCosThreadUtils::Sleep(10);
89 if(bBlockingModified){
90 serial.SetBlockingReads(true);
97 CeCosSocket::CeCosSocket ():
98 m_nDefaultTimeout(10*1000),
102 VTRACE(_T("Create socket instance %08x\n"),(unsigned int)this);
105 CeCosSocket::CeCosSocket (int sock /*result of previous call of Listen*/, bool *pbStop):
106 m_nDefaultTimeout(10*1000),
110 VTRACE(_T("Create socket instance %08x\n"),(unsigned int)this);
114 CeCosSocket::CeCosSocket (LPCTSTR pszHostPort,Duration dTimeout):
115 m_nDefaultTimeout(10*1000),
119 VTRACE(_T("Create socket instance %08x\n"),(unsigned int)this);
120 Connect(pszHostPort,dTimeout);
123 bool CeCosSocket::Accept(int sock /*result of previous call of Listen*/, bool *pbStop)
126 while(0==pbStop||!*pbStop){
127 struct sockaddr cli_addr;
131 int clilen=sizeof(struct sockaddr);
132 m_nSock=::accept(sock, (struct sockaddr *) &cli_addr, &clilen);
135 if(WOULDBLOCK==SocketError()){
136 CeCosThreadUtils::Sleep(100);
140 memcpy(&m_nClient,cli_addr.sa_data+2,4);
141 TRACE(_T("Connection accepted from %s - socket %d\n"),(LPCTSTR )ClientName(m_nClient),m_nSock);
149 int CeCosSocket::Listen(int nTcpPort)
152 int sock=::socket(AF_INET, SOCK_STREAM, 0);
154 ERROR(_T("Couldn't create socket\n"));
156 VTRACE(_T("Created socket %d listening on port %d\n"),sock,nTcpPort);
157 // Bind socket to address
158 struct sockaddr_in serv_addr;
159 memset(&serv_addr, 0, sizeof serv_addr);
161 serv_addr.sin_family = AF_INET;
162 serv_addr.sin_port=htons((short)nTcpPort);
163 serv_addr.sin_addr.s_addr = INADDR_ANY;
165 if (::bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
166 TRACE(_T("Couldn't bind socket on port %d\n"),nTcpPort);
168 } else if (-1==::listen(sock, SOMAXCONN)){
170 TRACE(_T("socket error on listen - port %d\n"),nTcpPort);
174 bool rc=(0==::ioctlsocket(sock, FIONBIO, (unsigned long *)&nTrue));
176 int flags=::fcntl(sock,F_GETFL);
178 bool rc=(0==::fcntl (sock, F_SETFL, flags));
181 TRACE(_T("Failed to set socket options on socket %d\n"),sock);
188 bool CeCosSocket::Connect(LPCTSTR pszHostPort,Duration dTimeout)
190 dTimeout=TimeoutDuration(dTimeout);
191 struct sockaddr_in serv_addr;
193 VTRACE(_T("Connect: %s timeout=%d\n"),pszHostPort,dTimeout);
195 // Get the target host address
198 CeCosSocket::ParseHostPort(pszHostPort,strHost,nPort);
201 char *ip=GetHostByName(strHost).GetCString();
202 memset(&serv_addr, 0, sizeof serv_addr);
204 m_nSock = ::socket(AF_INET, SOCK_STREAM, 0);
206 TRACE(_T("Could not create socket [%s]\n"),pszHostPort);
211 VTRACE(_T("Created socket %d connected to %s\n"),m_nSock,pszHostPort);
212 // Bind socket to address
213 serv_addr.sin_family = AF_INET;
214 serv_addr.sin_port=htons((short)nPort);
216 serv_addr.sin_addr.s_addr = inet_addr(ip);
219 VTRACE(_T("Connect() : connecting to server\n"));
220 int cc=::connect(m_nSock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
226 WOULDBLOCK==SocketError()
228 EINPROGRESS==SocketError()
231 // Allow dTimeout milliseconds for connect to complete
235 #pragma warning( push )
236 #pragma warning( disable : 4127 ) // conditional expression is constant
238 FD_SET((unsigned)m_nSock, &set);
240 #pragma warning( pop )
243 tv.tv_sec = dTimeout/1000;
244 tv.tv_usec = 1000*(dTimeout % 1000);
245 switch(::select(m_nSock, NULL, &set , NULL, &tv)){
248 strMsg.Format(_T("attempt timed out after %d seconds"),dTimeout/1000);
252 strMsg=SocketErrString();
258 strMsg=SocketErrString();
263 TRACE(_T("Could not connect to %s - %s\n"),pszHostPort,(LPCTSTR)strMsg);
264 CloseSocket(m_nSock);
275 bool CeCosSocket::sendrecv(bool bSend,const void *pData,unsigned int nLength,
276 LPCTSTR pszMsg,Duration dTimeout,CeCosSocket::StopFunc pFnStop,void *pParam)
279 dTimeout=TimeoutDuration(dTimeout);
281 LPCTSTR pszSR=(bSend?_T("sending"):_T("receiving"));
282 LPTSTR c=(LPTSTR )pData;
285 while((nTodo>0) && ((0==pFnStop) || (!pFnStop(pParam)))){
286 int s=bSend?::send(m_nSock, (const char *)c, nTodo, 0): ::recv(m_nSock, (char *)c, nTodo, 0);
288 m_nErr=ERR_READ_AFTER_CLOSE;
292 if(-1==s && WOULDBLOCK==SocketError()){
293 Duration d=Duration(Now()-ft0);
295 TRACE(_T("%d/%d mSec timeout on socket %d %s %s - processed %d/%d bytes\n") ,
296 d,dTimeout,m_nSock,pszSR,pszMsg,
297 nLength-nTodo,nLength);
301 CeCosThreadUtils::Sleep(100);
307 TRACE(_T("Error on socket %d %s %s - %s\n") ,m_nSock, pszSR, pszMsg, (LPCTSTR )SocketErrString());
314 // Graceful socket closedown
315 CeCosSocket::~CeCosSocket()
318 VTRACE(_T("Delete socket instance %08x\n"),(unsigned int)this);
321 bool CeCosSocket::CloseSocket(int &sock)
325 VTRACE(_T("Closing socket %d\n"),sock);
327 shutdown(sock,0);// SD_BOTH
329 rc=(0==closesocket(sock));
335 TRACE(_T("!!! Exception caught in CeCosSocket::CloseSocket!!!\n"));
342 bool CeCosSocket::SetSocketOptions()
347 rc=(0==::ioctlsocket(m_nSock, FIONBIO, (unsigned long *)&nTrue));
350 int flags=::fcntl(m_nSock,F_GETFL);
353 rc=(0==::fcntl (m_nSock, F_SETFL, flags));
357 setsockopt(m_nSock,SOL_SOCKET,SO_LINGER,(const char *)&bLinger, sizeof(bLinger));
359 TRACE(_T("Failed to set socket options socket %d - %s\n"),m_nSock,(LPCTSTR )SocketErrString());
364 String CeCosSocket::SocketErrString(int nErr)
369 case ERR_TIMEOUT: str=_T("Read operation timed out");break;
370 case ERR_READ_AFTER_CLOSE: str=_T("Read operation after socket closed");break;
372 case WSAEACCES: str=_T("Permission denied");break;
373 case WSAEADDRINUSE: str=_T("Address already in use");break;
374 case WSAEADDRNOTAVAIL: str=_T("Cannot assign requested address");break;
375 case WSAEAFNOSUPPORT: str=_T("Address family not supported by protocol family");break;
376 case WSAEALREADY: str=_T("Operation already in progress");break;
377 case WSAECONNABORTED: str=_T("Software caused connection abort");break;
378 case WSAECONNREFUSED: str=_T("Connection refused");break;
379 case WSAECONNRESET: str=_T("Connection reset by peer");break;
380 case WSAEDESTADDRREQ: str=_T("Destination address required");break;
381 case WSAEFAULT: str=_T("Bad address");break;
382 case WSAEHOSTDOWN: str=_T("Host is down");break;
383 case WSAEHOSTUNREACH: str=_T("No route to host");break;
384 case WSAEINPROGRESS: str=_T("Operation now in progress");break;
385 case WSAEINTR: str=_T("Interrupted function call");break;
386 case WSAEINVAL: str=_T("Invalid argument");break;
387 case WSAEISCONN: str=_T("Socket is already connected");break;
388 case WSAEMFILE: str=_T("Too many open files");break;
389 case WSAEMSGSIZE: str=_T("Message too long");break;
390 case WSAENETDOWN: str=_T("Network is down");break;
391 case WSAENETRESET: str=_T("Network dropped connection on reset");break;
392 case WSAENETUNREACH: str=_T("Network is unreachable");break;
393 case WSAENOBUFS: str=_T("No buffer space available");break;
394 case WSAENOPROTOOPT: str=_T("Bad protocol option");break;
395 case WSAENOTCONN: str=_T("Socket is not connected");break;
396 case WSAENOTSOCK: str=_T("Socket operation on non-socket");break;
397 case WSAEOPNOTSUPP: str=_T("Operation not supported");break;
398 case WSAEPFNOSUPPORT: str=_T("Protocol family not supported");break;
399 case WSAEPROCLIM: str=_T("Too many processes");break;
400 case WSAEPROTONOSUPPORT: str=_T("Protocol not supported");break;
401 case WSAEPROTOTYPE: str=_T("Protocol wrong type for socket");break;
402 case WSAESHUTDOWN: str=_T("Cannot send after socket shutdown");break;
403 case WSAESOCKTNOSUPPORT: str=_T("Socket type not supported");break;
404 case WSAETIMEDOUT: str=_T("Connection timed out");break;
405 case WSATYPE_NOT_FOUND: str=_T("Class type not found");break;
406 case WSAEWOULDBLOCK: str=_T("Resource temporarily unavailable");break;
407 case WSAHOST_NOT_FOUND: str=_T("Host not found");break;
408 case WSA_INVALID_HANDLE: str=_T("Specified event object handle is invalid");break;
409 case WSA_INVALID_PARAMETER: str=_T("One or more parameters are invalid");break;
410 //case WSAINVALIDPROCTABLE: str=_T("Invalid procedure table from service provider");break;
411 //case WSAINVALIDPROVIDER: str=_T("Invalid service provider version number");break;
412 case WSA_IO_INCOMPLETE: str=_T("Overlapped I/O event object not in signaled state");break;
413 case WSA_IO_PENDING: str=_T("Overlapped operations will complete later");break;
414 case WSA_NOT_ENOUGH_MEMORY: str=_T("Insufficient memory available");break;
415 case WSANOTINITIALISED: str=_T("Successful case WSAStartup not yet:performed");break;
416 case WSANO_DATA: str=_T("Valid name, no data record of requested type");break;
417 case WSANO_RECOVERY: str=_T("This is a non-recoverable error");break;
418 //case WSAPROVIDERFAILEDINIT: str=_T("Unable to initialize a service provider");break;
419 case WSASYSCALLFAILURE: str=_T("System call failure");break;
420 case WSASYSNOTREADY: str=_T("Network subsystem is unavailable");break;
421 case WSATRY_AGAIN: str=_T("Non-authoritative host not found");break;
422 case WSAVERNOTSUPPORTED: str=_T("WINSOCK.DLL version out of range");break;
423 case WSAEDISCON: str=_T("Graceful shutdown in progress");break;
424 case WSA_OPERATION_ABORTED: str=_T("Overlapped operation aborted");break;
426 str.Format(_T("Unknown error %d (0x%08x)"),nErr,nErr);
430 case ERR_TIMEOUT: str=_T("Read operation timed out");break;
431 case ERR_READ_AFTER_CLOSE: str=_T("Read operation after socket closed");break;
439 bool CeCosSocket::sendInteger(int n,LPCTSTR pszMsg,Duration dTimeout)
441 // This has to support cross-architectural endianness
442 unsigned char c[sizeof(int)];
443 for(unsigned int i=0;i<sizeof(int);i++){
444 c[i]=(unsigned char)(n&0xff);
447 return send (c, sizeof(int),pszMsg,dTimeout);
450 bool CeCosSocket::recvInteger(int & n,LPCTSTR pszMsg,Duration dTimeout)
452 // This has to support cross-architectural endianness
453 unsigned char c[sizeof(int)];
454 bool rc=recv (c, sizeof(int),pszMsg,dTimeout);
457 for(int i=sizeof(int)-1;i>=0;--i){
465 // Socket communications for strings are always non-UNICODE:
466 bool CeCosSocket::recvString (String &str,LPCTSTR pszMsg,Duration dTimeout)
470 if(recvInteger(nLength,pszMsg,dTimeout)){
475 char *c=(char *)b.Data();
477 rc=recv(c,nLength,pszMsg,dTimeout);
479 str=String::CStrToUnicodeStr(c);
486 // Socket communications for strings are always non-UNICODE:
487 bool CeCosSocket::sendString (const String &str,LPCTSTR pszMsg,Duration dTimeout)
489 char *psz=str.GetCString();
490 int nLength=strlen(psz);
491 bool rc=sendInteger(nLength,pszMsg,dTimeout) && (0==nLength || send(psz,nLength,pszMsg,dTimeout));
497 // Give indication of bytes available to be read (but don't read them)
498 bool CeCosSocket::Peek (unsigned int &nAvail)
501 int n=::recv(m_nSock, buf, sizeof buf, MSG_PEEK);
507 if(WOULDBLOCK==SocketError()){
508 rc=true; // nAvail stays==0
510 ERROR(_T("Peek: err=%d %s\n"),SocketError(),(LPCTSTR)SocketErrString());
514 m_nErr=ERR_READ_AFTER_CLOSE;
523 // Connect tcp/ip port and serial port together.
524 // Traffic is passed through pFunc, passed parameter pParam.
525 // The pFunc function:
526 // may reallocate pBuf (using malloc/realloc etc...)
527 // must leave pBuf allocated on exit
528 // should not close either serial or socket
529 // should leave writing to its caller
530 // should return false if it wishes to terminate the connection (after caller has written output)
531 bool CeCosSocket::ConnectSocketToSerial (CeCosSocket &socket,CeCosSerial &serial,FilterFunc *pSerialToSocketFilterFunc/*=0*/,void *pSerialParam/*=0*/,FilterFunc *pSocketToSerialFilterFunc/*=0*/,void *pSocketParam/*=0*/,bool *pbStop/*=0*/)
535 void *pBuf=malloc(BUFSIZE);
536 TRACE(_T("ConnectSocketToSerial: connected\n"));
541 unsigned int nWritten;//hack
542 serial.Write(_T("+"),1,nWritten);//hack
545 while(rc && (0==pbStop || !(*pbStop))){
546 unsigned int nRead=0;
547 switch(SSRead (serial,socket,pBuf,BUFSIZE,nRead,pbStop)){
549 VTRACE(_T("Serial:%d\n"),nRead);
550 if(pSerialToSocketFilterFunc){
551 rc=pSerialToSocketFilterFunc(pBuf,nRead,serial,socket,pSerialParam);
553 if(nRead && !socket.send(pBuf,nRead)){
554 TRACE(_T("Failed to write to socket\n"));
559 unsigned int nWritten;
560 VTRACE(_T("Socket:%d\n"),nRead);
561 if(pSocketToSerialFilterFunc){
562 rc=pSocketToSerialFilterFunc(pBuf,nRead,serial,socket,pSocketParam);
565 LPTSTR c=(LPTSTR )pBuf;
568 if(!serial.Write(pBuf,nRead,nWritten)){
569 TRACE(_T("Failed to write to serial\n"));
579 case SS_SERIAL_ERROR:
580 TRACE(_T("SSRead serial error - %s\n"),(LPCTSTR)serial.ErrString());
583 case SS_SOCKET_ERROR:
584 TRACE(_T("SSRead socket error - %s\n"),(LPCTSTR)socket.SocketErrString());
588 TRACE(_T("SSRead stopped\n"));
595 ERROR(_T("!!! ConnectSocketToSerial exception caught!!!\n"));
603 // Connect two tcp/ip ports together.
604 // Traffic is passed through pFunc, passed parameter pParam.
605 // The pFunc function:
606 // may reallocate pBuf (using malloc/realloc etc...)
607 // must leave pBuf allocated on exit
608 // should not close either serial or socket
609 // should leave writing to its caller
610 // should return false if it wishes to terminate the connection (after caller has written output)
611 bool CeCosSocket::ConnectSocketToSocket (CeCosSocket &o,FilterFunc *pSocketToSocketFilterFunc1,FilterFunc *pSocketToSocketFilterFunc2,void *pParam,bool *pbStop)
614 void *pBuf=malloc(BUFSIZE);
615 TRACE(_T("ConnectSocketToSocket: connected\n"));
618 while(rc && (0==pbStop || !(*pbStop))){
621 FD_SET((unsigned)m_nSock, &set);
622 FD_SET((unsigned)o.m_nSock, &set);
626 switch(::select(m_nSock,&set,0,0,&tv)){
633 unsigned int nAvail=0;
634 if(FD_ISSET((unsigned)m_nSock, &set) && Peek(nAvail) && recv(pBuf,nAvail)){
635 //rc=pSocketToSocketFilterFunc1(pBuf,nAvail,socket,this,o);
638 if(FD_ISSET((unsigned)o.m_nSock, &set) && o.Peek(nAvail) && o.recv(pBuf,nAvail)){
639 //rc=pSocketToSocketFilterFunc2(pBuf,nAvail,socket,o,this);
649 TRACE(_T("!!! ConnectSocketToSocket exception caught!!!\n"));
656 bool CeCosSocket::ConnectSocketToSerial (
657 int nListenSock,LPCTSTR pszPort, int nBaud,
658 FilterFunc *pSerialToSocketFilterFunc/*=0*/,void *pSerialParam/*=0*/,FilterFunc *pSocketToSerialFilterFunc/*=0*/,void *pSocketParam/*=0*/,
663 TRACE(_T("ConnectSocketToSerial : socket %d <--> %s\n"),nListenSock,pszPort);
666 serial.SetBlockingReads(false);
667 // Open serial device.
668 if (!serial.Open(pszPort,nBaud)){
669 ERROR(_T("Couldn't open port %s\n"),pszPort);
671 // Flush the serial buffer.
674 TRACE(_T("ConnectSocketToSerial: waiting for connection...\n"));
676 if(!socket.Accept(nListenSock,pbStop)){
677 ERROR(_T("ConnectSocketToSerial - couldn't accept\n"));
679 rc=ConnectSocketToSerial (socket,serial,pSerialToSocketFilterFunc,pSerialParam,pSocketToSerialFilterFunc,pSocketParam,pbStop);
682 TRACE(_T("ConnectSocketToSerial : done\n"));
685 TRACE(_T("ConnectSocketToSerial !!!exception handled!!!\n"));
690 String CeCosSocket::ClientName(int nClient)
693 memcpy(ip,&nClient,4);
694 struct hostent *he=::gethostbyaddr((const char *)ip,4,AF_INET);
697 str=String::CStrToUnicodeStr(he->h_name);
699 str.Format(_T("%u.%u.%u.%u"),ip[0],ip[1],ip[2],ip[3]);
704 String CeCosSocket::HostPort(LPCTSTR pszHost,int nPort)
707 str.Format(_T("%s:%d"),pszHost,nPort);
711 // Split the string into host:port parts. Result tells us whether it was successful.
712 bool CeCosSocket::ParseHostPort (LPCTSTR pszHostPort, String &strHost, int &nPort)
714 int n=_stscanf(pszHostPort,_T("%[^:]:%d"),strHost.GetBuffer(_tcslen(pszHostPort)),&nPort);
715 strHost.ReleaseBuffer();
716 return 2==n && nPort>0 && nPort<=0xffff;
719 // Is the string in the form host:port?
720 bool CeCosSocket::IsLegalHostPort (LPCTSTR pszHostPort)
724 return ParseHostPort(pszHostPort,strHost,nPort);
727 // Translate a timeout that may be one of the special values DEFAULTTIMEOUT or NOTIMEOUT to a value in milliseconds.
728 Duration CeCosSocket::TimeoutDuration(Duration dTimeout)
732 dTimeout=m_nDefaultTimeout;
743 String CeCosSocket::SocketErrString() {
744 return SocketErrString(m_nErr);
748 bool CeCosSocket::SameHost(LPCTSTR host1, LPCTSTR host2)
750 return 0==_tcscmp(host1,host2) || (GetHostByName(host1)==GetHostByName(host2));
753 bool CeCosSocket::Init()
757 WORD wVersionRequested = MAKEWORD( 2, 0 );
758 WSAStartup( wVersionRequested, &wsaData );
763 void CeCosSocket::Term()
770 LPCTSTR CeCosSocket::MyHostName()
775 if(0==gethostname(szMyname,sizeof szMyname)){
776 str=String::CStrToUnicodeStr(szMyname);
782 LPCTSTR CeCosSocket::MySimpleHostName()
787 // Remove all after a '.'
788 LPCTSTR c=_tcschr(str,_TCHAR('.'));
790 str.resize(c-(LPCTSTR)str);
796 const String CeCosSocket::GetHostByName(LPCTSTR pszHost)
798 typedef std::map<String,String> MapStringToString;
799 static MapStringToString hostmap;
800 MapStringToString::iterator it=hostmap.find(pszHost);
801 if(hostmap.end()==it){
802 char *h=0; // avoid erroneous gcc warning message
803 h=String(pszHost).GetCString();
805 struct hostent* host_dat;
806 if (0!=(host_dat=::gethostbyname(h))){
807 char *c=inet_ntoa( *( (struct in_addr *)host_dat->h_addr_list[0] ) );
810 hostmap[pszHost]=String::CStrToUnicodeStr(ip);
814 return String::CStrToUnicodeStr(ip);