I am trying to set up a Client, Router, Server in C++ with sockets and UDP using GoBackN windowing. I'm not sure why I'm getting a buffer overrun error in Client::write_log(), at the fprintf() line.
Main run() function
void Client::run()
{
srand ( time(NULL) );
//socket data types
SOCKET client_socket; // Client socket
SOCKADDR_IN sa_out; // fill with server info, IP, port
char buffer[RAWBUF_SIZE]; // Buffer
WSADATA wsadata; // WSA connection
char router[11]; // Host data
char cusername[128], filename[128], direction[3]; // Other header data
DWORD dwusername = sizeof(cusername); // Retains the size of the username
char trace_data[128];
FILE* logfile = fopen("client.log", "w");
try
{
if (WSAStartup(0x0202,&wsadata)!=0)
{
throw "Error in starting WSAStartup";
}
else
{
/* Display the wsadata structure */
cout<< endl
<< "wsadata.wVersion " << wsadata.wVersion << endl
<< "wsadata.wHighVersion " << wsadata.wHighVersion << endl
<< "wsadata.szDescription " << wsadata.szDescription << endl
<< "wsadata.szSystemStatus " << wsadata.szSystemStatus << endl
<< "wsadata.iMaxSockets " << wsadata.iMaxSockets << endl
<< "wsadata.iMaxUdpDg " << wsadata.iMaxUdpDg << endl << endl;
}
client_socket = open_port(PEER_PORT2);
prompt("Enter the router hostname: ",router);
sa_out = prepare_peer_connection(router, ROUTER_PORT2);
prompt("Enter a filename: ",filename); // Retrieve a filename from the client
prompt("Direction of transfer [get|put]: ", direction); // Retrieve a transfer direction
// Make sure the direction is one of get or put
if(!strcmp(direction,GET) || !strcmp(direction,PUT))
{
// Retrieve the local user name
GetUserName(cusername, &dwusername);
int selected = rand() % 256;
int received, verify;
int client_num = 0; // Client packet number
int server_num = 0; // Server packet number
int progress = 0;
int rcv;
cout << "Starting packet ID negotiation..." << endl;
while(1)
{
// Send a random number to the server
if(progress < 1)
{
memset(buffer, 0, sizeof(buffer));
sprintf(buffer,"RAND %d",selected);
cout << "Sending " << buffer << endl;
if((rcv = send_safe(client_socket, sa_out, buffer, RAWBUF_SIZE, 200)) == 201)
{
progress = 1;
}
else if(rcv != 200)
{
continue;
}
// Finally wait for a response from the server with the number
if(recv_safe(client_socket, sa_out, buffer, RAWBUF_SIZE, 100) == 100)
{
cout << "Received " << buffer << endl;
sscanf(buffer,"RAND %d %d",&verify,&received);
}
else
continue;
progress = 1;
}
// Send acknowledgement to the server along with our random number
memset(buffer, 0, sizeof(buffer));
sprintf(buffer, "RAND %d", received);
cout << "Sending " << buffer << endl;
if(send_safe(client_socket, sa_out, buffer, RAWBUF_SIZE, 201) != 201)
{
progress = 0;
continue;
}
break;
}
client_num = selected % WINDOW_SIZE + 1;
server_num = received % WINDOW_SIZE + 1;
cout << "Negotiated server start " << server_num << " and client start " << client_num << endl;
sprintf(trace_data, "Negotiated srv %d and cli %d", server_num, client_num);
write_log(logfile, cusername, trace_data);
// Send client headers
sprintf(buffer, HEADER, cusername, direction, filename);
while((rcv = send_safe(client_socket,sa_out,buffer,RAWBUF_SIZE,777)) != 777)
{
if(rcv == 101)
break;
}
// Perform a get request
if(!strcmp(direction,GET))
{
get(client_socket, sa_out, cusername, filename, client_num, server_num, logfile);
}
else if(!strcmp(direction,PUT))
{
put(client_socket, sa_out, cusername, filename, client_num, server_num, logfile);
}
}
else
{
throw "The method you requested does not exist, use get or put";
}
}
catch (const char *str)
{
cerr << str << WSAGetLastError() << endl;
}
//close the client socket and clean up
fclose(logfile);
closesocket(client_socket);
WSACleanup();
}
PUT() CODE
void Client::put(SOCKET s, SOCKADDR_IN sa, char * username, char* filename, int client_num, int server_num, FILE* logfile)
{
char window[FRAME_SIZE * WINDOW_SIZE]; // data retention window
char buffer[FRAME_SIZE]; // send buffer
int filesize;
int size = 0, sent = 0; // Trace variables
char tracebuf[128];
FILE* send_file;
fopen_s(&send_file, filename, "rb");
if(send_file != NULL)
{
// open the file
// Determines the file size
fseek(send_file, 0L, SEEK_END);
filesize = ftell(send_file);
fseek(send_file, 0L, SEEK_SET);
//write filesize to logfile
sprintf(tracebuf, "Filesize %d", filesize);
write_log(logfile, username, tracebuf);
//add file size to buffer
strncpy_s(buffer, "SIZ", 3);
memcpy(buffer + (3 * sizeof(char)), &filesize, sizeof(int));
//if filesize was sent to server successfully
if(send_safe(s,sa,buffer,FRAME_SIZE,101) == 101)
{
cout << "Sent filesize, starting transfer..." << endl;
//reset buffer
memset(buffer, 0, sizeof(buffer));
int count = 0;
int offset = 0;
int frames_outstanding = 0; //for frames not ACKED
int next = 0;
bool resend = false;
int packet_id;
int pid_max = WINDOW_SIZE + 1;
// Start sending the file
while (1)
{
// If the acks mismatch with the current send offset and there are frames not ACKED, this has to be a resend
if(next != offset && frames_outstanding > 0)
resend = true;
// Send as many frames as available for the given window size
while((!feof(send_file) && frames_outstanding < WINDOW_SIZE) || resend)
{
if(next == offset)
resend = false;
if(!resend)
{
if(feof(send_file))
break;
fread(buffer,1,FRAME_SIZE,send_file); // Read the next block of data
memcpy(window + (offset * FRAME_SIZE), buffer, FRAME_SIZE); // Store the data in the local window
//memcpy(window, buffer, FRAME_SIZE); // Store the data in the local window
send_packet(s, sa, buffer, FRAME_SIZE, offset); // Send the packet to peer
offset = (offset + 1) % pid_max; // Update the offset
frames_outstanding++;
}
else
{
// Resend by copying the data from the window
memcpy(buffer, window + (next * FRAME_SIZE), FRAME_SIZE);
send_packet(s, sa, buffer, FRAME_SIZE, next);
//log
sprintf(tracebuf, "Resending packet %d", next);
write_log(logfile, username, tracebuf);
next = (next + 1) % pid_max;
}
}
// Receive ACKs before continuing sending
//while the # of unacked frames are greater than 0
while(frames_outstanding > 0)
{
//if packet id < 0
if((packet_id = recv_packet(s,sa,buffer,FRAME_SIZE,next)) < 0)
{
if(count < filesize)
resend = true;
break;
}
// Receive acknowledgment from the client
if(!strncmp(buffer,"NAK", 3))
{
if(packet_id >= 0)
next = packet_id; // Set the next packet to the packet id
break;
}
else if(!strncmp(buffer,"ALL", 3))
{
frames_outstanding = 0;
break;
}
//log
sprintf(tracebuf, "Sent %d bytes", count);
write_log(logfile, username, tracebuf);
memset(buffer, 0, sizeof(buffer)); // Zero the buffer
next = (next + 1) % pid_max; // Update the next frame tracker
frames_outstanding--; // Another frame has been acked
}
if(feof(send_file) && frames_outstanding == 0)
break; // Break when done reading the file and all frames are acked
}
cout << "File transfer completed" << endl;
fclose(send_file);
}
else
{
fclose(send_file);
return put(s,sa,username,filename, client_num, server_num, logfile);
}
}
}
WRITE_LOG() CODE
void Client::write_log(FILE* logfile, char* username, char* message)
{
fprintf(logfile, "%s > %s\n", username, message);
memset(message, 0, sizeof(message));
}
Not at all sure what you actually intdended to do here:
memset(message, 0, sizeof(message));
but it's likely not doing what you intended. sizeof(message) is either 4 or, more likely, 8 on a modern system. So you are writing 8 zeros to message. Which may not be 8 bytes long in the first place.
Possibly you want:
memset(message, 0, strlen(message));
or:
message[0] = 0;
(Which will do set the string to "empty", but not fill the entire thing with zeros).
Related
I sent data to socket
int index = 0, ret;
while(size) {
if((ret = send(sock, &buffer[index], size, 0)) <= 0)
return (!ret) ? index : -1;
index += ret;
size -= ret;
}
cout <<"send_sock:" << buffer << endl;
prints: send_sock:HTTP/1.1 200 OK
I received data from socket
n = read(infd, buf, sizeof buf);
cout << buf << endl;
Prints:~�������HTTP/1.1 200 OK
I'm having an issue with winsock on windows 8.1 where recv keeps returning 0 randomly. I'm running both client and server on the same machine (thus all traffic is pointed at the loopback address) and i have breakpoints on any statement on both client and server which would shut down the sockets. But when the issue occurs the server is still operating normally, and hasn't shutdown anything, while the client has hit a breakpoint that only triggers on recv returning 0 or less.
The client keeps returning 0 from recv randomly (although always at the same point in my code) when the server VS 2013 project is set to compile as a windows program (rather than a console, in order to make it produce no window, it's supposed to be silent running). The bug doesn't seem to occur when i compile the server as a console application, as I've been debugging the program in that mode and only come across this issue when i switched compilation settings.
Is there any way to launch a console window when compiling as a windows application so i can see any debug statements?
Does winsock behave differently when compiling for the console vs compiling for a windows application?
Why would the client's recv return 0, when I've not sent a shutdown signal from the server?
My code, ask if there's any more you need:
Client
void recvLoop()
{
int recievedBytes = 1;
while (running && recievedBytes > 0)
{
WORD dataSize;
WORD dataType;
int recievedBytesA = ConnectSock.Recieve(&dataSize, sizeof(WORD));
if (recievedBytesA <= 0)
{
closing = true; //breakpoint set here
attemptKillThreads();
continue;
}
int recievedBytesB = ConnectSock.Recieve(&dataType, sizeof(WORD));
if (recievedBytesB <= 0)
{
closing = true; //breakpoint set here
attemptKillThreads();
continue;
}
unique_ptr<char[]> data(new char[dataSize]);
int recievedBytesC = ConnectSock.Recieve(data.get(), dataSize);
if (recievedBytesC <= 0)
{
closing = true; //breakpoint set here - Always triggers here
attemptKillThreads();
continue;
}
//use the received data.....
}
}
When this breaks recievedBytesA = 2, recievedBytesB = 2, recievedBytesC = 0, dataType = 0, dataSize = 0
ConnectSock is a global of type ConnectSocket. here is its Recieve()
int ConnectSocket::Recieve(void *recvbuf, int recvbuflen)
{
if (sock != INVALID_SOCKET)
{
int i = recv(sock, (char *)recvbuf, recvbuflen, 0);
if ((i == SOCKET_ERROR))
{
int err = 0;
err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
}
return i;
}
return -2;
}
Server:
void sendLoop()
{
int bytessent = 0;
QueuePack tosend;
while (running)
{
tosend = sendQueue.Dequeue();
if (tosend.packType == QueuePack::EXIT || tosend.packType == 0 || tosend.dSize == 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(&tosend.dSize, sizeof(WORD));
//cout used to see what exactly is being sent, even if it is garbage when converted to text
cout << tosend.dSize << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(&tosend.packType, sizeof(WORD));
cout << tosend.dSize << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(tosend.bufferPtr(), tosend.dSize);
cout << tosend.bufferPtr() << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
}
}
if (Connection->shutdownSock(SD_SEND) == SOCKET_ERROR)
{
Connection->closeSock();
}
}
SendData is literally a wrapper for send that uses a reinterpret_cast
int SendData(void * writeBuffer, int bufferSize)
{
return send(SocketManager.clientSocket, reinterpret_cast<char *>(writeBuffer), bufferSize, 0);
}
sendQueue is a Bounded blocking queue that holds QueuePacks
QueuePacks are used to transfer data, it's size and what kind of data it is between threads. both Client and server use this as it allows me to make sure data gets to the right thread on the client
Queuepack has 2 public variables packType and dSize of type WORD.
QueuePack::QueuePack() : packType(UND), dSize(0)
{
int h = 0; //debug statement to break on - never gets hit after initial collection construction occurs
}
QueuePack::QueuePack(const WORD type, WORD size, char * data) : packType(type), dSize(size)
{
//debug test and statement to break on
if (size == 0 || type == 0)
{
int h = 0; //breakpoint - never gets hit
}
dSize = (dSize < 1 ? 1 : dSize);
_buffer = make_unique<char[]>(dSize);
memcpy(_buffer.get(), data, dSize);
}
QueuePack::QueuePack(QueuePack &other) : packType(other.packType), dSize(other.dSize)
{
//debug test and statement to break on
if (other.dSize == 0 || other.packType == 0)
{
int h = 0; //breakpoint - never gets hit
}
if (&other == this)
{
return;
}
_buffer = make_unique<char[]>(dSize);
other.buffer(_buffer.get());
}
QueuePack QueuePack::operator= (QueuePack &other)
{
// check for self-assignment
if (&other == this)
{
return *this;
}
// reuse storage when possible
if (dSize != other.dSize)
{
_buffer.reset(new char[other.dSize]);
dSize = other.dSize;
}
packType = other.packType;
other.buffer(_buffer.get());
return *this;
}
QueuePack::~QueuePack()
{
}
HRESULT QueuePack::buffer(void* container)
{
try
{
memcpy(container, _buffer.get(), dSize);
}
catch (...)
{
return E_FAIL;
}
return S_OK;
}
char * QueuePack::bufferPtr()
{
return _buffer.get();
}
When this breaks recievedBytesA = 2, recievedBytesB = 2, recievedBytesC = 0, dataType = 0, dataSize = 0
You are calling ConnectSock.Recieve() when dataSize is 0. There is nothing to read, so Receive() reports that 0 bytes were read.
You need to add a check for that condition:
unique_ptr<char[]> data(new char[dataSize]);
if (dataSize != 0) // <-- add this
{
int recievedBytesC = ConnectSock.Recieve(data.get(), dataSize);
if (recievedBytesC <= 0)
{
closing = true;
attemptKillThreads();
continue;
}
}
Your code is also assuming that Recieve() reads all bytes that are requested, it is not handling the possibility that recv() may return fewer bytes. So you need to make Recieve() call recv() in a loop to guarantee that everything requested is actually read:
int ConnectSocket::Recieve(void *recvbuf, int recvbuflen)
{
if (sock == INVALID_SOCKET)
return -2;
char *buf = static_cast<char *>(recvbuf);
int total = 0;
while (recvbuflen > 0)
{
int i = recv(sock, buf, recvbuflen, 0);
if (i == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
return -1;
}
if (i == 0)
{
cout << "disconnected" << endl;
return 0;
}
buf += i;
recvbuflen -= i;
total += i;
}
return total;
}
Same with SendData(), as send() may return fewer bytes than requested:
int SendData(void * writeBuffer, int bufferSize)
{
if (SocketManager.clientSocket == INVALID_SOCKET)
return -2;
char *buf = static_cast<char *>(writeBuffer);
int total = 0;
while (bufferSize > 0)
{
int i = send(SocketManager.clientSocket, buf, bufferSize, 0);
if (i == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
return -1;
}
buf += i;
bufferSize -= i;
total += i;
}
return total;
}
when I want to use the same socket connected to the host to send() and recv() for the second time, recv() will return 0 without anything in buffer
basically Im doing:
connect to the website
send packets
receive packets
send packets again (I think this one is working since it's not giving me SOCKET_ERROR)
receive packets again (this one is returning 0 so as "connection closed")
source code: http://pastebin.com/sm5k5GAe
as you can see, I have sock2, which when I use for second send/recv it's working fine, however I will do more communication, and having sockets for all of that and connecting them would be kinda stupid I guess
The only 2 conditions that cause recv() to return 0 are:
if you provided it with a 0-length buffer.
if the other party has gracefully closed the connection on its end. You can use a packet sniffer, such as Wireshark, to verify this.
You are sending HTTP 1.1 requests that include a Connection: close header in them. That tells an HTTP server to close its end of the connection after sending the response. That is perfectly normal behavior. Simply read the response, close your end of the connection, and reconnect before sending the next request.
If you want to keep the connection open so you can send multiple requests over a single connection, send a Connection: keep-alive header instead, or just omit the Connection header altogether since you are sending HTTP 1.1 requests and keep-alive is the default behavior for HTTP 1.1.
Either way, you must look at the server's actual response Connection header to know if it is going to close its end of the connection or not. For an HTTP 0.9 or 1.0 response, if there is no Connection: keep-alive header present then you must assume the connection is going to be closed. For an HTTP 1.1 response, if there is no Connection: close header then you must assume the connection is being left open. But you do have to be prepared to handle the possibility that the connection may still be closed, such as by a intermediate router/firewall, so simply reconnect if the next request fails with a connection error.
With that said, you are better off not implementing HTTP manually, but instead using a pre-made library that handles these details for you, such as libcurl.
Update:
Try something more like this instead:
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <fstream>
using namespace std;
SOCKET sock;
string currentHost;
bool checkConnection(string Hostname);
int readFromSock(char *data, int datalen, bool disconnectOK = false);
int readData(string &workBuffer, char *data, int datalen, bool disconnectOK = false);
bool readLine(string &workBuffer, string &line);
bool doRequest(string Hostname, string Request, string &Reply);
bool GetReply(string Host, string &Reply);
bool GetLogin(string Reply, string MyStr, string &Login);
bool GetSession(string Reply, string MyStr, string &Session);
bool GetLoginReply(string Host, string Username, string Password, string Login, string Session, string &Reply);
bool Login(string Host, string Resource, string Username, string Password);
bool IsConnected(string Reply, string MyStr);
#pragma comment (lib, "ws2_32.lib")
#pragma warning(disable:4996)
int main()
{
string sLoginSite, sUsername, sPassword;
char buffer[32];
//Initialize Winsock
WSADATA WsaData;
if(WSAStartup(MAKEWORD(2,2), &WsaData) != 0)
{
cout << "WinSock Startup Failed" << endl;
system("pause");
return 1;
}
sock = INVALID_SOCKET;
//Get Login Site
cout << "Travian login site e.g. ts1.travian.com: ";
cin >> buffer;
sLoginSite = buffer;
cout << endl;
//Get Username
cout << "Username: ";
cin >> buffer;
sUsername = buffer;
//Get Password
cout << "Password: ";
cin >> buffer;
sPassword = buffer;
cout << endl;
// Perform Login
if (!Login(sLoginSite, sUsername, sPassword))
{
cout << "Error while Logging in" << endl;
system("pause");
return 1;
}
cout << "Successfully connected to the account \"" << sUsername << "\" at " << sLoginSite << endl;
system("pause");
if (sock != INVALID_SOCKET)
closesocket(sock);
WSACleanup();
return 0;
}
// ensure connection to Hostname
bool checkConnection(string Hostname)
{
// Switching to a different hostname? If so, disconnect...
if (currentHost != Hostname)
{
if (sock != INVALID_SOCKET)
{
closesocket(sock);
sock = INVALID_SOCKET;
}
currentHost = Host;
}
// TODO: make sure the socket is actually still connected. If not, disconnect...
if (sock != INVALID_SOCKET)
{
/*
if (no longer connected)
{
closesocket(sock);
sock = INVALID_SOCKET;
}
*/
}
// Create a new connection?
if (sock == INVALID_SOCKET)
{
// resolve the Hostname...
struct hostent *host = gethostbyname(Hostname.c_str());
if(!host)
{
cout << "Can't Resolve Hostname" << endl;
return false;
}
// Connect to the Hostname...
SOCKET newSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (newSock == INVALID_SOCKET)
{
cout << "Error creating Socket" << endl;
return false;
}
SOCKADDR_IN SockAddr;
ZeroMemory(&SockAddr, sizeof(SockAddr)));
SockAddr.sin_port = htons(80);
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
cout << "Connecting" << endl;
if(connect(newSock, (SOCKADDR*) &SockAddr, sizeof(SockAddr)) != 0)
{
closesocket(newSock);
cout << "Can't Connect to Hostname" << endl;
return false;
}
sock = newSock;
cout << "Successfully connected to " << Hostname << "!" << endl;
}
// Ready
return true;
}
// read raw data from the socket directly
// returns how many bytes were actually read, -1 on error, or 0 on disconnect
int readFromSock(char *data, int datalen, bool disconnectOK)
{
int read = 0;
while (datalen > 0)
{
// more data is expected...
int ret = recv(sock, data, datalen, 0);
if (ret == SOCKET_ERROR)
{
cout << "recv failed: " << WSAGetLastError() << endl;
closesocket(sock);
sock = INVALID_SOCKET;
return -1;
}
if (ret == 0)
{
cout << "server disconnected" << endl;
closesocket(sock);
sock = INVALID_SOCKET;
// if the caller is OK with a disconnect occurring, exit without error...
if (disconnectOK)
break;
return -1;
}
// move forward in the output buffer
data += ret;
datalen -= ret;
// increment the result value
read += ret;
}
// done
return read;
}
// read raw data from an in-memory buffer, reading from the socket directly only when the buffer is empty.
// returns how many bytes were actually read, -1 on error, or 0 on disconnect
int readData(string &workBuffer, char *data, int datalen, bool disconnectOK)
{
int read = 0;
int len;
char buffer[512];
while (datalen > 0)
{
// more data is expected...
len = workBuffer.length();
if (len > 0)
{
// the work buffer has cached data, move to the output buffer...
if (len > datalen) len = datalen;
workBuffer.copy(data, len);
workBuffer.erase(len);
// move forward in the output buffer
data += len;
datalen -= len;
// increment the return value
read += len;
}
else
{
// the work buffer is empty, read from the socket and cache it...
len = readFromSock(buffer, sizeof(buffer), disconnectOK);
if (ret == -1)
return -1;
// disconnected?
if (ret == 0)
break;
// append new data to the work buffer...
workBuffer += string(buffer, ret);
}
}
// done
return read;
}
// reads a LF-delimited line of text from an in-memory buffer, reading from the socket directly only when the buffer is empty.
// returns whether a full line was actually read.
bool readLine(string &workBuffer, string &line)
{
// clear the output...
line = "";
int found, len, start = 0;
char buffer[512];
do
{
// check if a LF is already cached. Ignore previously searched text..
found = workBuffer.find("\n", start);
if (found != string::npos)
{
len = found;
// is the LF preceded by a CR? If so, do include it in the output...
if (len > 0)
{
if (workBuffer[len-1] == '\r')
--len;
}
// output the line, up to but not including the CR/LF...
line = workBuffer.substr(0, len);
workBuffer.erase(found);
break;
}
// the work buffer does not contain a LF, read from the socket and cache it...
len = readFromSock(buffer, sizeof(buffer));
if (len <= 0)
{
closesocket(sock);
sock = INVALID_SOCKET;
return false;
}
// append new data to the work buffer and search again...
start = workBuffer.length();
workBuffer += string(buffer, len);
}
while (true);
// done
return true;
}
// perform an HTTP request and read the reply
// returns whether the reply was actually read
bool doRequest(string Hostname, string Request, string &Reply)
{
// clear the output
Reply = "";
char buffer[512];
string str, workBuffer;
string sContentLength, sTransferEncoding, sConnection;
bool doClose;
// make sure there is a connection, reconnecting if needed...
if (!checkConnection(Hostname))
return false;
// send the request...
char *data = Request.c_str();
int len = Request.length();
int ret;
do
{
ret = send(sock, data, len, 0);
if (ret == SOCKET_ERROR)
{
cout << "Send Error" << endl;
closesocket(sock);
sock = INVALID_SOCKET;
return false;
}
// move forward in the input buffer...
data += ret;
len -= ret;
}
while (len > 0);
// read the response's status line...
if (!readLine(workBuffer, str))
return false;
// TODO: parse out the line's values, ie: "200 OK HTTP/1.1"
int ResponseNum = ...;
int VersionMajor = ...;
int VersionMinor = ...;
// only HTTP 1.0 responses have headers...
if (VersionMajor >= 1)
{
// read the headers until a blank line is reached...
do
{
// read a header
if (!readLine(workBuffer, str))
return false;
// headers finished?
if (str.length() == 0)
break;
// TODO: do case-insensitive comparisons
if (str.compare(0, 15, "Content-Length:") == 0)
sContentLength = str.substr(15);
else if (str.compare(0, 18, "Transfer-Encoding:") == 0)
sTransferEncoding = str.substr(18);
else if (str.compare(0, 18, "Connection:") == 0)
sConnection = str.substr(11);
}
while (true);
// If HTTP 1.0, the connection must closed if the "Connection" header is not "keep-alive"
// If HTTP 1.1+, the connection must be left open the "Connection" header is not "close"
// TODO: do case-insensitive comparisons
if ((VersionMajor == 1) && (VersionMinor == 0))
doClose = (sConnection.compare"keep-alive") != 0);
else
doClose = (sConnection.compare("close") == 0);
}
else
{
// HTTP 0.9 or earlier, no support for headers or keep-alives
doClose = true;
}
// TODO: do case-insensitive comparisons
if (sTransferEncoding.compare(sTransferEncoding.length()-7, 7, "chunked") == 0)
{
// the response is chunked, read the chunks until a blank chunk is reached...
do
{
// read the chunk header...
if (!readLine(workBuffer, str))
return false;
// ignore any extensions for now...
int found = str.find(";");
if (found != string::npos)
str.resize(found);
// convert the chunk size from hex to decimal...
size = strtol(str.c_str(), NULL, 16);
// chunks finished?
if (size == 0)
break;
// read the chunk's data...
do
{
len = size;
if (len > sizeof(buffer)) len = sizeof(buffer);
if (!readData(workBuffer, buffer, len))
return false;
// copy the data to the output
Reply += string(buffer, len);
size -= len;
}
while (size > 0);
// the data is followed by a CRLF, skip it...
if (!readLine(workBuffer, str))
return false;
}
while (true);
// read trailing headers...
do
{
// read a header...
if (!readLine(workBuffer, str))
return false;
// headers finished?
if (str.length() == 0)
break;
// process header as needed, overwriting HTTP header if needed...
}
while (true);
}
else if (sContentLength.length() != 0)
{
// the response has a length, read only as many bytes as are specified...
// convert the length to decimal...
len = strtol(sContentLength.c_str(), NULL, 10);
if (len > 0)
{
// read the data...
do
{
ret = len;
if (ret > sizeof(buffer)) ret = sizeof(buffer);
ret = readData(workBuffer, buffer, ret);
if (ret <= 0)
return false;
// copy the data to the output
Reply += string(buffer, ret);
len -= ret;
}
while (len > 0);
}
}
else
{
// response is terminated by a disconnect...
do
{
len = readData(workBuffer, buffer, sizeof(buffer), true);
if (len == -1)
return false;
// disconnected?
if (len == 0)
break;
// copy the data to the output
Reply += string(buffer, len);
}
while (true);
doClose = true;
}
// close the socket now?
if (doClose)
{
closesocket(sock);
sock = INVALID_SOCKET;
}
// TODO: handle other responses, like 3xx redirects...
return ((ResponseNum / 100) == 2);
}
// Login to Hostname with Username and Password
// returns whether login was successful or not
bool Login(string Hostname, string Username, string Password)
{
string sLoginForm, sReply, sSessionID, sLoginID, sLoginReply;
//Get Login Form HTML
if (!GetReply(Hostname, sReply))
{
cout << "Reply Error" << endl;
return false;
}
//Save Reply
ofstream Data;
Data.open("Reply.txt");
Data << sReply;
Data.close();
//Get Session ID from HTML
if (!GetSession(sReply, "sess_id", sSessionID))
{
cout << "Session ID Error" << endl;
return false;
}
//Get Login ID from HTML
if (!GetLogin(sReply, "<input type=\"hidden\" name=\"login\" value=", sLoginID))
{
cout << "Login ID Error" << endl;
return false;
}
// perform final Login
if (!GetLoginReply(Hostname, Username, Password, sLoginID, sSessionID, sLoginReply))
{
cout << "Login Reply Error" << endl;
return false;
}
/*
if(!IsConnected(sLoginReply, "HTTP/1.1 303 See Other"))
{
cout << "Invalid Username or Password" << endl;
return false;
}
*/
//Save Reply
Data.open("login.txt");
Data << sLoginReply;
Data.close();
// done
return true;
}
bool GetReply(string Hostname, string &Reply)
{
string str;
str = "GET / HTTP/1.1\r\n";
str += "Host: " + Hostname + "\r\n"
str += "Connection: keep-alive\r\n"
str += "\r\n";
return doRequest(Hostname, str, Reply);
}
bool GetSession(string Reply, string MyStr, string &Session)
{
int found = Reply.find(MyStr);
if(found == string::npos)
return false;
Session = Reply.substr(found+MyStr.Length(), 32);
return true;
}
bool GetLogin(string Reply, string MyStr, string &Login)
{
int found = Reply.find(MyStr);
if(found == string::npos)
return false;
Login = Reply.substr(found+MyStr.length(), 10)
return true;
}
bool GetLoginReply(string Hostname, string Username, string Password, string Login, string Session, string &Reply)
{
string str, special;
special = "name=" + Username + "&password=" + Password + "&s1=Login&w=1600%3A900&login=" + Login;
string temp;
stringstream ss;
ss << special.length();
ss >> temp;
str = "POST /dorf1.php HTTP/1.1\r\n";
str += "Host: " + Hostname + "\r\n";
str += "Cookie: sess_id=" + Session + "; highlightsToggle=false; WMBlueprints=%5B%5D\r\n";
str += "Content-Type: application/x-www-form-urlencoded\r\n";
str += "Content-Length: " + temp + "\r\n"
str += "\r\n";
str += special;
return doRequest(Hostname, str, Reply);
}
bool IsConnected(string Reply, string MyStr)
{
return (Reply.find(MyStr) != string::npos);
}
Strange. I can make GET requests using the included code on my local machine, and it works like a charm. From the local machine command line, using GET -Ue 192.168.2.106:8129 I get
200 OK
Content-Length: 296
Content-Type: text/html
Client-Date: Sat, 09 Feb 2013 20:26:09 GMT
Client-Response-Num: 1
<html><body><h1>Directory</h1><a href=/.>.</a><br/><a href=/..>..</a><br/><a href=/bldg.jpg>bldg.jpg</a><br/><a href=/hello.txt>hello.txt</a><br/><a href=/world.gif>world.gif</a><br/><a href=/index.html>index.html</a><br/><a href=/testing>testing</a><br/><a href=/favicon.ico>favicon.ico</a><br/>
Wheras from the remote machine I get
200 OK
Content-Length: 328
Content-Type: text/html
Client-Aborted: die
Client-Date: Sat, 09 Feb 2013 20:25:16 GMT
Client-Response-Num: 1
X-Died: read failed: Connection reset by peer at /usr/share/perl5/LWP/Protocol/http.pm line 414.
The second one looks like it wants to work, but the wonky "X-Died" header is odd. I have a sneaking suspicion that my GetLine method is off, but I'm not quite sure. Any idea what could be causing this issue?
Main function:
int main(int argc, char* argv[])
{
// First set up the signal handler
struct sigaction sigold, signew;
signew.sa_handler = handler;
sigemptyset(&signew.sa_mask);
sigaddset(&signew.sa_mask, SIGPIPE);
signew.sa_flags = SA_RESTART;
sigaction(SIGPIPE, &signew, &sigold);
int hSocket,hServerSocket; /* handle to socket */
struct hostent* pHostInfo; /* holds info about a machine */
struct sockaddr_in Address; /* Internet socket address stuct */
int nAddressSize=sizeof(struct sockaddr_in);
char pBuffer[BUFFER_SIZE];
int nHostPort;
q = new myQueue();
// THREAD POOL!
pthread_t thread_id[NUMTHREADS];
int i=0;
for(i=0; i < NUMTHREADS; i++)
{
threadParams param;
param.a = i;
pthread_create(&thread_id[i], 0, &WorkerHandler, NULL);
}
if(argc < 2)
{
printf("\nUsage: server host-port\n");
return 0;
}
else
{
nHostPort = atoi(argv[1]);
}
printf("\nStarting server");
printf("\nMaking socket");
/* make a socket */
hServerSocket=socket(AF_INET,SOCK_STREAM,0);
if(hServerSocket == SOCKET_ERROR)
{
printf("\nCould not make a socket\n");
return 0;
}
/* fill address struct */
Address.sin_addr.s_addr=INADDR_ANY;
Address.sin_port=htons(nHostPort);
Address.sin_family=AF_INET;
printf("\nBinding to port %d",nHostPort);
int optval = 1;
setsockopt(hServerSocket, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
/* bind to a port */
if(bind(hServerSocket,(struct sockaddr*)&Address,sizeof(Address)) == SOCKET_ERROR)
{
printf("\nCould not connect to host\n");
return 0;
}
/* get port number */
getsockname( hServerSocket, (struct sockaddr *) &Address,(socklen_t *)&nAddressSize);
printf("opened socket as fd (%d) on port (%d) for stream i/o\n",hServerSocket, ntohs(Address.sin_port) );
printf("Server\n\
sin_family = %d\n\
sin_addr.s_addr = %d\n\
sin_port = %d\n"
, Address.sin_family
, Address.sin_addr.s_addr
, ntohs(Address.sin_port));
/* establish listen queue */
if(listen(hServerSocket, 1000) == SOCKET_ERROR)
{
printf("\nCould not listen\n");
return 0;
}
while(1)
{
int socket = accept(hServerSocket, (struct sockaddr*)&Address, (socklen_t *)&nAddressSize);
q->enqueue(socket);
}
}
void handler (int status)
{
}
Handle requests on the socket:
int handleRequest(int socket)
{
cout << "handling a request..." << endl;
// Get the URL
char * line = GetLine(socket);
//char * line = "GET /bldg.jpg HTTP/1.1";
char * url;
url = strtok(line, " "); // Splits spaces between words in str
url = strtok(NULL, " "); // Splits spaces between words in str
// build filename
ostringstream fullpathstream;
fullpathstream << dirpath << url;
string fullpath = fullpathstream.str();
//Use the stat function to get the information
struct stat fileAtt;
if (stat(fullpath.c_str(), &fileAtt) != 0) //start will be 0 when it succeeds
{
cout << "File not found:" << fullpath.c_str() << endl;
writeError(socket);
return 0;
}
// Make correct filetypes
if (S_ISREG (fileAtt.st_mode))
{
printf ("%s is a regular file.\n", fullpath.c_str());
char * contentType;
if(strstr(url, ".jpg"))
{
cout << "JPEG" << endl;
contentType = "image/jpg";
}
else if(strstr(url, ".gif"))
{
contentType = "image/gif";
cout << "GIF" << endl;
}
else if(strstr(url, ".html"))
{
contentType = "text/html";
cout << "HTML" << endl;
}
else if(strstr(url, ".txt"))
{
cout << "TXT" << endl;
contentType = "text/plain";
}
else if(strstr(url, ".ico"))
{
cout << "ICO" << endl;
contentType = "image/ico";
}
serveFile(fullpath, socket, fileAtt.st_size, contentType);
}
if (S_ISDIR (fileAtt.st_mode))
{
printf ("%s is a directory.\n", fullpath.c_str());
serveDirectory(socket, url);
}
return 0;
};
GetLine function for getting the first line of the request so I knowt the requested URL:
// Read the line one character at a time, looking for the CR
// You dont want to read too far, or you will mess up the content
char * GetLine(int fds)
{
char tline[MAX_MSG_SZ];
char *line;
int messagesize = 0;
int amtread = 0;
while((amtread = read(fds, tline + messagesize, 1)) < MAX_MSG_SZ)
{
if (amtread > 0)
messagesize += amtread;
else
{
perror("Socket Error is:");
fprintf(stderr, "Read Failed on file descriptor %d messagesize = %d\n", fds, messagesize);
exit(2);
}
//fprintf(stderr,"%d[%c]", messagesize,message[messagesize-1]);
if (tline[messagesize - 1] == '\n')
break;
}
tline[messagesize] = '\0';
chomp(tline);
line = (char *)malloc((strlen(tline) + 1) * sizeof(char));
strcpy(line, tline);
//fprintf(stderr, "GetLine: [%s]\n", line);
return line;
}
I have multithreading happening, and this is where I close my sockets.
void * WorkerHandler(void *arg)
{
threadParams* params = (threadParams*)arg;
while(1)
{
int socket = q->dequeue();
handleRequest(socket);
if(close(socket) == SOCKET_ERROR)
{
printf("\nCould not close socket\n");
return 0;
}
}
}
I am having a lot of trouble tracking down where I am getting a BufferOverrun in the code below when I send it socket data from my flash/as3 telnet client. I am mostly looking to find more memory leaks and such, so in addition to this problem, is there an easier way to check for buffer overflow/overrun than manual debugging?
/*
MTSocketServer
--------------
The purpose of this application is to have a client application that can connect to this server.
This server should be able to parse messages from connected users and to send them to other users.
*/
// Headers and Namespace
#include "stdafx.h"
#include "MTSocketServer.h"
using namespace std;
// Constants
#define BUFFERSIZE 1000
#define PORT 20248
// Global Variables
SOCKET server;
struct Client_Data
{
SOCKET client_socket;
string user_id;
};
list<Client_Data> clients;
// Method Prototypes
UINT Server_Thread(LPVOID pParams);
UINT Client_Thread(LPVOID pParams);
string Recv_String(SOCKET listener);
void Send_String(const char* message, SOCKET sender);
bool Identity_Exists(string id);
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
AfxBeginThread(Server_Thread, NULL);
while(_getch() != 27);
closesocket(server);
WSACleanup();
return 0;
}
UINT Server_Thread(LPVOID pParams)
{
// Spawn Server Thread
cout << "Main thread spawned, press ESC at any time to exit.\r\n";
WSADATA wsa_data;
SOCKADDR_IN server_location;
if (int wsa_return_val = WSAStartup(0x101, &wsa_data) != 0)
{
cout << "Incorrect version of 'Ws2_32.dll'.\r\n";
cout << "Client thread de-initialized.\r\n";
AfxEndThread(0, true);
return 1;
}
server_location.sin_family = AF_INET;
server_location.sin_addr.s_addr = INADDR_ANY;
server_location.sin_port = htons(20248);
server = socket(AF_INET, SOCK_STREAM, NULL);
if (server == INVALID_SOCKET)
{
cout << "Socket creation error.\r\n";
cout << "Client thread de-initialized.\r\n";
AfxEndThread(0, true);
return 1;
}
if (bind(server, (SOCKADDR*)&server_location, sizeof(server_location)) != 0)
{
cout << "Socket binding error.\r\n";
cout << "Client thread de-initialized.\r\n";
AfxEndThread(0, true);
return 1;
}
if (listen(server, 10) != 0)
{
cout << "Socket listening error.\r\n";
cout << "Client thread de-initialized.\r\n";
AfxEndThread(0, true);
return 1;
}
SOCKET client;
SOCKADDR_IN client_location;
int len_of_client = sizeof(client_location);
bool abort = false;
while(abort == false)
{
client = accept(server, (SOCKADDR*)&client_location, &len_of_client);
// Spawn Client Thread
cout << "Connection from " << inet_ntoa(client_location.sin_addr) << ". Client thread spawned.\r\n";
AfxBeginThread(Client_Thread, (LPVOID)client);
}
// De-Initialize Server Thread
cout << "Server thread de-initialized.\r\n";
AfxEndThread(0, true);
return 0;
}
UINT Client_Thread(LPVOID pParams)
{
SOCKET client = (SOCKET)pParams;
string client_id = "";
// Check if a client with the same ID is on already
send(client, "ID_ME", 6, 0);
client_id = Recv_String(client);
if (Identity_Exists(client_id))
{
Send_String("That ID is already taken. Please try another.", client);
} else
{
cout << "Created ID " << client_id << " successfully\r\n";
Send_String("Type 'send' to send a message, 'quit' to exit.", client);
Client_Data this_client;
this_client.user_id = client_id;
this_client.client_socket = client;
clients.push_back(this_client);
bool is_con = true;
while(is_con)
{
string response = Recv_String(client);
if (response == "send")
{
Send_String("What username to send to?", client);
string to_id = Recv_String(client);
if (Identity_Exists(to_id))
{
Send_String("Type your message below: ", client);
string temp = Recv_String(client) + "\n\0";
const char* message = temp.c_str();
for (list<Client_Data>::iterator iterator = clients.begin(), end = clients.end(); iterator != end; ++iterator)
{
if (to_id == iterator->user_id)
{
SOCKET temp = iterator->client_socket;
cout << temp;
Send_String(message, temp);
}
}
} else
{
Send_String("Invalid username specified", client);
}
}
else if (response == "quit")
{
Send_String("Quit Command Issued", client);
break;
}
else
{
Send_String("Invalid Command Issued", client);
}
}
}
// De-Initialize Client Thread
closesocket(client);
cout << "Client thread de-initialized.\r\n";
AfxEndThread(0, true);
return 0;
}
// Function to parse full string values since they are often recieved one at a time
string Recv_String(SOCKET listener)
{
char buffer[BUFFERSIZE];
string temp;
int numofbytes = 0;
while (true)
{
int num = recv(listener, buffer, BUFFERSIZE, 0);
numofbytes++;
buffer[num] = '\0';
if (buffer[num-1] == '\n')
break;
if(numofbytes >= BUFFERSIZE-1)
break;
temp += buffer;
strcpy(buffer, "");
}
return temp;
}
void Send_String(const char* message, SOCKET sender)
{
char buffer[BUFFERSIZE];
strcpy(buffer, message);
size_t size = strlen(message);
int sendtest = send(sender, buffer, size, 0);
strcpy(buffer, "");
}
bool Identity_Exists(string id)
{
for (list<Client_Data>::iterator iterator = clients.begin(), end = clients.end(); iterator != end; ++iterator)
{
if (id == iterator->user_id)
{
return true;
}
}
return false;
}
buffer[num] = '\0'; writes one beyond the buffer if num happens to be equal to BUFFERSIZE. It writes one below the buffer if num happens to be -1.
if (buffer[num-1] == '\n') though not a buffer overrun, is also wrong. The \n can be anywhere (from 0 to n-1) in the buffer.