Skip to content

Instantly share code, notes, and snippets.

@M-griffin
Last active August 29, 2015 14:17
Show Gist options
  • Save M-griffin/9ba8daa95a4bc5e60f62 to your computer and use it in GitHub Desktop.
Save M-griffin/9ba8daa95a4bc5e60f62 to your computer and use it in GitHub Desktop.
Experimental Windows Telnet Server [Forks Processes and Pipes STDIO from Console like Linux]
/*
Michael Griffin <mrmisticismo@hotmail.com>
Free for use and modification
A Dirty (incomplete) Rewrite of ddtelnetd for windows to test forking and piping stdio
Works by connecting then forking to CMD.exe, will also work with any
generic console application
Originally complied in DEVC++ for Windows, should work with gcc/mingw32
*/
#include "socket.h" // For Socket, ServerSocket, and SocketException
#include <vector>
#include <algorithm>
#include <iostream> // For cerr and cout
#include <sstream>
#include <cstdlib> // For atoi()
#include <windows.h>
#include <cstdio>
#include <conio.h>
#include <pthread.h> // For POSIX threads
#pragma hdrstop
using namespace std;
// Processes
#define TA_FAILED 0
#define TA_SUCCESS_CLEAN 1
#define TA_SUCCESS_KILL 2
#define TA_SUCCESS_16 3
// Telnet
#define BUFSIZE 4096
#define OUTFD 1
#define INFD 0
#define FALSE 0
#define TRUE 1
#define IAC '\xFF'
#define SE '\xF0'
#define GA '\xF9'
#define SB '\xFA'
#define WILL '\xFB'
#define WONT '\xFC'
#define DO '\xFD'
#define DONT '\xFE'
#define EC 247 /* erase the current character */
#define TELOPT_OLD_ENVIRON 36 /* Old - Environment variables */
#define TELOPT_SGA 3 /* suppress go ahead */
#define TELOPT_ECHO 1 /* echo */
#define TELOPT_NAWS 31 /* window size */
#define TELOPT_BINARY 0 /* 8-bit data path */
static char *argv_init[] = {NULL, NULL, NULL, NULL, NULL};
const unsigned int RCVBUFSIZE = 4096; // Size of receive buffer
unsigned short echoServPort = 23; // Telnet
#define bzero(a) memset(a,0,sizeof(a)) //easier -- shortcut
struct Connection {
TCPSocket *clntSock;
char acBuffer[RCVBUFSIZE];
int nCharsInBuffer;
Connection(TCPSocket *clntSock_) : clntSock(clntSock_), nCharsInBuffer(0) { }
};
typedef vector<Connection> ConnectionList;
typedef struct
{
DWORD dwID ;
DWORD dwThread ;
} TERMINFO ;
//ConnectionList gConnections;
bool IsWinNT() //check if we're running NT
{
OSVERSIONINFO osv;
osv.dwOSVersionInfoSize = sizeof(osv);
GetVersionEx(&osv);
return (osv.dwPlatformId == VER_PLATFORM_WIN32_NT);
}
void ErrorMessage(char *str) //display detailed error info
{
LPVOID msg;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &msg,
0,
NULL
);
printf("%s: %s\n",str,msg);
LocalFree(msg);
}
int send_iac(unsigned char command,
int option,
TCPSocket *sock)
{
char buf[3];
int w;
buf[0] = IAC;
buf[1] = command;
buf[2] = option;
// w = write(OUTFD, buf, option == -1 ? 2 : 3);
return w;
}
int remove_iacs(unsigned char* in,
int len,
unsigned char* out,
int* out_count,
TCPSocket *sock) {
unsigned char *end = in + len;
unsigned char* orig_in = in;
static int ignore = FALSE;
while (in < end)
{
if (*in != IAC)
{
if(ignore == FALSE)
{
*out = *in; in++; out++;
*out_count = *out_count + 1;
}
else
{
in++;
}
}
else
{
if((in+1) == end)
{
*orig_in = IAC;
return 1;
}
if(ignore && *(in+1) != SE)
{
send_iac(*(in+1), -1, sock);
in += 2;
continue;
}
cout << "got real IAC." << *(in+1) << " " << *(in+2) << endl;
switch((int)*(in+1)) {
case IAC:
*out = IAC; ++out;
*out_count = *out_count + 1;
in += 2;
break;
case WILL:
case WONT:
case DO:
case DONT:
if((in+2) == end)
{
*orig_in = IAC;
*(orig_in+1) = *(end-1);
return 2;
}
in += 3;
break;
case SB:
ignore = TRUE;
cout << "Negotiation started." << endl;
in += 2;
break;
case SE:
ignore = FALSE;
cout << "Negotiation completed." << endl;
in += 2;
break;
/*
case NOP:
case DM:
case BREAK:
case IP:
case AO:
case AYT:
case GA:
in += 2;
break; */
case EC:
in += 2;
*out = '\b';
++out;
*out_count = *out_count + 1;
break;
default:
cout << "Unknown IAC arg: " << *(in+1) << endl;
in += 2;
break;
}
}
}
return 0;
}
BOOL CALLBACK TerminateAppEnum( HWND hwnd, LPARAM lParam )
{
DWORD dwID ;
GetWindowThreadProcessId(hwnd, &dwID) ;
if(dwID == (DWORD)lParam)
{
PostMessage(hwnd, WM_CLOSE, 0, 0) ;
}
return TRUE ;
}
// Kill Process once threads and sockets are killed.
DWORD WINAPI TerminateApp( DWORD dwPID, DWORD dwTimeout )
{
HANDLE hProc ;
DWORD dwRet ;
// If we can't open the process with PROCESS_TERMINATE rights,
// then we give up immediately.
hProc = OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE,
dwPID);
if(hProc == NULL)
{
return TA_FAILED ;
}
// TerminateAppEnum() posts WM_CLOSE to all windows whose PID
// matches your process's.
EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM) dwPID) ;
// Wait on the handle. If it signals, great. If it times out,
// then you kill it.
if(WaitForSingleObject(hProc, dwTimeout)!=WAIT_OBJECT_0)
dwRet=(TerminateProcess(hProc,0)?TA_SUCCESS_KILL:TA_FAILED);
else
dwRet = TA_SUCCESS_CLEAN ;
CloseHandle(hProc) ;
return dwRet ;
}
// Main Thread / Process Loop.
void HandleTCPAndPipes(TCPSocket *sock)
{
STARTUPINFO si;
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd; //security information for pipes
PROCESS_INFORMATION pi;
HANDLE newstdin,newstdout,read_stdout,write_stdin; //pipe handles
if (IsWinNT()) //initialize security descriptor (Windows NT)
{
InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, true, NULL, false);
sa.lpSecurityDescriptor = &sd;
}
else
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = true; //allow inheritable handles
if (!CreatePipe(&newstdin,&write_stdin,&sa,0)) //create stdin pipe
{
ErrorMessage((char *)"CreatePipe stdin");
//getch();
return;
}
if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) //create stdout pipe
{
ErrorMessage((char *)"CreatePipe stdout");
//getch();
CloseHandle(newstdin);
CloseHandle(write_stdin);
return;
}
GetStartupInfo(&si); //set startupinfo for the spawned process
//The dwFlags member tells CreateProcess how to make the process.
//STARTF_USESTDHANDLES validates the hStd* members. STARTF_USESHOWWINDOW
//validates the wShowWindow member.
//
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdOutput = newstdout;
si.hStdError = newstdout; //set the new handles for the child process
si.hStdInput = newstdin;
char app_spawn[] = "C:\\Windows\\System32\\cmd.exe"; // Program to execute on command line.
//spawn the child process
if (!CreateProcess(app_spawn,NULL,NULL,NULL,TRUE,CREATE_NEW_CONSOLE,
NULL,NULL,&si,&pi))
{
ErrorMessage((char *)"CreateProcess child process");
CloseHandle(newstdin);
CloseHandle(newstdout);
CloseHandle(read_stdout);
CloseHandle(write_stdin);
return;
}
unsigned long exit=0; //process exit code
unsigned long bread; //bytes read
unsigned long avail; //bytes available
// Socket
char echoBuffer[RCVBUFSIZE];
unsigned char buf[RCVBUFSIZE];
unsigned char tmpbuf[RCVBUFSIZE];
unsigned int recvMsgSize;
int iacs;
int new_offset = 0;
int out_count = 0;
unsigned char *ptr1, *ptr2;
bzero(buf);
for(;;) //main program loop
{
GetExitCodeProcess(pi.hProcess,&exit); //while the process is running
if (exit != STILL_ACTIVE || !sock->isConnected())
break;
BOOL b = PeekNamedPipe(read_stdout,buf,RCVBUFSIZE,&bread,&avail,NULL);
//printf( "-peek res=%d avail=%d bread=%lu\n", b, avail, bread );
//check to see if there is any data to read from stdout
if (bread != 0)
{
bzero(buf);
bzero(tmpbuf);
if (avail > RCVBUFSIZE)
{
while (bread >= RCVBUFSIZE) { //& sock->isConnected())
//cout << "read_stdout1" << endl;
// Read From Piped Program to std_out, pass to std_out and Socket
ReadFile(read_stdout,tmpbuf,RCVBUFSIZE,&bread,NULL); //read the stdout pipe
/*
ptr1 = tmpbuf;
ptr2 = buf;
iacs = 0;
while(ptr1 != tmpbuf + recvMsgSize && ptr2 != buf + recvMsgSize) {
if(*ptr1 == IAC) {
*ptr2 = IAC; ptr2++;
*ptr2 = IAC; ptr2++;
ptr1++; iacs++;
}
else {
*ptr2 = *ptr1;
ptr1++; ptr2++;
}
}*/
// Now Sent to Socket
//sock->send(buf, + iacs);
if (sock->send(tmpbuf, strlen((char *)tmpbuf)) == SOCKET_ERROR) break;
printf("%s",tmpbuf);
bzero(tmpbuf);
bzero(buf);
}
// if (!sock->isConnected()) break;
}
else
{
//cout << "read_stdout2" << endl;
ReadFile(read_stdout,tmpbuf,RCVBUFSIZE,&bread,NULL);
/*
ptr1 = tmpbuf;
ptr2 = buf;
iacs = 0;
while(ptr1 != tmpbuf + recvMsgSize && ptr2 != buf + recvMsgSize) {
if(*ptr1 == IAC) {
*ptr2 = IAC; ptr2++;
*ptr2 = IAC; ptr2++;
ptr1++; iacs++;
}
else {
*ptr2 = *ptr1;
ptr1++; ptr2++;
}
}*/
// Zero means
// Echo message back to client
// sock->send(buf, recvMsgSize + iacs);
// if (!sock->isConnected()) break;
if (sock->send(tmpbuf, bread) == SOCKET_ERROR) break;
printf("%s",tmpbuf);
bzero(tmpbuf);
bzero(buf);
//sock->send(buf, RCVBUFSIZE);
}
}
// Grab from socketm and pass to Pipe or program
//if (recvMsgSize = sock->recv(buf, RCVBUFSIZE) > 0)
//cout << "sock->isConnected ?" << endl;
// recvMsgSize = sock->recv(buf + new_offset, BUFSIZE - new_offset);
bzero(buf);
while (sock->isConnected())
{ // Zero means
recvMsgSize = sock->recv(buf, RCVBUFSIZE);
if (recvMsgSize == 0)
{
cout << "Error recv: " << recvMsgSize << endl;
break;
}
// No Message Continue On
if (recvMsgSize == WSAEWOULDBLOCK)
break;
if (buf[0] == 13)
sprintf((char *)buf,"\r\n");
// Echo message back to client
WriteFile(write_stdin,buf,strlen((char *)buf),&bread,NULL); //send an extra newline char,
if (sock->send(buf, recvMsgSize) == SOCKET_ERROR) break;
cout << buf;
bzero(buf);
Sleep(10);
}
// Lost Connection == 0;
if (recvMsgSize == 0) break; // || !sock->isConnected()) break;
// }
/*
if (recvMsgSize = sock->recv(buf + new_offset, BUFSIZE - new_offset) > 0)
{
//cout << "sock->recv!" << endl;
new_offset = remove_iacs(buf, recvMsgSize + new_offset, tmpbuf, (int *)&recvMsgSize, sock);
// Write to Console and Program.
WriteFile(write_stdin,tmpbuf,out_count,&bread,NULL); //send an extra newline char,
//cout << tmpbuf;
}
else {
cout << "INVALID_SOCKET!" << endl;
break;
}*/
//cout << "Continue!" << endl;
Sleep(30);
}
//need to term process if still running in a clean way.
//
//PostThreadMessage(pi.dwThreadId, WM_CLOSE, ...
//Or you can use TerminateProcess(pi.hProcess, 0)
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
CloseHandle(newstdin); //clean stuff up
CloseHandle(newstdout);
CloseHandle(read_stdout);
CloseHandle(write_stdin);
// Terminate process Application, if fail try just process.
if (TerminateApp(pi.dwProcessId,0) == TA_FAILED)
cout << "Terminate App Failed!" << endl;
else
cout << "Terminate App Success!" << endl;
// TerminateProcess(pi.hProcess, 0);
}
// TCP client handling function
void HandleTCPClient(TCPSocket *sock)
{
cout << "Handling client " << endl;
// New Addresses
std::ostringstream foreignConnection;
try
{
foreignConnection << sock->getForeignAddress();
}
catch (SocketException e)
{
cerr << "Unable to get foreign address" << endl;
}
foreignConnection << " : ";
try
{
foreignConnection << (short)sock->getForeignPort();
}
catch (SocketException e)
{
cerr << "Unable to get foreign port" << endl;
}
cout << foreignConnection.str() << endl;
// Send Default Telnet Options to Client.
if(send_iac(DONT, TELOPT_OLD_ENVIRON, sock) < 0)
{
exit(1);
}
if(send_iac(DO, TELOPT_SGA, sock) < 0)
{
exit(1);
}
if(send_iac(WILL, TELOPT_ECHO, sock) < 0)
{
exit(1);
}
if(send_iac(WILL, TELOPT_SGA, sock) < 0)
{
exit(1);
}
if(send_iac(WONT, TELOPT_NAWS, sock) < 0)
{
exit(1);
}
if(send_iac(WILL, TELOPT_BINARY, sock) < 0)
{
exit(1);
}
if(send_iac(DO, TELOPT_BINARY, sock) < 0)
{
exit(1);
}
// Loops Pipes and Sockets
HandleTCPAndPipes(sock);
// Done with Loop, Return back to ThreadMain
cout << "Connection Ended: " << foreignConnection.str() << " Socket Closed()." << endl;
}
void *ThreadMain(void *clntSock)
{
// Guarantees that thread resources are deallocated upon return
pthread_detach(pthread_self());
cout << " with thread " << pthread_self().p << endl;
// Extract socket file descriptor from argument
HandleTCPClient((TCPSocket *) clntSock);
delete (TCPSocket *) clntSock;
return NULL;
}
//---------------------------------------------------------------------------
int main(int argc, char *argv[])
{
SetConsoleTitle( "Enthral [WIN32] Telnetd Server Beta 1 (c) 2011 Michael Griffin." );
//ConnectionList gConnections;
char buf[1024]={0};
try
{
// Socket descriptor for server
TCPServerSocket servSock(echoServPort);
cout << "Server Stated, Waiting for a Connection" << endl;
for (;;)
{ // Run forever
// Create separate memory for client argument
bzero(buf);
if (kbhit()) //check for user input.
{
bzero(buf);
*buf = (char)getch();
//printf("%c",*buf);
if (*buf == '\r' or *buf == '\n')
{
*buf = '\n';
printf("%c",*buf);
//if necessary
bzero(buf);
}
else if (*buf == 27) // Escape
{
cout << "ESC Hit, Closing All Sockets.. Exiting Program!" << endl;
// Loop through sctive Connections.
/*
ConnectionList::iterator it = gConnections.begin();
while (it != gConnections.end()) {
cout << "gConnections->cleanUp() " << endl;
bool bOK = false;
if (!bOK)
{
// Something bad happened on the socket, or the
// client closed its half of the connection. Shut
// the conn down and remove it from the list.
// not closing socket in thread properly!!!
if (it->clntSock != NULL) {
cout << "gConnection clntSock->Shutdown()." << endl;
it->clntSock->Shutdown();
cout << "gConnection delete" << endl;
delete it->clntSock;
}
gConnections.erase(it);
it = gConnections.begin();
cout << "gConnection closed." << endl;
}
else
{
// Go on to next connection
++it;
}
}
*/
cout << "servSock->cleanUp() " << endl;
servSock.cleanUp();
break;
// Make Sure Process is closed or completed!
}
}
// cout << "servSock.accept()" << endl;
// Setup Client Socket and Add to List.
TCPSocket *clntSock = servSock.accept();
if ((int)clntSock != INVALID_SOCKET) // SOCKET_ERROR {
{
// Tell user we accepted the socket, and add it to
// our connecition list.
// Sleep
Sleep(100);
// If CLient Connects then Start new Thread
cout << "clntSock->isConnected()" << endl;
// Don't really need gconnections just yet.
//gConnections.push_back(Connection(clntSock));
/*
if ((gConnections.size() + 1) > 64)
{
// For the background on this check, see
// www.tangentsoft.net/wskfaq/advanced.html#64sockets
// The +1 is to account for the listener socket.
cout << "WARNING: More than 63 client "
"connections accepted. This will not "
"work reliably on some Winsock "
"stacks!" << endl;
}*/
// Create client thread
pthread_t threadID; // Thread ID from pthread_create()
if (pthread_create(&threadID, NULL, ThreadMain, (void *)clntSock) != 0)
{
cerr << "Unable to create thread" << endl;
throw SocketException("pthread_create() failed.", true);
//exit(1);
}
}
//else
//throw SocketException("clntSock INVALID_SOCKET failed.", true);
Sleep(10);
}
}
catch (SocketException &e)
{
cerr << e.message() << endl;
system("PAUSE");
exit(1);
}
cout << "Shutdown Completed!" << endl;
Sleep(2000);
return 0;
}
#include "socket.h"
#include <iostream> // debugging
#include <winsock.h> // For socket(), connect(), send(), and recv()
typedef int socklen_t;
typedef char raw_type; // Type used for raw data on this platform
using namespace std;
static bool initialized = false;
// SocketException Code
SocketException::SocketException(const string &message, bool inclSysMsg)
throw() : userMessage(message)
{
if (inclSysMsg)
{
userMessage.append(": ");
userMessage.append(strerror(errno));
}
}
SocketException::~SocketException() throw()
{
}
const char *SocketException::message() const throw()
{
return userMessage.c_str();
}
// Function to fill in address structure given an address and port
static void fillAddr(const string &address,
unsigned short port,
sockaddr_in &addr)
{
memset(&addr, 0, sizeof(addr)); // Zero out address structure
addr.sin_family = AF_INET; // Internet address
hostent *host; // Resolve name
if ((host = gethostbyname(address.c_str())) == NULL)
{
// strerror() will not work for gethostbyname() and hstrerror()
// is supposedly obsolete
throw SocketException("Failed to resolve name (gethostbyname())");
}
addr.sin_addr.s_addr = *((unsigned long *) host->h_addr_list[0]);
addr.sin_port = htons(port); // Assign port in network byte order
}
// Socket Code
Socket::Socket(int type, int protocol)
throw(SocketException)
{
if (!initialized)
{
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 0); // Request WinSock v2.0
if (WSAStartup(wVersionRequested, &wsaData) != 0) { // Load WinSock DLL
throw SocketException("Unable to load WinSock DLL");
}
initialized = true;
}
// Make a new socket
if ((sockDesc = socket(AF_INET, type, protocol)) < 0)
{
throw SocketException("Socket creation failed (socket())", true);
}
}
Socket::Socket(int sockDesc)
{
this->sockDesc = sockDesc;
}
Socket::~Socket() {
// Don't Close on Server Socket Already Cleaned up!
if (sockDesc != -1)
{
cout << "shutdown" << endl;
// Shutdown();
cout << "closesocket" << endl;
::closesocket(sockDesc);
sockDesc = -1;
}
}
string Socket::getLocalAddress()
throw(SocketException)
{
sockaddr_in addr;
unsigned int addr_len = sizeof(addr);
if (getsockname(sockDesc, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0)
{
throw SocketException("Fetch of local address failed (getsockname())", true);
}
return inet_ntoa(addr.sin_addr);
}
unsigned short Socket::getLocalPort()
throw(SocketException)
{
sockaddr_in addr;
unsigned int addr_len = sizeof(addr);
int ret = 0;
if (ret = getsockname(sockDesc, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0)
{
cout << "ret: " << ret << endl;
throw SocketException("Fetch of local port failed (getsockname())", true);
}
return ntohs(addr.sin_port);
}
void Socket::setLocalPort(unsigned short localPort)
throw(SocketException)
{
// Bind the socket to its port
sockaddr_in localAddr;
memset(&localAddr, 0, sizeof(localAddr));
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
localAddr.sin_port = htons(localPort);
// DISABLE NAGLE
int t = 1;
if (setsockopt( sockDesc, IPPROTO_TCP, TCP_NODELAY, (const char *)&t, sizeof(int) ) != 0)
throw SocketException("setsockopt failed (setsockopt(NAGLE))", true);
// SET reuse address
t = 1;
if (setsockopt( sockDesc, SOL_SOCKET, SO_REUSEADDR, (const char *)&t, sizeof(int) ) != 0)
throw SocketException("setsockopt failed (setsockopt(REUSE))", true);
/// t = 1;
/// if (setsockopt( sockDesc, SOL_SOCKET, SO_LINGER, (const char *)&t, sizeof(int) ) != 0)
/// throw SocketException("setsockopt failed (setsockopt(SO_LINGER))", true);
// Set Non-Blocking
u_long iMode = 1;
int ret = 0;
if (ret = ioctlsocket( sockDesc, FIONBIO, &iMode ) == SOCKET_ERROR )
{
cout << "ret: " << ret << endl;
throw SocketException("sockDesc SERVER Socket creation failed (setsockopt(Non-Blocking))", true);
}
if (ret = bind(sockDesc, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0)
{
cout << "ret: " << ret << endl;
throw SocketException("Set of local port failed (bind())", true);
}
}
void Socket::setLocalAddressAndPort(const string &localAddress,
unsigned short localPort)
throw(SocketException)
{
// Get the address of the requested host
sockaddr_in localAddr;
fillAddr(localAddress, localPort, localAddr);
// DISABLE NAGLE
int t = 1;
if (setsockopt( sockDesc, IPPROTO_TCP, TCP_NODELAY, (const char *)&t, sizeof(int) ) != 0)
throw SocketException("Socket creation failed (setsockopt(NAGLE))", true);
// SET reuse address
t = 1;
if (setsockopt( sockDesc, SOL_SOCKET, SO_REUSEADDR, (const char *)&t, sizeof(int) ) != 0)
throw SocketException("Socket creation failed (setsockopt(REUSE))", true);
// Set Non-Blocking
u_long iMode = 1;
int ret = 0;
if (ret = ioctlsocket( sockDesc, FIONBIO, &iMode ) == SOCKET_ERROR )
{
cout << "ret: " << ret << endl;
throw SocketException("sockDesc SERVER Socket creation failed (setsockopt(Non-Blocking))", true);
}
if (bind(sockDesc, (sockaddr *) &localAddr, sizeof(sockaddr_in)) < 0) {
throw SocketException("Set of local address and port failed (bind())", true);
}
}
// Seems to Be Working Now, need to test Values.
int Socket::isConnected() //throw(SocketException) {
{
// Always retuns 1, only real test is call recv!
// Probably a Non-Block Issue.
return 1;
/*
// Check if we can read peer name, if not, no longer connected.
sockaddr_in addr;
unsigned int addr_len = sizeof(addr);
unsigned int ret;
ret = getpeername(sockDesc, (sockaddr *) &addr,(socklen_t *) &addr_len);
cout << "isConnected(): " << ret << endl;
if (ret < 0)
return 0;
else
return 1;
*/
/*
// Quick Test ..
// getpeername(). If the socket is connected, getpeername() will return 0.
// If the socket is not connected, getpeername() will return ENOTCONN
fd_set writeSet;
fd_set exceptSet;
FD_ZERO( &writeSet );
FD_ZERO( &exceptSet );
FD_SET( (unsigned int)sockDesc, &writeSet );
FD_SET( (unsigned int)sockDesc, &exceptSet );
struct timeval nullTime = {0,0};
int _select = select( sockDesc+1, NULL, &writeSet, &exceptSet, &nullTime );
cout << "isConnected(): " << _select << endl;
if( _select < 0 ) {
// throw SocketException("isConnected() failed (_select())", true);
//close();
cout << "isConnected(): Failed " << _select << endl;
return 0;
}
if( FD_ISSET(sockDesc, &exceptSet) ) {
// throw SocketException("isConnected() failed (FD_ISSET())", true);
//close();
cout << "isConnected(): failed (FD_ISSET())" << endl;
return 0;
}
int writeIsSet = FD_ISSET( sockDesc, &writeSet );
if( writeIsSet ) {
// The connect is now officially open
// state &= ~ZO_OPEN_WOULD_BLOCK;
}
else
cout << "isConnected(): failed (writeIsSet())" << writeIsSet << endl;
// else
// throw SocketException("isConnected() failed (writeIsSet())", true);
*/
// return writeIsSet ? 1 : 0;
}
void Socket::Shutdown() { //throw(SocketException) {
unsigned int err;
int errlen = sizeof(err);
getsockopt(sockDesc, SOL_SOCKET, SO_ERROR, (char*)&err, &errlen);
if (err != NO_ERROR) {
cout << "(SO_ERROR) err != NO_ERROR : err =" << err << endl;
//throw SocketException("getsockopt err != NO_ERROR", true);
}
// err = 0;
//err = setsockopt( sockDesc, SOL_SOCKET, SO_LINGER, (const char *)&err, sizeof(int));
//if (err != NO_ERROR) {
// cout << "(SO_LINGER) err != NO_ERROR : err =" << err << endl;
// //throw SocketException("getsockopt err != NO_ERROR", true);
// }
char buffer[4096] ={0};
// Clear Out Read first before closing socket
if (sockDesc = -1)
{
unsigned int rtn = 0;
while (rtn = ::recv(sockDesc, (raw_type *) buffer, 4096, 0) > 0)
{
// 0 is connection lost
cout << "clearing recv (rtn) : " << rtn << endl;
//throw SocketException("Received failed (recv())", true);
}
// Shutdown Send/Receive on Socket
rtn = shutdown(sockDesc, 2);
cout << "shutdown(sockDesc, 2) (rtn) : " << rtn << endl;
//throw SocketException("clntSock Shutting Down SO_ERROR.", true);
}
}
void Socket::cleanUp() //throw(SocketException) {
{
if (WSACleanup() != 0) {
cout << "WSACleanup Failed!" << endl;
//throw SocketException("WSACleanup() failed");
}
else
cout << "WSACleanup Completed!" << endl;
sockDesc = -1;
}
unsigned short Socket::resolveService(const string &service,
const string &protocol) {
struct servent *serv; /* Structure containing service information */
if ((serv = getservbyname(service.c_str(), protocol.c_str())) == NULL)
return atoi(service.c_str()); /* Service is port number */
else
return ntohs(serv->s_port); /* Found port (network byte order) by name */
}
// CommunicatingSocket Code
CommunicatingSocket::CommunicatingSocket(int type, int protocol)
throw(SocketException) : Socket(type, protocol) {
}
CommunicatingSocket::CommunicatingSocket(int newConnSD) : Socket(newConnSD) {
}
void CommunicatingSocket::connect(const string &foreignAddress,
unsigned short foreignPort) throw(SocketException) {
// Get the address of the requested host
sockaddr_in destAddr;
fillAddr(foreignAddress, foreignPort, destAddr);
// Try to connect to the given port
if (::connect(sockDesc, (sockaddr *) &destAddr, sizeof(destAddr)) < 0) {
throw SocketException("Connect failed (connect())", true);
}
}
// No Exceptions, need to pass back Errors, not exit Program!!
int CommunicatingSocket::send(const void *buffer, int bufferLen)
{
//throw(SocketException) {
int rtn;
if (rtn = ::send(sockDesc, (raw_type *) buffer, bufferLen, 0) < 0) {
//throw SocketException("Send failed (send())", true);
}
return rtn;
}
// No Exceptions, need to pass back Errors, not exit Program!!
int CommunicatingSocket::recv(void *buffer, int bufferLen)
{
//throw(SocketException) {
unsigned int rtn = 0;
u_long num_bytes_present = 0;
rtn = ioctlsocket(sockDesc, FIONREAD ,&num_bytes_present);
//cout << "num_bytes_present: " << num_bytes_present << endl;
if ((rtn = ::recv(sockDesc, (raw_type *) buffer, bufferLen, 0)) < 0)
{
//cout << "rtn: " << rtn << endl;
throw SocketException("Received failed (recv())", true);
}
//cout << "rtn: " << rtn << endl;
// Pass Through
if (num_bytes_present == 0 && rtn > num_bytes_present)
return WSAEWOULDBLOCK;
// Here will capture proper input or return dead socket.
return rtn;
}
string CommunicatingSocket::getForeignAddress()
throw(SocketException) {
sockaddr_in addr;
unsigned int addr_len = sizeof(addr);
if (getpeername(sockDesc, (sockaddr *) &addr,(socklen_t *) &addr_len) < 0) {
throw SocketException("Fetch of foreign address failed (getpeername())", true);
}
return inet_ntoa(addr.sin_addr);
}
unsigned short CommunicatingSocket::getForeignPort() throw(SocketException) {
sockaddr_in addr;
unsigned int addr_len = sizeof(addr);
if (getpeername(sockDesc, (sockaddr *) &addr, (socklen_t *) &addr_len) < 0) {
throw SocketException("Fetch of foreign port failed (getpeername())", true);
}
return ntohs(addr.sin_port);
}
// TCPSocket Code
TCPSocket::TCPSocket()
throw(SocketException) : CommunicatingSocket(SOCK_STREAM, IPPROTO_TCP) {
}
TCPSocket::TCPSocket(const string &foreignAddress, unsigned short foreignPort)
throw(SocketException) : CommunicatingSocket(SOCK_STREAM, IPPROTO_TCP) {
connect(foreignAddress, foreignPort);
}
TCPSocket::TCPSocket(int newConnSD) : CommunicatingSocket(newConnSD) {
}
// TCPServerSocket Code
TCPServerSocket::TCPServerSocket(unsigned short localPort, int queueLen)
throw(SocketException) : Socket(SOCK_STREAM, IPPROTO_TCP) {
setLocalPort(localPort);
setListen(queueLen);
}
TCPServerSocket::TCPServerSocket(const string &localAddress,
unsigned short localPort, int queueLen)
throw(SocketException) : Socket(SOCK_STREAM, IPPROTO_TCP) {
setLocalAddressAndPort(localAddress, localPort);
setListen(queueLen);
}
TCPSocket *TCPServerSocket::accept() throw(SocketException) {
int newConnSD;
if ((newConnSD = ::accept(sockDesc, NULL, 0)) < 0) {
//throw SocketException("Accept failed (accept())", true);
//cout << "Accept pass through accept()" << endl;
// NonBlocking, no connecting, pass through.
return (TCPSocket*)SOCKET_ERROR;
}
int t = 1;
if (setsockopt( newConnSD, IPPROTO_TCP, TCP_NODELAY, (const char *)&t, sizeof(int) ) != 0)
throw SocketException("Socket creation failed (setsockopt(NAGLE))", true);
// SET reuse address
t = 1;
if (setsockopt( newConnSD, SOL_SOCKET, SO_REUSEADDR, (const char *)&t, sizeof(int) ) != 0)
throw SocketException("Socket creation failed (setsockopt(REUSE))", true);
// Set Non-Blocking
u_long iMode = 1;
int ret = 0;
if (ret = ioctlsocket( newConnSD, FIONBIO, &iMode ) == SOCKET_ERROR )
{
cout << "ret: " << ret << endl;
throw SocketException("newConnSD Socket creation failed (setsockopt(Non-Blocking))", true);
}
return new TCPSocket(newConnSD);
}
void TCPServerSocket::setListen(int queueLen) throw(SocketException) {
if (listen(sockDesc, queueLen) < 0) {
throw SocketException("Set listening socket failed (listen())", true);
}
}
#ifndef __SOCKET_INCLUDED__
#define __SOCKET_INCLUDED__
#include <string> // For string
#include <exception> // For exception class
using namespace std;
/**
* Signals a problem with the execution of a socket call.
*/
class SocketException : public exception {
public:
/**
* Construct a SocketException with a explanatory message.
* @param message explanatory message
* @param incSysMsg true if system message (from strerror(errno))
* should be postfixed to the user provided message
*/
SocketException(const string &message, bool inclSysMsg = false) throw();
/**
* Provided just to guarantee that no exceptions are thrown.
*/
~SocketException() throw();
/**
* Get the exception message
* @return exception message
*/
const char *message() const throw();
private:
string userMessage; // Exception message
};
/**
* Base class representing basic communication endpoint
*/
class Socket {
public:
/**
* Close and deallocate this socket
*/
~Socket();
/**
* Get the local address
* @return local address of socket
* @exception SocketException thrown if fetch fails
*/
string getLocalAddress() throw(SocketException);
/**
* Get the local port
* @return local port of socket
* @exception SocketException thrown if fetch fails
*/
unsigned short getLocalPort() throw(SocketException);
/**
* Set the local port to the specified port and the local address
* to any interface
* @param localPort local port
* @exception SocketException thrown if setting local port fails
*/
void setLocalPort(unsigned short localPort) throw(SocketException);
/**
* Set the local port to the specified port and the local address
* to the specified address. If you omit the port, a random port
* will be selected.
* @param localAddress local address
* @param localPort local port
* @exception SocketException thrown if setting local port or address fails
*/
void setLocalAddressAndPort(const string &localAddress,
unsigned short localPort = 0) throw(SocketException);
void Shutdown(); //throw(SocketException);
/**
* Socket Connection exists,
* @return 0, 1 - 0 for Error 1 for Connected.
* @exception SocketException thrown When Connection Lost.
*/
int isConnected(); // throw(SocketException);
/**
* If WinSock, unload the WinSock DLLs; otherwise do nothing. We ignore
* this in our sample client code but include it in the library for
* completeness. If you are running on Windows and you are concerned
* about DLL resource consumption, call this after you are done with all
* Socket instances. If you execute this on Windows while some instance of
* Socket exists, you are toast. For portability of client code, this is
* an empty function on non-Windows platforms so you can always include it.
* @param buffer buffer to receive the data
* @param bufferLen maximum number of bytes to read into buffer
* @return number of bytes read, 0 for EOF, and -1 for error
* @exception SocketException thrown WinSock clean up fails
*/
void cleanUp(); //throw(SocketException);
/**
* Resolve the specified service for the specified protocol to the
* corresponding port number in host byte order
* @param service service to resolve (e.g., "http")
* @param protocol protocol of service to resolve. Default is "tcp".
*/
static unsigned short resolveService(const string &service,
const string &protocol = "tcp");
private:
// Prevent the user from trying to use value semantics on this object
Socket(const Socket &sock);
void operator=(const Socket &sock);
protected:
int sockDesc; // Socket descriptor
Socket(int type, int protocol) throw(SocketException);
Socket(int sockDesc);
};
/**
* Socket which is able to connect, send, and receive
*/
class CommunicatingSocket : public Socket {
public:
/**
* Establish a socket connection with the given foreign
* address and port
* @param foreignAddress foreign address (IP address or name)
* @param foreignPort foreign port
* @exception SocketException thrown if unable to establish connection
*/
void connect(const string &foreignAddress, unsigned short foreignPort)
throw(SocketException);
/**
* Write the given buffer to this socket. Call connect() before
* calling send()
* @param buffer buffer to be written
* @param bufferLen number of bytes from buffer to be written
* @exception SocketException thrown if unable to send data
*/
int send(const void *buffer, int bufferLen); // throw(SocketException);
/**
* Read into the given buffer up to bufferLen bytes data from this
* socket. Call connect() before calling recv()
* @param buffer buffer to receive the data
* @param bufferLen maximum number of bytes to read into buffer
* @return number of bytes read, 0 for EOF, and -1 for error
* @exception SocketException thrown if unable to receive data
*/
int recv(void *buffer, int bufferLen); // throw(SocketException);
/**
* Get the foreign address. Call connect() before calling recv()
* @return foreign address
* @exception SocketException thrown if unable to fetch foreign address
*/
string getForeignAddress() throw(SocketException);
/**
* Get the foreign port. Call connect() before calling recv()
* @return foreign port
* @exception SocketException thrown if unable to fetch foreign port
*/
unsigned short getForeignPort() throw(SocketException);
protected:
CommunicatingSocket(int type, int protocol) throw(SocketException);
CommunicatingSocket(int newConnSD);
};
/**
* TCP socket for communication with other TCP sockets
*/
class TCPSocket : public CommunicatingSocket {
public:
/**
* Construct a TCP socket with no connection
* @exception SocketException thrown if unable to create TCP socket
*/
TCPSocket() throw(SocketException);
/**
* Construct a TCP socket with a connection to the given foreign address
* and port
* @param foreignAddress foreign address (IP address or name)
* @param foreignPort foreign port
* @exception SocketException thrown if unable to create TCP socket
*/
TCPSocket(const string &foreignAddress, unsigned short foreignPort)
throw(SocketException);
private:
// Access for TCPServerSocket::accept() connection creation
friend class TCPServerSocket;
TCPSocket(int newConnSD);
};
/**
* TCP socket class for servers
*/
class TCPServerSocket : public Socket {
public:
/**
* Construct a TCP socket for use with a server, accepting connections
* on the specified port on any interface
* @param localPort local port of server socket, a value of zero will
* give a system-assigned unused port
* @param queueLen maximum queue length for outstanding
* connection requests (default 5)
* @exception SocketException thrown if unable to create TCP server socket
*/
TCPServerSocket(unsigned short localPort, int queueLen = 5)
throw(SocketException);
/**
* Construct a TCP socket for use with a server, accepting connections
* on the specified port on the interface specified by the given address
* @param localAddress local interface (address) of server socket
* @param localPort local port of server socket
* @param queueLen maximum queue length for outstanding
* connection requests (default 5)
* @exception SocketException thrown if unable to create TCP server socket
*/
TCPServerSocket(const string &localAddress, unsigned short localPort,
int queueLen = 5) throw(SocketException);
/**
* Blocks until a new connection is established on this socket or error
* @return new connection socket
* @exception SocketException thrown if attempt to accept a new connection fails
*/
TCPSocket *accept() throw(SocketException);
private:
void setListen(int queueLen) throw(SocketException);
};
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment