/*************************************************************************\ * Copyright (c) 2002 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ // // Author: Jeff Hill // #include #include "errlog.h" #define epicsAssertAuthor "Jeff Hill johill@lanl.gov" #include "epicsAssert.h" #define epicsExportSharedSymbols #include "casdef.h" #include "caNetAddr.h" #include "casIntfIO.h" #include "casStreamIO.h" #include "casStreamOS.h" // // 5 appears to be a TCP/IP built in maximum // const unsigned caServerConnectPendQueueSize = 5u; // // casIntfIO::casIntfIO() // casIntfIO::casIntfIO ( const caNetAddr & addrIn ) : sock ( INVALID_SOCKET ), addr ( addrIn.getSockIP() ) { int status; osiSocklen_t addrSize; bool portChange; if ( ! osiSockAttach () ) { throw S_cas_internal; } /* * Setup the server socket */ this->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ); if (this->sock==INVALID_SOCKET) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); printf ( "No socket error was %s\n", sockErrBuf ); throw S_cas_noFD; } epicsSocketEnableAddressReuseDuringTimeWaitState ( this->sock ); status = bind ( this->sock, reinterpret_cast (&this->addr), sizeof(this->addr) ); if (status<0) { if (SOCKERRNO == SOCK_EADDRINUSE) { // // enable assignment of a default port // (so the getsockname() call below will // work correctly) // this->addr.sin_port = ntohs (0); status = bind( this->sock, reinterpret_cast (&this->addr), sizeof(this->addr) ); } if (status<0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); char buf[64]; ipAddrToA (&this->addr, buf, sizeof(buf)); errPrintf ( S_cas_bindFail, __FILE__, __LINE__, "- bind TCP IP addr=%s failed because %s", buf, sockErrBuf ); epicsSocketDestroy (this->sock); throw S_cas_bindFail; } portChange = true; } else { portChange = false; } addrSize = ( osiSocklen_t ) sizeof (this->addr); status = getsockname ( this->sock, reinterpret_cast ( &this->addr ), &addrSize ); if (status) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAS: getsockname() error %s\n", sockErrBuf ); epicsSocketDestroy (this->sock); throw S_cas_internal; } // // be sure of this now so that we can fetch the IP // address and port number later // assert (this->addr.sin_family == AF_INET); if ( portChange ) { errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n"); errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n", ntohs (this->addr.sin_port) ); errlogPrintf ( "cas warning: but now two or more servers share the same UDP port.\n"); errlogPrintf ( "cas warning: Depending on your IP kernel this server may not be\n" ); errlogPrintf ( "cas warning: reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST)\n" ); } status = listen(this->sock, caServerConnectPendQueueSize); if(status < 0) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAS: listen() error %s\n", sockErrBuf ); epicsSocketDestroy (this->sock); throw S_cas_internal; } } // // casIntfIO::~casIntfIO() // casIntfIO::~casIntfIO() { if (this->sock != INVALID_SOCKET) { epicsSocketDestroy (this->sock); } osiSockRelease (); } // // newStreamIO::newStreamClient() // casStreamOS *casIntfIO::newStreamClient ( caServerI & cas, clientBufMemoryManager & bufMgr ) const { static bool oneMsgFlag = false; struct sockaddr newClientAddr; osiSocklen_t length = ( osiSocklen_t ) sizeof ( newClientAddr ); SOCKET newSock = epicsSocketAccept ( this->sock, & newClientAddr, & length ); if ( newSock == INVALID_SOCKET ) { int errnoCpy = SOCKERRNO; if ( errnoCpy != SOCK_EWOULDBLOCK && ! oneMsgFlag ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAS: %s accept error \"%s\"\n", __FILE__, sockErrBuf ); oneMsgFlag = true; } return NULL; } else if ( sizeof ( newClientAddr ) > (size_t) length ) { epicsSocketDestroy ( newSock ); errlogPrintf ( "CAS: accept returned bad address len?\n" ); return NULL; } oneMsgFlag = false; ioArgsToNewStreamIO args; args.clientAddr = newClientAddr; args.sock = newSock; casStreamOS * pOS = new casStreamOS ( cas, bufMgr, args ); if ( ! pOS ) { errMessage ( S_cas_noMemory, "unable to create data structures for a new client" ); epicsSocketDestroy ( newSock ); } else { if ( cas.getDebugLevel() > 0u ) { char pName[64u]; pOS->hostName ( pName, sizeof ( pName ) ); errlogPrintf ( "CAS: allocated client object for \"%s\"\n", pName ); } } return pOS; } // // casIntfIO::setNonBlocking() // void casIntfIO::setNonBlocking() { int status; osiSockIoctl_t yes = true; status = socket_ioctl(this->sock, FIONBIO, &yes); // X aCC 392 if ( status < 0 ) { char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "%s:CAS: server non blocking IO set fail because \"%s\"\n", __FILE__, sockErrBuf ); } } // // casIntfIO::getFD() // int casIntfIO::getFD() const { return this->sock; } // // casIntfIO::show() // void casIntfIO::show(unsigned level) const { if (level>2u) { printf(" casIntfIO::sock = %d\n", this->sock); } } // // casIntfIO::serverAddress () // (avoid problems with GNU inliner) // caNetAddr casIntfIO::serverAddress () const { return caNetAddr (this->addr); }