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);
}
Related
This is my implementation of a soon-to-be HTTP server.
void Webserv::resetFdSets()
{
int fildes;
FD_ZERO(&to_read);
FD_ZERO(&to_write);
max_fd = -1;
Server_map::iterator it;
for (it = servers.begin(); it != servers.end(); ++it) //set server sockets to be read
{
fildes = it->second.getFd();
FD_SET(fildes, &to_read);
if (fildes > max_fd)
max_fd = fildes;
}
std::list<int>::iterator iter;
for (iter = accepted.begin(); iter != accepted.end(); ++iter) // set client sockets if any
{
fildes = (*iter);
FD_SET(fildes, &to_read);
FD_SET(fildes, &to_write);
if (fildes > max_fd)
max_fd = fildes;
}
}
accept()
void Webserv::acceptConnections()
{
int client_fd;
sockaddr_in cli_addr;
socklen_t cli_len = sizeof(sockaddr_in);
Server_map::iterator it;
for (it = servers.begin(); it != servers.end(); ++it)
{
ft_bzero(&cli_addr, sizeof(cli_addr));
if (FD_ISSET(it->second.getFd(), &to_read)) // if theres data in server socket
{
client_fd = accept(it->second.getFd(), reinterpret_cast<sockaddr*>(&cli_addr), &cli_len);
if (client_fd > 0) // accept and add client
{
fcntl(client_fd, F_SETFL, O_NONBLOCK);
accepted.push_back(client_fd);
}
else
throw (std::runtime_error("accept() failed"));
}
}
}
recv()
void Webserv::checkReadSockets()
{
char buf[4096];
std::string raw_request;
ssize_t bytes;
static int connections;
std::list<int>::iterator it;
for (it = accepted.begin(); it != accepted.end(); ++it)
{
if (FD_ISSET(*it, &to_read))
{
++connections;
std::cout << "Connection counter : " << connections << std::endl;
while ((bytes = recv(*it, buf, sizeof(buf) - 1, MSG_DONTWAIT)) > 0)
{
buf[bytes] = '\0';
raw_request += buf;
}
std::cout << raw_request << std::endl;
}
}
}
send()
void Webserv::checkWriteSockets()
{
char buf[8096];
char http_success[] = {
"HTTP/1.1 200 OK\r\n"
"Server: This op server\r\n"
"Content-Length: 580\r\n"
"Content-Type: text/html\r\n"
"Connection: Closed\r\n\r\n"
};
std::list<int>::iterator it = accepted.begin();
while (it != accepted.end())
{
int cliSock = (*it);
if (FD_ISSET(cliSock, &to_write))
{
int response_fd = open("hello.html", O_RDONLY);
int bytes = read(response_fd, buf, sizeof(buf));
send(cliSock, http_success, sizeof(http_success) - 1, MSG_DONTWAIT); // header
send(cliSock, buf, bytes, MSG_DONTWAIT); // hello world
close(cliSock);
close(response_fd);
it = accepted.erase(it);
}
else
++it;
}
}
main loop:
void Webserv::operate()
{
int rc;
while (true)
{
resetFdSets(); // add server and client fd for select()
if ((rc = select(max_fd + 1, &to_read, &to_write, NULL, NULL)) > 0) // if any of the ports are active
{
acceptConnections(); // create new client sockets with accept()
checkReadSockets(); // parse and route client requests recv() ...
checkWriteSockets(); // send() response and delete client
}
else if (rc < 0)
{
std::cerr << "select() failed" << std::endl;
exit(1);
}
}
}
The same code highlighted + Webserv class definition on pastebin: https://pastebin.com/9B8uYumF
For now the algorithm is such:
reset fd_sets for reading and writing.
if any of the file descriptors trigger select, we check whether server FD are set, accept connections and store them in a list of ints.
if any of the client FD are FD_ISSET() to read - we read their request and print it.
finally, if any of the client FD are ready to receive - we dump an html page in there and close the connection.
While checking all the client descriptors FD_ISSET() returns false 80% of the time when it shouldn't. Hence I cannot get client requests consistently. Strangely enough, it returns true with fd_set for write sockets much more often. Here's a demo with 2 out of 10 successful reads using the above code: https://imgur.com/a/nzc21LV
I know that the new clients are always accepted because it always enters the if condition in acceptConnections(). In other words, the listen FD are initialized correctly.
edit: Here's how it's initialized:
void Server::init()
{
if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
throw (std::runtime_error("socket() failed"));
}
fcntl(listen_fd, F_SETFL, O_NONBLOCK);
int yes = 1;
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listen_fd, reinterpret_cast<sockaddr*>(&server_addr), sizeof(server_addr)))
{
throw (std::runtime_error("bind() failed"));
}
if (listen(listen_fd, 0))
throw (std::runtime_error("listen() failed"));
}
I am using windows sockets with c++. In the following call I am trying to reply a message to the socket that just connected.
I tried connecting using a dummy client in c++. It would connect but the recv() would not receive anything.
Then I tried using telnet, it worked instantly, just as i wanted.
SOCKET s = accept(ls, (sockaddr*)&clientSin, &s_len);
if (s == INVALID_SOCKET) {
cerr << "Error in accept call: " << WSAGetLastError();
}
else {
cout << "Connection accepted at , socket no. :" << s << endl;
//adding to list of incoming sockets
inactiveList.push_back(s);
//send message to client requesting credentials
char buff[10];
// the character 'x' is a code to the client to provide the server with the credentials
buff[0] = 'x';
buff[1] = '\0';
//send(s, buff, 2, 0);
if (send(s, "From Vic: ", 10, 0) == INVALID_SOCKET)
{
int errorcode = WSAGetLastError();
cerr << "send to client failed: " << errorcode << endl;
closesocket(s);
continue;
}
Sleep(1000);
if (send(s, "From Vic: ", 10, 0) == INVALID_SOCKET)
{
int errorcode = WSAGetLastError();
cerr << "send to client failed: " << errorcode << endl;
closesocket(s);
continue;
}
}
the recv code is:
tnb = 0;
while ((nb = recv(s, &buff[tnb], LINESZ - tnb, 0)) > 0)
{
tnb += nb;
}
/* If there was an error on the read, report it. */
if (nb < 0)
{
printf("recv failed: %d\n", WSAGetLastError());
return 1;
}
if (tnb == 0)
{
printf("Disconnect on recv");
}
/* Make the response NULL terminated and display it. Using C output */
printf("tnb = %d\n", tnb);
buff[tnb] = '\0';
puts(buff);
Taking all my comments and turning it into an answer.
I suspect your recv loop is continuing forever because you haven't sent enough data to make it break out of the loop.
Change this:
while ((nb = recv(s, &buff[tnb], LINESZ - tnb, 0)) > 0)
{
tnb += nb;
}
To this: (notice that I'm allocating +1 for the array buff)
char buff[LINESZ+1]; // +1 for null terminator
buff[0] = '\0';
tnb = 0;
while (tnb < LINESZ)
{
nb = recv(s, &buff[tnb], LINESZ-tnb, 0);
if (nb < 0)
{
printf("Error on socket: %d\n", (int)WSAGetLastError());
break;
}
else if (nb == 0)
{
printf("Remote socket closed\n");
break;
}
printf("Received: %d bytes\n", (int)nb);
tnb += nb;
buff[tnb] = '\0'; // null terminate the end of the buffer so it will print reliably
}
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).
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.