diff options
Diffstat (limited to 'SrcShared/EmTransportSocket.cpp')
-rw-r--r-- | SrcShared/EmTransportSocket.cpp | 1220 |
1 files changed, 1220 insertions, 0 deletions
diff --git a/SrcShared/EmTransportSocket.cpp b/SrcShared/EmTransportSocket.cpp new file mode 100644 index 0000000..8121cec --- /dev/null +++ b/SrcShared/EmTransportSocket.cpp @@ -0,0 +1,1220 @@ +/* -*- mode: C++; tab-width: 4 -*- */ +/* ===================================================================== *\ + Copyright (c) 2000-2001 Palm, Inc. or its subsidiaries. + All rights reserved. + + This file is part of the Palm OS Emulator. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. +\* ===================================================================== */ + +#include "EmCommon.h" +#include "EmTransportSocket.h" + +#include "EmErrCodes.h" // kError_CommOpen, kError_CommNotOpen, kError_NoError +#include "Logging.h" // LogSerial +#include "Platform.h" // Platform::AllocateMemory + +#if PLATFORM_MAC +#include <GUSIPOSIX.h> // inet_addr +#endif + +#if PLATFORM_UNIX +#include <errno.h> // ENOENT, errno +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> // timeval +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <unistd.h> // close +#include <arpa/inet.h> // inet_addr +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE 0xffffffff +#endif + +#define PRINTF if (!LogSerial ()) ; else LogAppendMsg + +EmTransportSocket::OpenPortList EmTransportSocket::fgOpenPorts; + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket c'tor + * + * DESCRIPTION: Constructor. Initialize our data members. + * + * PARAMETERS: None + * + * RETURNED: Nothing + * + * NOTES: We used to only have a fDataSocket. Now, to allow for + * a listening socket to be opened before a connection is + * attempted, we need two sockets. Note that EmTransport + * Socket does not get its hands dirty with actual sockets. + * Thus, fDataListenSocket is not a socket in the traditional + * sense, and may be connected or not. + * + ***********************************************************************/ + +EmTransportSocket::EmTransportSocket (void) : + fReadMutex (), + fReadBuffer (), + fDataConnectSocket (NULL), + fDataListenSocket (NULL), + fConfig (), + fCommEstablished (false) +{ +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket c'tor + * + * DESCRIPTION: Constructor. Initialize our data members. + * + * PARAMETERS: desc - descriptor information used when opening + * the TCP port. + * + * RETURNED: Nothing + * + ***********************************************************************/ + +EmTransportSocket::EmTransportSocket (const EmTransportDescriptor& desc) : + fReadMutex (), + fReadBuffer (), + fDataConnectSocket (NULL), + fDataListenSocket (NULL), + fConfig (), + fCommEstablished (false) +{ + ConfigSocket config; + + string addr = desc.GetSchemeSpecific (); + string::size_type colonPos = addr.find (':'); + string::size_type nonNumPos = addr.find_first_not_of ("0123456789"); + + // If there's a colon, assume a fully-specified address. + + if (colonPos != string::npos) + { + config.fTargetHost = addr.substr (0, colonPos); + config.fTargetPort = addr.substr (colonPos + 1); + } + + // If there's no colon, look for numbers. If the entire + // address is made up of numbers, assume it's a port number. + + else if (nonNumPos == string::npos) + { + config.fTargetPort = addr; + } + + // Otherwise, assume it's a host address with no port. + + else + { + config.fTargetHost = addr; + } + + fConfig = config; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket c'tor + * + * DESCRIPTION: Constructor. Initialize our data members. + * + * PARAMETERS: config - configuration information used when opening + * the TCP port. + * + * RETURNED: Nothing + * + ***********************************************************************/ + +EmTransportSocket::EmTransportSocket (const ConfigSocket& config) : + fReadMutex (), + fReadBuffer (), + fDataConnectSocket (NULL), + fDataListenSocket (NULL), + fConfig (config), + fCommEstablished (false) +{ +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket d'tor + * + * DESCRIPTION: Destructor. Delete our data members. + * + * PARAMETERS: None + * + * RETURNED: Nothing + * + ***********************************************************************/ + +EmTransportSocket::~EmTransportSocket (void) +{ + this->Close (); +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::Open + * + * DESCRIPTION: Open the transport using the information provided + * either in the constructor or with SetConfig. + * + * PARAMETERS: None + * + * RETURNED: 0 if no error. + * + * NOTES: Implements the new method of opening a listening socket, + * then opening a connection socket. This avoids the problem + * where both machines might try to connect, fail, and then + * each fall back to listening. However, this approach does + * not work in localhost mode, for obvious (upon reflection) + * reasons: if you start listening, then shout, you are + * going to hear yourself (or in this case, you're going to + * drop your listening socket once your connecting socket + * tells you that it has connected, not knowing that you have + * connected to yourself). + * + ***********************************************************************/ + +ErrCode EmTransportSocket::Open (void) +{ + PRINTF ("EmTransportSocket::Open..."); + + // Exit if communications have already been established. + + if (fCommEstablished) + { + PRINTF ("EmTransportSocket::Open: TCP port already open...leaving..."); + return kError_CommOpen; + } + + string registrationKey = "TCP:" + fConfig.fTargetHost + ":" + fConfig.fTargetPort; + EmAssert (fgOpenPorts.find (registrationKey) == fgOpenPorts.end ()); + + ErrCode err; + + if (fConfig.fTargetHost != "localhost") + { + err= OpenCommPortListen (fConfig); + + if (err) + { + PRINTF ("EmTransportSocket::Open: comm port closed due to error %ld on listening attempt", err); + PRINTF ("EmTransportSocket::Open: closing listening socket"); + + err = CloseCommPortListen (); + fCommEstablished = false; + + if (err) + PRINTF ("EmTransportSocket::Open: err = %ld when closing listen socket", err); + } + else if (fDataListenSocket->ConnectPending ()) + { + PRINTF ("EmTransportSocket::Open: not attempting to connect due to a pending connection"); + } + else + { + PRINTF ("EmTransportSocket::Open: listening socket opened properly"); + + // set to true here because we are listening properly; not affected by whether this + // next connect call fails + fCommEstablished = true; + + err = OpenCommPortConnect (fConfig); + } + + if (err == 0) + { + fCommEstablished = true; + fgOpenPorts[registrationKey] = this; + + PRINTF ("EmTransportSocket::Open: successful connection, so closing listening socket"); + + err = CloseCommPortListen (); + } + else + { + PRINTF ("EmTransportSocket::Open: err = %ld on connect attempt", err); + PRINTF ("EmTransportSocket::Open: closing connect socket"); + + err = CloseCommPortConnect (); + } + } + else + { + PRINTF ("EmTransportSocket::Open: opening in localhost mode (old style)"); + + err = OpenCommPortConnect (fConfig); + + if (err) + { + PRINTF ("EmTransportSocket::Open: err %ld in connect attempt", err); + + err = CloseCommPortConnect (); + + if (err) + PRINTF ("EmTransportSocket::Open: err %ld in connect socket close", err); + + PRINTF ("EmTransportSocket::Open: falling back to listening"); + + err = OpenCommPortListen (fConfig); + + PRINTF ("EmTransportSocket::Open: listening socket opened properly"); + } + + if (err == 0) + { + fCommEstablished = true; + fgOpenPorts [registrationKey] = this; + } + else + { + PRINTF ("EmTransportSocket::Open: error %ld on listening attempt, closing listen socket", err); + + err = CloseCommPortListen (); + } + } + + return err; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::Close + * + * DESCRIPTION: Close the transport. + * + * PARAMETERS: None + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ + +ErrCode EmTransportSocket::Close (void) +{ + PRINTF ("EmTransportSocket::Close..."); + + if (!fCommEstablished) + { + PRINTF ("EmTransportSocket::Close: TCP port not open...leaving..."); + return kError_CommNotOpen; + } + + fCommEstablished = false; + + string registrationKey = "TCP:" + fConfig.fTargetHost + ":" + fConfig.fTargetPort; + + fgOpenPorts.erase (registrationKey); + + ErrCode err = CloseCommPort (); + + if (err) + PRINTF ("EmTransportSocket::Close: err = %ld", err); + + return err; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::Read + * + * DESCRIPTION: Read up to the given number of bytes, storing them in + * the given buffer. + * + * PARAMETERS: len - maximum number of bytes to read. + * data - buffer to receive the bytes. + * + * RETURNED: 0 if no error. The number of bytes actually read is + * returned in len if there was no error. + * + ***********************************************************************/ + +ErrCode EmTransportSocket::Read (long& len, void* data) +{ + PRINTF ("EmTransportSocket::Read..."); + + if (!fCommEstablished) + { + PRINTF ("EmTransportSocket::Read: port not open, leaving"); + return kError_CommNotOpen; + } + + GetIncomingData (data, len); + + if (LogSerialData ()) + LogAppendData (data, len, "EmTransportSocket::Read: reading %ld bytes.", len); + else + PRINTF ("EmTransportSocket::Read: reading %ld bytes", len); + + return kError_NoError; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::Write + * + * DESCRIPTION: Write up the the given number of bytes, using the data + * in the given buffer. + * + * PARAMETERS: len - number of bytes in the buffer. + * data - buffer containing the bytes. + * + * RETURNED: 0 if no error. The number of bytes actually written is + * returned in len if there was no error. + * + ***********************************************************************/ + +ErrCode EmTransportSocket::Write (long& len, const void* data) +{ + PRINTF ("EmTransportSocket::Write..."); + + ErrCode err; + + if (!fCommEstablished) + { + PRINTF ("...EmTransportSocket::Write: port not open, leaving"); + return kError_CommNotOpen; + } + + // Tracking errors here so that we can close the connection if some + // mechanism farther down the food chain (in CTCPSocket) finds out + // that the connection was dropped. This wasn't checked before, as + // errors were ignored in PutOutgoingData, explaining the many + // writes of 0 bytes (indicating a dropped connection) in log files. + + err = PutOutgoingData (data, len); + + if (err) + { + PRINTF ("...EmTransportSocket::Write: PutOutgoingData returned err %ld, closing connection", err); + + this->Close (); + + return kError_CommNotOpen; + } + + if (LogSerialData ()) + LogAppendData (data, len, "EmTransportSocket::Write: writing %ld bytes.", len); + else + PRINTF ("EmTransportSocket::Write: writing %ld bytes", len); + + return kError_NoError; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::CanRead + * + * DESCRIPTION: Return whether or not the transport is available for + * a read operation (that is, it's connected to another + * entity). Does NOT indicate whether or not there are + * actually any bytes available to be read. + * + * PARAMETERS: None + * + * RETURNED: True if so. + * + ***********************************************************************/ + +Bool EmTransportSocket::CanRead (void) +{ + return fCommEstablished; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::CanWrite + * + * DESCRIPTION: Return whether or not the transport is available for + * a write operation (that is, it's connected to another + * entity). Does NOT indicate whether or not there is + * actually any room in the transport's internal buffer + * for the data being written. + * + * PARAMETERS: None + * + * RETURNED: True if so. + * + ***********************************************************************/ + +Bool EmTransportSocket::CanWrite (void) +{ + return fCommEstablished; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::BytesInBuffer + * + * DESCRIPTION: Returns the number of bytes that can be read with the + * Read method. Note that bytes may be received in + * between the time BytesInBuffer is called and the time + * Read is called, so calling the latter with the result + * of the former is not guaranteed to fetch all received + * and buffered bytes. + * + * PARAMETERS: minBytes - try to buffer at least this many bytes. + * Return when we have this many bytes buffered, or + * until some small timeout has occurred. + * + * RETURNED: Number of bytes that can be read. + * + ***********************************************************************/ + +long EmTransportSocket::BytesInBuffer (long /*minBytes*/) +{ + if (!fCommEstablished) + return 0; + + return this->IncomingDataSize (); +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::GetSpecificName + * + * DESCRIPTION: Returns the port name, or host address, depending on the + * transport in question. + * + * PARAMETERS: + * + * RETURNED: string, appropriate to the transport in question. + * + ***********************************************************************/ + + string EmTransportSocket::GetSpecificName (void) + { + string returnString = fConfig.fTargetHost + ":" + fConfig.fTargetPort; + return returnString; + } + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::GetTransport + * + * DESCRIPTION: Return any transport object currently using the port + * specified in the given configuration. + * + * PARAMETERS: config - The configuration object containing information + * on a port in which we're interested. All or some + * of the information in this object is used when + * searching for a transport object already utilizing + * the port. + * + * RETURNED: Any found transport object. May be NULL. + * + ***********************************************************************/ + +EmTransportSocket* EmTransportSocket::GetTransport (const ConfigSocket& config) +{ + string registrationKey = "TCP:" + config.fTargetHost + ":" + config.fTargetPort; + OpenPortList::iterator iter = fgOpenPorts.find (registrationKey); + + if (iter == fgOpenPorts.end ()) + return NULL; + + return iter->second; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::GetDescriptorList + * + * DESCRIPTION: Return the list of TCP ports on this computer. Used + * to prepare a menu of TCP port choices. + * + * PARAMETERS: nameList - port names are added to this list. + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmTransportSocket::GetDescriptorList (EmTransportDescriptorList& descList) +{ + descList.clear (); + + descList.push_back (EmTransportDescriptor (kTransportSocket)); +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::OpenCommPortConnect + * + * DESCRIPTION: Open the TCP port. + * + * PARAMETERS: config - data block describing which port to use. + * + * RETURNED: 0 if no error. + * + * NOTES: Implements a subset of OpenCommPort's functionality. + * In particular, it tries to connect, then returns. + * OpenCommPortListen implements the listening behavior. + * This change was made so that control over the behavior + * (whether connect first or listen first) could be + * in Open. + * + ***********************************************************************/ + +ErrCode EmTransportSocket::OpenCommPortConnect (const EmTransportSocket::ConfigSocket& config) +{ + ErrCode err; + + if (fDataConnectSocket) + return errNone; + + fDataConnectSocket = new CTCPClientSocket (EmTransportSocket::EventCallBack, + config.fTargetHost, atoi (config.fTargetPort.c_str ()), this); + + // Try establishing a connection to some peer already waiting on the + // target host, on the target port + + err = fDataConnectSocket->Open (); + + return err; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::OpenCommPortListen + * + * DESCRIPTION: Open the TCP port. + * + * PARAMETERS: config - data block describing which port to use. + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ + +ErrCode EmTransportSocket::OpenCommPortListen (const EmTransportSocket::ConfigSocket& config) +{ + ErrCode err; + + if (fDataListenSocket) + return errNone; + + fDataListenSocket = new CTCPClientSocket (EmTransportSocket::EventCallBack, + config.fTargetHost, atoi (config.fTargetPort.c_str ()), this); + + // Fall into server mode and start waiting + + err = fDataListenSocket->OpenInServerMode (); + + return err; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::EventCallBack + * + * DESCRIPTION: + * + * PARAMETERS: None. + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ +void EmTransportSocket::EventCallBack (CSocket* s, int event) +{ + switch (event) + { + case CSocket::kConnected: + break; + + case CSocket::kDataReceived: + while (s->HasUnreadData(1)) + { + long len = 1; + char buf; + + s->Read(&buf, len, &len); + + if (len > 0) + { + // Log the data. + if (LogSerialData ()) + LogAppendData (&buf, len, "EmTransportSocket::CommRead: Received data:"); + else + PRINTF ("EmTransportSocket::CommRead: Received %ld TCP bytes.", len); + + // Add the data to the EmTransportSocket object's buffer. + ((CTCPClientSocket*)s)->GetOwner()->PutIncomingData (&buf, len); + } + } + break; + + case CSocket::kDisconnected: + break; + } +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::CloseCommPort + * + * DESCRIPTION: Close the comm port. + * + * PARAMETERS: None. + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ + +ErrCode EmTransportSocket::CloseCommPort (void) +{ + // Must close each socket separately. + + if (fDataListenSocket) + { + fDataListenSocket->Close (); + fDataListenSocket->Delete (); + fDataListenSocket = NULL; + } + + if (fDataConnectSocket) + { + fDataConnectSocket->Close (); + fDataConnectSocket->Delete (); + fDataConnectSocket = NULL; + } + + return errNone; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::CloseCommPortConnect + * + * DESCRIPTION: Close the comm port. + * + * PARAMETERS: None. + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ + +ErrCode EmTransportSocket::CloseCommPortConnect (void) +{ + if (fDataConnectSocket) + { + fDataConnectSocket->Close (); + fDataConnectSocket->Delete (); + fDataConnectSocket = NULL; + } + + return errNone; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::CloseCommPortListen + * + * DESCRIPTION: Close the comm port. + * + * PARAMETERS: None. + * + * RETURNED: 0 if no error. + * + ***********************************************************************/ + +ErrCode EmTransportSocket::CloseCommPortListen (void) +{ + if (fDataListenSocket) + { + fDataListenSocket->Close (); + fDataListenSocket->Delete (); + fDataListenSocket = NULL; + } + + return errNone; +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::PutIncomingData + * + * DESCRIPTION: Thread-safe method for adding data to the queue that + * holds data read from the TCP port. + * + * PARAMETERS: data - pointer to the read data. + * len - number of bytes pointed to by "data". + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmTransportSocket::PutIncomingData (const void* data, long& len) +{ + if (len == 0) + return; + + omni_mutex_lock lock (fReadMutex); + + char* begin = (char*) data; + char* end = begin + len; + while (begin < end) + fReadBuffer.push_back (*begin++); +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::GetIncomingData + * + * DESCRIPTION: Thread-safe method for getting data from the queue + * holding data read from the TCP port. + * + * PARAMETERS: data - pointer to buffer to receive data. + * len - on input, number of bytes available in "data". + * On exit, number of bytes written to "data". + * + * RETURNED: Nothing + * + ***********************************************************************/ + +void EmTransportSocket::GetIncomingData (void* data, long& len) +{ + omni_mutex_lock lock (fReadMutex); + + if (len > (long) fReadBuffer.size ()) + len = (long) fReadBuffer.size (); + + char* p = (char*) data; + deque<char>::iterator begin = fReadBuffer.begin (); + deque<char>::iterator end = begin + len; + + copy (begin, end, p); + fReadBuffer.erase (begin, end); +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::IncomingDataSize + * + * DESCRIPTION: Thread-safe method returning the number of bytes in the + * read queue. + * + * PARAMETERS: None. + * + * RETURNED: Number of bytes in the read queue. + * + ***********************************************************************/ + + +long EmTransportSocket::IncomingDataSize (void) +{ + omni_mutex_lock lock (fReadMutex); + + return fReadBuffer.size (); +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::PutOutgoingData + * + * DESCRIPTION: Thread-safe method for adding data to the queue that + * holds data to be written to the TCP port. + * + * PARAMETERS: data - pointer to the read data. + * len - number of bytes pointed to by "data". + * + * RETURNED: Nothing + * + * NOTES: See the caveat in the constructor's comments about treating + * these "sockets" as real sockets. The actual control over + * sockets is down a level in CTCPSocket; thus, the listening + * socket at this level is, logically, where data should be + * written to. The connect socket is tried first, as the + * listening socket is closed on a successful connection. + * + ***********************************************************************/ + +ErrCode EmTransportSocket::PutOutgoingData (const void* data, long& len) +{ + ErrCode err = kError_CommNotOpen; + + if (fDataListenSocket) + err = fDataListenSocket->Write (data, len, &len); + if (fDataConnectSocket) + err = fDataConnectSocket->Write (data, len, &len); + + return err; +} + + +#pragma mark - + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::ConfigSocket c'tor + * + * DESCRIPTION: Constructor. Initialize our data members. + * + * PARAMETERS: None + * + * RETURNED: Nothing + * + ***********************************************************************/ + +EmTransportSocket::ConfigSocket::ConfigSocket (void) +{ +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::ConfigSocket d'tor + * + * DESCRIPTION: Destructor. Delete our data members. + * + * PARAMETERS: None + * + * RETURNED: Nothing + * + ***********************************************************************/ + +EmTransportSocket::ConfigSocket::~ConfigSocket (void) +{ +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::ConfigSocket::NewTransport + * + * DESCRIPTION: Create a new transport object based on the configuration + * information in this object. + * + * PARAMETERS: None + * + * RETURNED: The new transport object. + * + ***********************************************************************/ + +EmTransport* EmTransportSocket::ConfigSocket::NewTransport (void) +{ + return new EmTransportSocket (*this); +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::ConfigSocket::GetTransport + * + * DESCRIPTION: Return any transport object currently using the port + * specified in the given configuration. + * + * PARAMETERS: None + * + * RETURNED: Any found transport object. May be NULL. + * + ***********************************************************************/ + +EmTransport* EmTransportSocket::ConfigSocket::GetTransport (void) +{ + return EmTransportSocket::GetTransport (*this); +} + + +/*********************************************************************** + * + * FUNCTION: EmTransportSocket::ConfigSocket::operator== + * + * DESCRIPTION: Compare two Config objects to each other + * + * PARAMETERS: other - the object to compare "this" to. + * + * RETURNED: True if the objects are equivalent. + * + ***********************************************************************/ + +bool EmTransportSocket::ConfigSocket::operator==(const ConfigSocket& other) const +{ + return + fTargetHost == other.fTargetHost && + fTargetPort == other.fTargetPort; +} + + +#pragma mark - + +/*********************************************************************** + * + * FUNCTION: CTCPClientSocket::CTCPClientSocket + * + * DESCRIPTION:CTPClientSocket c'tor + * + * PARAMETERS: + * + * RETURNED: Nothing + * + ***********************************************************************/ + +enum { kSocketState_Unconnected, kSocketState_Listening, kSocketState_Connected }; + +CTCPClientSocket::CTCPClientSocket (EventCallback fn, string targetHost, int targetPort, EmTransportSocket* transport) : + CTCPSocket (fn, targetPort) +{ + fTargetHost = targetHost; + fTransport = transport; +} + + +/*********************************************************************** + * + * FUNCTION: CTCPClientSocket::~CTCPClientSocket + * + * DESCRIPTION: + * + * PARAMETERS: + * + * RETURNED: + * + ***********************************************************************/ + +CTCPClientSocket::~CTCPClientSocket () +{ +} + + +/*********************************************************************** + * + * FUNCTION: CTCPClientSocket::Open + * + * DESCRIPTION: Open a socket and try to establish a connection to a + * TCP target. + * + * PARAMETERS: None. + * + * RETURNED: errNone if a connection has been established, or a + * nonzero error code otherwise + * + ***********************************************************************/ + +ErrCode CTCPClientSocket::Open (void) +{ + PRINTF ("CTCPClientSocket(0x%08X)::Open...", this); + + EmAssert (fSocketState == kSocketState_Unconnected); + EmAssert (fConnectedSocket == INVALID_SOCKET); + + sockaddr addr; + int result; + + fConnectedSocket = this->NewSocket (); + if (fConnectedSocket == INVALID_SOCKET) + { + PRINTF ("...NewSocket failed for connecting socket; result = %08X", this->GetError ()); + return this->ErrorOccurred (); + } + + // Attempt to connect to that address (in case anyone is listening). + result = connect (fConnectedSocket, this->FillAddress (&addr), sizeof(addr)); + if (result == 0) + { + PRINTF ("...connected!"); + + fSocketState = kSocketState_Connected; + return errNone; + } + // if the connection was unsuccessful, this should be logged as well + else + { + PRINTF ("...unable to connect: %d", result); + } + + closesocket (fConnectedSocket); + fConnectedSocket = INVALID_SOCKET; + + return this->ErrorOccurred (); +} + + +/*********************************************************************** + * + * FUNCTION: CTCPClientSocket::OpenInServerMode + * + * DESCRIPTION: + * + * PARAMETERS: None. + * + * RETURNED: errNone if a connection has been established, or a + * nonzero error code otherwise + * + ***********************************************************************/ + +ErrCode CTCPClientSocket::OpenInServerMode (void) +{ + int result; + sockaddr addr; + + fListeningSocket = this->NewSocket (); + if (fListeningSocket == INVALID_SOCKET) + { + PRINTF ("...NewSocket failed for listening socket; result = %08X", this->GetError ()); + return this->ErrorOccurred (); + } + + result = bind (fListeningSocket, this->FillLocalAddress (&addr), sizeof(addr)); + if (result != 0) + { + PRINTF ("...bind failed; result = %08X", this->GetError ()); + return this->ErrorOccurred (); + } + + // Start listening for a connection. + + result = listen (fListeningSocket, 1); + if (result != 0) + { + PRINTF ("...listen failed; result = %08X", this->GetError ()); + return this->ErrorOccurred (); + } + + PRINTF ("...listening for connection"); + + fSocketState = kSocketState_Listening; + + return errNone; +} + + +/*********************************************************************** + * + * FUNCTION: CTCPClientSocket::FillLocalAddress + * + * DESCRIPTION: Fill in a sockaddr data structure with values + * appropriate for connecting from the outside. This is an + * Internet address on the local machine. + * + * PARAMETERS: addr - the sockaddr to fill in. + * + * RETURNED: The same sockaddr passed in. + * + ***********************************************************************/ + +sockaddr* CTCPClientSocket::FillLocalAddress (sockaddr* addr) +{ + sockaddr_in* addr_in = (sockaddr_in*) addr; + +#ifdef HAVE_SIN_LEN + addr_in->sin_len = sizeof (*addr_in); +#endif + addr_in->sin_family = AF_INET; + addr_in->sin_port = htons (fPort); + addr_in->sin_addr.s_addr = htonl (INADDR_ANY); + + return addr; +} + + +/*********************************************************************** + * + * FUNCTION: CTCPClientSocket::GetOwner + * + * DESCRIPTION:This creates a link between a CTCPClientSocket object and + * a EmTransportSocket object. + * + * PARAMETERS: None + * + * RETURNED: Associated transport object, as passed to c'tor + * + ***********************************************************************/ + +EmTransportSocket* CTCPClientSocket::GetOwner (void) +{ + return fTransport; +} + + +/*********************************************************************** + * + * FUNCTION: CTCPClientSocket::FillAddress + * + * DESCRIPTION: Fill in a sockaddr data structure with values + * appropriate for connecting from the outside. This is an + * Internet address on the local machine. + * + * PARAMETERS: addr - the sockaddr to fill in. + * + * RETURNED: The same sockaddr passed in. + * + ***********************************************************************/ + +sockaddr* CTCPClientSocket::FillAddress (sockaddr* addr) +{ + PRINTF ("CTCPSocket(0x%08X)::FillAddress...", this); + + sockaddr_in* addr_in = (sockaddr_in*) addr; + const char* name = fTargetHost.c_str (); + unsigned long ip; + + // Check for common "localhost" case in order to avoid a name lookup on the Mac + + if (!_stricmp(name,"localhost")) + { + ip = htonl(INADDR_LOOPBACK); + } + else + { + // Try decoding a dotted ip address string + ip = inet_addr(name); + + if (ip == INADDR_NONE) + { + hostent* entry; + + // Perform a DNS lookup + entry = gethostbyname(name); + + if (entry) + { + ip = *(unsigned long*) entry->h_addr; + } + } + } + +#ifdef HAVE_SIN_LEN + addr_in->sin_len = sizeof (*addr_in); +#endif + addr_in->sin_family = AF_INET; + addr_in->sin_port = htons (fPort); + addr_in->sin_addr.s_addr = ip; + + return addr; +} |