Why am I getting a bad file descriptor error? - c++

I am trying to write a short program that acts like a client, like telnet.
I receive from the user input like so: www.google.com 80 (the port number) and /index.html
However, I get some errors. When I write some debug information, it says that I have a bad file descriptor and Read Failed on file descriptor 100 messagesize = 0.
struct hostent * pHostInfo;
struct sockaddr_in Address;
long nHostAddress;
char pBuffer[BUFFER_SIZE];
unsigned nReadAmount;
int nHostPort = atoi(port);
vector<char *> headerLines;
char buffer[MAX_MSG_SZ];
char contentType[MAX_MSG_SZ];
int hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (hSocket == SOCKET_ERROR)
{
cout << "\nCould Not Make a Socket!\n" << endl;
return 1;
}
pHostInfo = gethostbyname(strHostName);
memcpy(&nHostAddress,pHostInfo -> h_addr, pHostInfo -> h_length);
Address.sin_addr.s_addr = nHostAddress;
Address.sin_port=htons(nHostPort);
Address.sin_family = AF_INET;
if (connect(hSocket, (struct sockaddr *)&Address, sizeof(Address)) == SOCKET_ERROR)
{
cout << "Could not connect to the host!" << endl;
exit(1);
}
char get[] = "GET ";
char http[] = " HTTP/1.0\r\nHost: ";
char end[] = "\r\n\r\n";
strcat(get, URI);
strcat(get, http);
strcat(get, strHostName);
strcat(get, end);
strcpy(pBuffer, get);
int len = strlen(pBuffer);
send(hSocket, pBuffer, len, 0);
nReadAmount = recv(hSocket,pBuffer, BUFFER_SIZE, 0);
//Parsing of data here, then close socket
if (close(hSocket) == SOCKET_ERROR)
{
cout << "Could not close the socket!" << endl;
exit(1);
}
Thanks!

You have a buffer overrun in your code. You're attempting to allocate a bunch of character arrays together into a buffer than is only 5 bytes long:
char get[] = "GET "; // 'get' is 5 bytes long
char http[] = " HTTP/1.0\r\nHost: ";
char end[] = "\r\n\r\n";
strcat(get, URI); // BOOM!
strcat(get, http);
strcat(get, strHostName);
strcat(get, end);
This is likely overwriting your local variable hSocket, resulting in the "bad file descriptor error".
Since you're using C++ (you have a vector in your code), just use a std::string instead of C arrays of characters so that you don't have to worry about memory management.

Another cause of 'Bad file descriptor' can be encountered when trying to write into a location onto which you do not have write permission. For example, when trying to download content with wget in Windows environment:
Cannot write to 'ftp.org.com/parent/child/index.html' (Bad file descriptor).

Another case may be the target out file name includes characters the filesystem won't allow. For instance, suppose you are running wget command on a Linux computer and the target path is an external hard drive formatted with NTFS. The Linux Ext4 filesystem will allow for characters such as '?', but the NTFS filesystem will not. The default behavior for wget is to name the target folder as the specified url. If the url contains a special character like ':', NTFS will not accept that character, resulting in the error "Bad file descriptor". You can disable this default behavior in wget with the --no-host-directories parameter.

Another cause of 'bad file descriptor' can be accidentally trying to write() to a read-only descriptor. I was stuck on that for a while because I was spending all my time looking for a second close() or buffer overrun.

Related

How to fix 'Address already in use' returned by bind() in AF_UNIX

I'm familiarising myself with Unix Sockets for Inter-Process Communication, and following a guide here. Each time I run the program that acts as a server (referred to as echos.c in the guide), I am getting an error that says bind: Address already in use, except for the first time I run it after deleting all build files.
The error is coming from this section of code:
#define SOCK_PATH "echo_socket"
int main(void)
{
int s, s2, len;
socklen_t t;
struct sockaddr_un local, remote;
char str[100];
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
local.sun_family = AF_UNIX;
strcpy(local.sun_path, SOCK_PATH);
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *)&local, len) == -1) {
perror("bind");
exit(1);
}
// ...
In the initial run, the program creates a socket file called echo_socke (I have checked this with ls -l, and it is strange that the name is missing the 't' to make it echo_socket), and then goes on to successfully bind and listen for the client process.
However, in subsequent runs where I do not delete the build files, I get the error bind: Address already in use, coming from the lines
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
Doing additional testing I found that unlink(local.sun_path) is returning a No such file or directory error, so I figure the socket file that the bind is attempting to access is not being found. Could this be due to the file name missing a character as mentioned earlier? (But it works on the first time around, so it can't be?). Any help figuring out what is going on would be much appreciated.

Fastest way to send multiple char array over TCP

I'm working on a webserver framework in C++ mostly for my own understanding, but I want to optimize it as well.
My question is is it faster to write multiple char arrays to the TCP connection for every html response or to spend the time to concatenate up front and only write to the TCP connection once. I was thinking about benchmarking it, but I am not quite sure how to go about it.
This is my first post on stackoverflow, although I have benefitted from the website very often!
Thanks!
Here is what I am talking about for sending many char arrays individually. The alternate would be concatenate all of these char arrays into one char array then sending that.
int main() {
sockaddr_in address;
int server_handle;
int addrlen = sizeof(address);
if ((server_handle = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("cannot create socket");
exit(0);
}
memset((char *) &address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_ANY);
address.sin_port = htons(PORT);
if (bind(server_handle, (sockaddr *) &address, (socklen_t) addrlen) < 0)
{
perror("bind failed");
exit(0);
}
if (listen(server_handle, 3) < 0)
{
perror("In listen");
exit(EXIT_FAILURE);
}
while(1) {
std::cout << "\n+++++++ Waiting for new connection ++++++++\n\n";
int client_handle;
if ((client_handle = accept(server_handle, (struct sockaddr *)&address, (socklen_t *) &addrlen))<0)
{
perror("In accept");
exit(EXIT_FAILURE);
}
// read and respond to client request
char buffer[30000] = {0};
int bytesRead = read(client_handle, buffer, 30000);
char * httptype = "HTTP/1.1 ";
char * status = "200 \n";
char * contenttype = "Content-Type: text/html \n";
char * contentlength = "Content-Length: 21\n\n";
char * body = "<h1>hello world!</h1>";
write(client_handle, httptype, 9);
write(client_handle, status, 5);
write(client_handle, contenttype, 26);
write(client_handle, contentlength, 20);
write(client_handle, body, 21);
std::cout << "------------------Response sent-------------------\n";
close(client_handle);
}
}
If you want to send multiple buffers with a single write call you can use vectored IO (aka scatter/gather IO) as the manual suggests:
char *str0 = "hello ";
char *str1 = "world\n";
struct iovec iov[2];
ssize_t nwritten;
iov[0].iov_base = str0;
iov[0].iov_len = strlen(str0);
iov[1].iov_base = str1;
iov[1].iov_len = strlen(str1);
nwritten = writev(STDOUT_FILENO, iov, 2);
In fact it writing to a socket is not really different from writing to a file descriptor. And the fwrite function was introduced to the C library for a reason: write (be it to a TCP connection or to a file descriptor) involve a system call on common OS and a context change user/kernel. That context change has some overhead, mainly if you write small chunks of data.
On the other hand, if you write larger chunks of data in sizes that are close to the physical size for the underlying system call (disk buffer for a file descriptor, or max packet size for a network socket), the fwrite call or in your example the code concatenating char arrays will not really lower the system overhead and will just add some user code processing.
TL/DR: this depends on the average size of what you write. The smaller it is, the higher benefit of concatenating the date in larger chunks before writing. And remember: this is a low level optimization that should only be considered if you have identified a performance bottleneck or if the code could be used in a broadly distributed library.

Does TCPdump strip any headers when receiving packets?

So i am attempting to send an already constructed packet over a RAW socket interface (these are packets that have been previously captured and i want to resend them without changing the packet integrity) and am using TCPdump to check that the packets are going over correctly (surprise they are not).
The packets are physically being sent but are always 24 bytes short of what my "sent" returns.
In wireshark my eth headers seem to be erased as my source and dest MAC addresses are "00:00:00:00:00
sock setup is as follows
sock = socket(AF_PACKET,SOCK_RAW,IPPROTO_RAW);
if(sock==-1)
{
qDebug() << "sock error";
}
int reuse = 1;
if(setsockopt(sock, IPPROTO_RAW, IP_HDRINCL, (char *)&reuse, sizeof(reuse)) < 0)
{
qDebug() << "error setting reuse"
}
else
{
"setting reuse"
}
struct sockaddr_ll sll;
struct ifreq ifr;
bzero(&sll, sizeof(sll));
bzero(&ifr, sizeof(ifr));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons(IPPROTO_RAW);
sll.sll_halen = ETH_ALEN;
strncpy((char*)ifr.ifr_ifrn.ifrn_name,interface.toUtf8.constData(),IFNAMSIZ);
if(ioctl(sock,SIOCGIFINDEX,&ifr) == -1)
{
qDebug() << "error getting interface name";
}
strncpy((char*)ifr.ifr_ifrn.ifrn_name,interface.toUtf8.constData(),IFNAMSIZ);
if(ioctl(sock,SIOCGIFHWADDR,&ifr) == -1)
{
qDebug() << "error getting interface name";
}
if(bind(sock,(struct sockaddr *)&sll,sizeof(sll))==-1)
{
qDebug() << "error binding sock";
}
after this im using
int size = write(sock,(const void*)&packet,hdr.caplen);
i've tried sendto in the past but it would always reconfigure things so this was my next solution which also isnt working as i would like.
I'm not the most savy with TCP/IP stuff so any help would be greatly appreciated!
okay so after just trying a bunch of different stuff i landed on what seems to be my solution.
i created a second pointer that will point to the top of the packet and send that instead.
(char *)sendingPacket;
struct ethhdr *ethh = (struct ethhdr*)packet;
sendingPacket = (char*) ethh;
i don't really understand why this works but sending the other packet doesn't so if anyone has insight please share!

getnameinfo() compile time error - Extracting MAC address in Linux

I have to send to the server the IP address and MAC address of the network interface from which my client socket is connected and communicating with the server.
All machines are on intra-net.
I have extracted the IP of my socket and I am attempting to extract the H/W address.
My strategy :
Extract IP of the socket using getsockname() system call.
Use getifaddrs() system call to list all available network interfaces. Inside a for-loop I am using getnameinfo() system call to find IP for currently iterating interface name and then compare this IP with socket IP (extracted from step 1) to find interface name of the connected socket.
Use ioctl(fd, SIOCGIFHWADDR, &ifr) system call to get H/W address using interface name found out in stage 2.
I am facing problem getnameinfo() system call.
If I don't type cast the first parameter to (struct sockaddr_in*) I get the following error : ai_family not supported
getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST,
NULL, 0, NI_NUMERICHOST);
If I type cast the first parameter to (struct sockaddr_in*) I get the following error : error: cannot convert ‘sockaddr_in*’ to ‘const sockaddr*’ for argument ‘1’ to ‘int getnameinfo(const sockaddr*, socklen_t, char*, socklen_t, char*, socklen_t, int)’
getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST,
NULL, 0, NI_NUMERICHOST);
Kindly advice. I am even open to some alternative strategy to programmatically and dynamically get Socket IP and MAC address.
bool Ethernet::getIp(void)
{
struct sockaddr_in addr;
char bufferIp[INET_ADDRSTRLEN];
socklen_t addrLen = sizeof(addr);
if(getsockname(this->clientSocket, (struct sockaddr*) &addr, &addrLen) == -1)
{
string errStr = strerror(errno);
FileOperations fo;
string str;
str = "Unable to extract IP address of socket";
str += " Error : " + errStr;
fo.printError(str);
return RETURN_FAILURE;
}
if(inet_ntop(AF_INET, &addr.sin_addr, bufferIp, INET_ADDRSTRLEN) == NULL)
{
string errStr = strerror(errno);
FileOperations fo;
string str;
str = "Unable to convert extracted IP address from binary to char* in Ethernet::getInterfaceDetails.";
str += " Error : " + errStr;
fo.printError(str);
return RETURN_FAILURE;
}
this->ip = string(bufferIp);
return RETURN_SUCCESS;
}
bool Ethernet::getMac(void)
{
int fd;
struct ifreq ifr;
// char *iface = "eth0";
unsigned char *mac;
fd = socket(AF_INET, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET;
strncpy(ifr.ifr_name , this->interfaceName.c_str(), IFNAMSIZ-1);
ioctl(fd, SIOCGIFHWADDR, &ifr);
close(fd);
mac = (unsigned char *)ifr.ifr_hwaddr.sa_data;
//display mac address
printf("Mac : %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n" , mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return RETURN_SUCCESS;
}
bool Ethernet::getInterfaceDetails(void)
{
struct ifaddrs *ifaddr, *ifa;
int s;
char host[NI_MAXHOST];
string tempAddr;
char buffer[INET_ADDRSTRLEN];
if (getifaddrs(&ifaddr) == -1)
{
string errStr = strerror(errno);
FileOperations fo;
string str;
str = "System call 'getifaddrs' failed in Ethernet::getInterfaceDetails.";
str += " Error : " + errStr;
fo.printError(str);
return RETURN_FAILURE;
}
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
continue;
this->interfaceName = string(ifa->ifa_name);
if(this->interfaceName == string("lo"))
continue;
s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST,
NULL, 0, NI_NUMERICHOST);
if(s != 0)
{
string errStr = gai_strerror(s);
cout << "Error : " << errStr << endl;
FileOperations fo;
string str;
str = "Unable to convert extracted IP address address from binary to char* in Ethernet::getInterfaceDetails.";
str += " Error : " + errStr;
fo.printError(str);
return RETURN_FAILURE;
}
tempAddr = string(host);
if(tempAddr == this->ip)
{
freeifaddrs(ifaddr);
return RETURN_SUCCESS;
}
}
return RETURN_FAILURE;
}
I see one strange thing regarding your call to getnameinfo: You are assuming that a non-null ifa_addr equals sockaddr_in type, but I could imagine you could get other types, e.g. sockaddr_in6. So you should check ifa_addr->sa_family field to make sure it's AF_INET. Perhaps you should handle IPv6 as well?
My theory here is that calling getnameinfo with a struct size that does not match what would be expected for the address family might be the reason for your error.
Also look at MAC address with getifaddrs for a related discussion.
The getifaddrs manual says
The ifa_addr field points to a structure containing the interface address. (The sa_family subfield should be consulted to determine the format of the address structure.) This field may contain a null pointer.
Thus
check that the field is not a null pointer (if it is, then it is not the IP you're looking for)
the length parameter must match the length of the addresses in sa_family / or filter just AF_INET.
But wouldn't it be easier to just create a datagram socket to the server then ask what's its address, or actually do the http connection and ask what mac address that socket is using?
If this is embedded Linux and the interface is always named the same, just read from /sys/class/net/eth0/address - much easier.
#Antti Haapala, #Mats;
Thanks for the help.
The problem was as you mentioned that other types/families of addresses where present in the interfaces and where causing problems to getnameinfo() system call.
Now I am filtering out other addresses and only allowing AF_INET.
I have added the following validation :
if(ifa->ifa_addr->sa_family != AF_INET)
continue;

FTP server file transfer

I am uncertain about a few things regarding ftp file transfer. I am writing an ftp server and I am trying to figure out how to make the file tranfer work correctly. So far it works somehow but I have certain doubts. Here is my file transfer function (only retrieve so far):
void RETRCommand(int & clie_sock, int & c_data_sock, char buffer[]){
ifstream file; //clie_sock is used for commands and c_data_sock for data transfer
char *file_name, packet[PACKET_SIZE]; //packet size is 2040
int packet_len, pre_pos = 0, file_end;
file_name = new char[strlen(buffer + 5)];
strcpy(file_name, buffer + 5);
sprintf(buffer, "150 Opening BINARY mode data connection for file transfer\r\n");
if (send(clie_sock, buffer, strlen(buffer), 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent: " << buffer << endl;
file_name[strlen(file_name) - 2] = '\0';
file.open(file_name, ios::in | ios::binary);
if (file.is_open()) {
file.seekg(0, file.end);
file_end = (int) file.tellg();
file.seekg(0, file.beg);
while(file.good()){
pre_pos = file.tellg();
file.read(packet, PACKET_SIZE);
if ((int) file.tellg() == -1)
packet_len = file_end - pre_pos;
else
packet_len = PACKET_SIZE;
if (send(c_data_sock, packet, packet_len, 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent some data" << endl;
}
}
else {
sprintf(buffer, "550 Requested action not taken. File unavailable\r\n", packet);
if (send(clie_sock, buffer, packet_len + 2, 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent: " << buffer << endl;
delete(file_name);
return;
}
sprintf(buffer, "226 Transfer complete\r\n");
if (send(clie_sock, buffer, strlen(buffer), 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent: " << buffer << endl;
close(c_data_sock);
delete(file_name);
}
So one problem is the data transfer itself. I am not exactly sure how it is supposed to work. Now it works like this: the server sends all the data to c_data_sock, closes this socket and then the client starts doing something. Shouldn't the client recieve the data while the server is sending them? And the other problem is the abor command. How am I supposed to recieve the abor command? I tried recv with flag set to MSG_OOB but then I get an error saying "Invalid argument". I would be glad if someone could give me a hint or an example of how to do it right as I don't seem to be able to figure it out myself.
Thanks,
John
Ftp use two connections. First - is command connection, in your case it is clie_sock. 'ABOR' command should be received though it. You going to receive it the same way you received 'RETR' command.
To receive file client establishes data connection with your server ( c_data_sock socket ). It will not be opened till client connects, so this is the answer to your second question. You cannot start client after server executes this function. First client sends 'retr' command to your command socket. Then your sever waits new connection from client ( after sending him data ip and port ). Then client connects ( now you have your c_data_sock ready ) and sends all the data to that socket, which are in turn received by the client.
You probably need to read more about networking in general if you feel you don't understand it. I prefer this one: http://beej.us/guide/bgnet/
Also you have a memory leak here, after you allocate an array with the
file_name = new char[strlen(buffer + 5)];
you need to delete it using
delete [] file_name;
Otherwise file_name will be treated as a simple pointer, not an array, so most of array memory will be kept by your application which is bad especially when creating server.