timeout and select() not working as expected - c++

I'm trying to communicate with a server using an rcon protocol to take control over a gamesever. So far I was using an exisiting C# library for this but this library is sometimes quite buggy and therefore I'm was writing my own application in C++ so that I'm able to use it both on windows and on my linux server.
So far this worked out pretty well but now I'm running into problems when I try to set a timeout using select() to find out if the server still started and responding to my commands. During the first run of the application select() always returns the right value, but after closing and running it again, results get weird.
My code pretty much looks like this:
#include <stdio.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string.h>
#include <unistd.h>
// Socket Includes
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// Other Includes
#include "main.h"
#include "crc32.h"
int main() {
struct sockaddr_in server;
int mysocket, slen = sizeof(server);
char buffer[2048];
if ((mysocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
printf("error creating socket");
memset((char *) &server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");
server.sin_port = htons(1234);
if(connect(mysocket, (sockaddr*) &server, sizeof(server)) < 0)
printf("error connecting the socket");
bool isConnected = false;
/*
Creating the Packet to send, cut to save some space
*/
sendto(mysocket, loginPacket.c_str(), loginPacket.length(), 0, (sockaddr *) &server, slen);
while(1) {
// clearing the buffer
bzero(buffer, 2048);
// Timeout Settings
int selectSize = 0;
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
fd_set fds;
FD_ZERO(&fds);
FD_SET(mysocket, &fds);
selectSize = select(mysocket + 1, &fds, 0, 0, &timeout);
std::cout << "Size is: " selectSize << std::endl; // just for testing :)
if (selectSize == 1) {
int recvLength = recvfrom(mysocket, buffer, sizeof(buffer), 0, (sockaddr *) &server, (socklen_t*) &slen);
if (buffer[7] == 0x00) {
if (buffer[8] == 0x01) {
//password correct
isConnected = true;
break;
}
if (buffer[8] == 0x00) {
//password wrong, do sth.
}
}
}
}
// tests
sayHello();
close(mysocket);
return 0;
}
When I start the script for the first time and the server is not started, everything works as expected, after 5 seconds selectSize returns the value 0. It loops further on until I start the the server, then it will return value 1 and break the while loop. Afterwards I quit the application, turn off the server and start the script again. Instead of return value 0 after 5 seconds, it immediately returns value 1 (even though the server is offline and there's not packet to receive) and then after 5 seconds it will return value 0. Running the same script (with some adjustments) on windows gave me pretty much the same result, but selectSize pretty much always returned 1, even though the server was offline and the value should have been 0. I read tons of sites about using select() the right way but none of them helped me so far as select() is not returning reliable results after quitting and restarting the application.
P.S.: There was some confusing information about how to use select() the right way, I used pretty much every solution provided on the internet like:
selectSize = select(mysocket + 1, &fds, 0, 0, &timeout);
selectSize = select(0, &fds, 0, 0, &timeout);
selectSize = select(1, &fds, 0, 0, &timeout);
But none of them gave me a reliable result.

Related

How to Test and Run a Client Server Program in C++ using Eclipse

I am trying to get started with UDP and would like to test and debug some client server programs.
I am using an Eclipse IDE with cygwin64 as a compiler.
I have found some example client server programs from here: https://www.geeksforgeeks.org/udp-server-client-implementation-c/
I would like to be able to run the example to get me started on learning about UDP client servers.
The example code is as follows:
Server
// Server side implementation of UDP client-server model
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define PORT 8080
#define MAXLINE 1024
// Driver code
int main() {
int sockfd;
char buffer[MAXLINE];
char *hello = "Hello from server";
struct sockaddr_in servaddr, cliaddr;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
// Filling server information
servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(PORT);
// Bind the socket with the server address
if ( bind(sockfd, (const struct sockaddr *)&servaddr,
sizeof(servaddr)) < 0 )
{
perror("bind failed");
exit(EXIT_FAILURE);
}
int len, n;
n = recvfrom(sockfd, (char *)buffer, MAXLINE,
MSG_WAITALL, ( struct sockaddr *) &cliaddr,
&len);
buffer[n] = '\0';
printf("Client : %s\n", buffer);
sendto(sockfd, (const char *)hello, strlen(hello),
0, (const struct sockaddr *) &cliaddr,
len);
printf("Hello message sent.\n");
return 0;
}
Client
// Client side implementation of UDP client-server model
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define PORT 8080
#define MAXLINE 1024
// Driver code
int main() {
int sockfd;
char buffer[MAXLINE];
char *hello = "Hello from client";
struct sockaddr_in servaddr;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
// Filling server information
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY;
int n, len;
sendto(sockfd, (const char *)hello, strlen(hello),
MSG_CONFIRM, (const struct sockaddr *) &servaddr,
sizeof(servaddr));
printf("Hello message sent.\n");
n = recvfrom(sockfd, (char *)buffer, MAXLINE,
MSG_WAITALL, (struct sockaddr *) &servaddr,
&len);
buffer[n] = '\0';
printf("Server : %s\n", buffer);
close(sockfd);
return 0;
}
I have tried opening up two different eclipse workbenches and running both the codes, but it is not running as expected and it is saying that the messages have been sent but I have not been able to receive them on the client or server.
I definitely would like to stick with c/c++ and try to get this working in eclipse if it's possible.
If anyone has any advice on how I can be able to see some results or anything that could get me started with this, it would be much appreciated. Thank you!
Problem
You have not set the address of the server correctly in the client code.
servaddr.sin_addr.s_addr = INADDR_ANY;
is meaningless when sending (Why would you want to send to any available address? You want to send to the server.) and is rejected by sendto. You would have seen this if you'd checked the return value. Always check the return value, even for UDP communications. Sure there are a million reasons why a packet won't arrive with UDP that you can't detect at the time of sendto, but it's good to know that the network stack actually accepted the message even if a grue subsequently devours the packet in one of the dark corners of the Internet. A sending error you can do something about, like perror and then fix the code accordingly. A grue, not so much. You'll have to make a protocol robust enough to retransmit or otherwise survive the loss of the packet.
Solution
Get the server's address structure with getaddrinfo.
Replace
// Filling server information
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY;
with something more like
struct addrinfo hints;
struct addrinfo *hostlist;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = 0;
hints.ai_protocol = IPPROTO_UDP;
if (getaddrinfo("Server Name or Address Goes Here",
PORT, // this needs to be a char *, not an integer
&hints,
&hostlist))
{
// handle error
exit(EXIT_FAILURE);
}
PORT in this case needs to be a char *, so instead of
#define PORT 8080
use
constexpr char const * PORT = "8080";
You can leave out the constexpr if your compiler is old and doesn't support it.
Then when you go to send, iterate through the hostlist until you find a host that responds. Since you've probably narrowed the servers down to exactly one candidate, this is probably overkill, but you might as well get into practice doing things right. The alternative sucks when systems start getting complicated.
struct addrinfo *curhost;
for (curhost = hostlist; curhost != NULL; curhost = curhost->ai_next)
{
int rval = sendto(sockfd,
(const char *) hello,
strlen(hello),
0,
curhost->ai_addr,
curhost->ai_addrlen);
if (rval> 0) // always check return codes. Programmers are lazy.
// They wouldn't have gone to the effort of putting
// it there if it wasn't important.
{
if server responds
do protocol stuff to complete transaction
break;
}
}
freeaddrinfo(hostlist); // thou shalt not leak resources.
if (curhost == nullptr)
{
notify user that no server was willing to talk
}
Documentation for getaddrinfo.

Windows UDP sockets: recvfrom() fails with error 10054

Hello everyone.
I'm trying to use Windows sockets to send and receive UDP packets (in C++).
It worked well until three days ago, when the program stopped behaving properly.
To summarize the situation:
When calling WSAPoll() on my socket, it always returns my socket updated with EVERY revents possible (corresponding to every events I gave the pollfd), even if there is no server launched.
When calling recvfrom() and no server is launched, it returns SOCKET_ERROR with error code 10054(*).
When calling recvfrom() and a server is launched, it works properly - blocks until it receives something.
The behavior is the same whether I try to connect to localhost or to a distant host.
(*) I investigated this error. In UDP, it means that there is an ICMP problem. ("On a UDP-datagram socket this error indicates a previous send operation resulted in an ICMP Port Unreachable message.").
I indeed call sendto() before recvfrom(), so the problem's not here.
I tried to put down my firewall to see if it changed anything, but it didn't. I also tried to put down every network flowing through my PC. In this state I managed to get the program to work for a few minutes, but when I enabled the networks it stopped working again. I tried to repeat the process but it would not work anymore.
I tried compiling with both visual studio (2015) and MinGW.
I tried on another computer too (under Windows 7, mine has Windows 8.1), to no avail.
Here is a simple test file which does not work on my computer.
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x501
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <vector>
#include <iostream>
int main() {
int clientSock;
char buf[100];
int serverPort;
/* Initializing WSA */
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
/* I create my socket */
struct addrinfo specs;
struct addrinfo *addr = new addrinfo;
ZeroMemory(&specs, sizeof(specs));
specs.ai_family = AF_INET;
specs.ai_socktype = SOCK_DGRAM;
specs.ai_flags = 0;
getaddrinfo("127.0.0.1", "2324", &specs, &addr);
clientSock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
/* I get the server's address */
struct sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
serverAddr.sin_port = htons(2324);
int len = sizeof(struct sockaddr);
/* I'll poll & recvfrom on my socket */
std::vector<pollfd> fds;
pollfd fd;
fd.fd = clientSock;
fd.events = POLLRDNORM;
fd.revents = -1;
fds.push_back(fd);
while(1) {
memset(buf,0,sizeof(buf));
printf("\nClient--->: ");
gets(buf);
/* It's UDP, so it doesn't matter if there is someone to receive the packet */
sendto(clientSock, buf, strlen(buf), 0, (sockaddr*)&serverAddr ,len);
memset(buf,0,sizeof(buf));
int ret;
/* Always returns "1" */
if ((ret = WSAPoll(fds.data(), 1, 0)) > 0) {
std::cout << ret;
/* Always returns "-1" */
std::cout << recvfrom(clientSock,buf,sizeof(buf),0, (sockaddr*)&serverAddr,&len) << std::endl;
printf("\n--->From the server: ");
printf("%s",buf);
}
}
closesocket(clientSock);
WSACleanup();
return 0;
}
Two questions:
Why does WSAPoll() always returns an updated socket, even if there wasn't any interaction with it ?
Why does recvfrom() return this error and how can I fix it ? I suppose it comes from my computer. I tried allowing ICMP through my firewall but it didn't change anything, maybe I did something wrong ?
Edit: I fixed my main program (not shown here because it is way too large) by just ignoring any "error 10054" I received. Now it works the same way it does on Unix.
Still, it is not really a solution (ignoring an error code... meh) and if anyone knows why I get the "ICMP Port Unreachable" error when calling sendto(), I'd be glad to hear about it.
In Windows, if host A use UDP socket and call sendto() to send something to host B, but B doesn't bind any port so that B doesn't receive the message, and then host A call recvfrom() to receive some message, recvfrom() will failed, and WSAGetLastError() will return 10054.
It's a bug of Windows. If UDP socket recv a ICMP(port unreachable) message after send a message, this error will be stored, and next time call recvfrom() will return this error.
There are 2 ways to solve this problem:
Make sure host B has already bound the port you want to send to.
Disable this error by using following code:
#include <Winsock2.h>
#include <Mstcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12)
BOOL bNewBehavior = FALSE;
DWORD dwBytesReturned = 0;
WSAIoctl(iSock, SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL);
Reference:
http://www.cnblogs.com/cnpirate/p/4059137.html
I have stripped down the Authors code and included the fix of simmerlee. This provides an simpler way to reproduce the error:
#include "stdafx.h"
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12)
void testCase(bool fixed)
{
int clientSock;
char rcvBuf[100];
// create socket
clientSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(fixed)
{
BOOL bNewBehavior = FALSE;
DWORD dwBytesReturned = 0;
WSAIoctl(clientSock, SIO_UDP_CONNRESET, &bNewBehavior, sizeof bNewBehavior, NULL, 0, &dwBytesReturned, NULL, NULL);
}
// bind socket
struct sockaddr_in clientAddr;
clientAddr.sin_family = AF_INET;
clientAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
clientAddr.sin_port = htons(61234);
int sizeClientAddr = sizeof(clientAddr);
bind(clientSock, (sockaddr*) &clientAddr, sizeClientAddr);
struct sockaddr_in serverAddr = clientAddr;
serverAddr.sin_port = htons(2324); // change port where nobody listens
int sizeServerAddr = sizeof(struct sockaddr);
int lasterror = 0;
int status = 0;
// send where nobody is listening
printf("Send to nowhere--->:\n");
/* It's UDP, so it doesn't matter if there is someone to receive the packet */
status =sendto(clientSock, "Message", 7, 0, (sockaddr*)&serverAddr, sizeServerAddr);
lasterror = WSAGetLastError();
printf("sendto return %d (lasterror %d)\n", status, lasterror);
// recvfrom with "failing" sendto before.
// fixed: This should block.
// unfixed: WSAGetLastError is 10054
memset(rcvBuf, 0, sizeof(rcvBuf));
status = recvfrom(clientSock, rcvBuf, sizeof(rcvBuf), 0, (sockaddr*)&serverAddr, &sizeServerAddr);
lasterror = WSAGetLastError();
printf("recvfrom return %d (lasterror %d)\n", status, lasterror);
printf("--->From the server: -%s-\n", rcvBuf);
closesocket(clientSock);
}
int _tmain(int argc, _TCHAR* argv[])
{
/* Initializing WSA */
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
printf("##### UNFIXED\n");
testCase(false);
printf("##### FIXED\n");
testCase(true);
WSACleanup();
// pause
char buf[100];
gets(buf);
return 0;
}
This should return:
##### UNFIXED
Send to nowhere--->:
sendto return 7 (lasterror 0)
recvfrom return -1 (lasterror 10054)
--->From the server: --
##### FIXED
Send to nowhere--->:
sendto return 7 (lasterror 0)
and then block.

Can I use select to combine stdin and accept?

I am trying to implement a server in C++/Linux that regularly takes user input from the terminal. Initially I had implemented two separate threads to handle this behavior. But I realized that I would need something like pthread_cancel to cancel the server thread in case the user wanted to shut down the server.
I then decided that it might be better to handle both actions in the same thread, so I dont have to worry about resource leakage. So what I have now is a 'select' call that selects over the stdin fd as well as my accepting fd. My code looks something like this...
fdset readfds;
FD_SET(acceptfd, &readfds);
FD_SET(stdinfd, &readfds);
while(1) {
select(n, &readfds, NULL, NULL, NULL);
....
}
For some reason I am no longer able to read input from stdin. This works fine when I remove either one of the two fds from my fd set, the other ome performs as expected. But when I leave them both in, while the acceptfd still accepts incoming connections, the stdinfd fails to respond to terminal input.
Does anyone know what I might be doing wrong here? Is this approach inherently flawed? Should I be focusing on keeping the two actions as separate threads and figuring out a way to exit cleanly instead?
Thanks for reading!!
As Ambroz commented, multiplexing stdin and some listened fd is possible.
But select is an old, nearly obsolete syscall, you should prefer using poll(2). If you insist on still using select(2) syscall, you should clear the readfds at first with FD_ZERO inside the loop. And the FD_SET macros should be inside the while loop, because select is permitted to modify the readfds.
The poll syscall is preferable to select because select impose a wired-in limit to the number of file descriptors the process can have (typically 1024, while the kernel is today able to deal with a bigger number of fds, eg 65536). In other words, select requires that every fd is < 1024 (which is false today). poll is able to deal with any set of any fd. The first argument to poll is an array (which you could calloc if you wanted to) whose size is the number of fds you want to multiplex. In your case, it is two (stdin and the second listened fd), so you can make it a local variable. Be sure to clear and initialize it before every call to poll.
You could debug with a debugger like gdb or just use strace
This epoll code works for me:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define PORT 4711
int main(void) {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = htons(INADDR_ANY);
bind(sockfd, (struct sockaddr*) &addr, sizeof (addr));
listen(sockfd, 10);
int epollfd = epoll_create1(0);
struct epoll_event event;
// add stdin
event.events = EPOLLIN|EPOLLPRI|EPOLLERR;
event.data.fd = STDIN_FILENO;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &event) != 0) {
perror("epoll_ctr add stdin failed.");
return 1;
}
// add socket
event.events = EPOLLIN|EPOLLPRI|EPOLLERR;
event.data.fd = sockfd;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &event) != 0) {
perror("epoll_ctr add sockfd failed.");
return 1;
}
char *line = NULL;
size_t linelen = 0;
for (;;) {
int fds = epoll_wait(epollfd, &event, 1, -1);
if (fds < 0) {
perror("epoll_wait failed.");
return 2;
}
if (fds == 0) {
continue;
}
if (event.data.fd == STDIN_FILENO) {
// read input line
int read = getline(&line, &linelen, stdin);
if (read < 0) {
perror("could not getline");
return 3;
}
printf("Read: %.*s", read, line);
} else if (event.data.fd == sockfd) {
// accept client
struct sockaddr_in client_addr;
socklen_t addrlen = sizeof (client_addr);
int clientfd = accept(sockfd, (struct sockaddr*) &client_addr, &addrlen);
if (clientfd == -1) {
perror("could not accept");
return 4;
}
send(clientfd, "Bye", 3, 0);
close(clientfd);
} else {
// cannot happen™
fprintf(stderr, "Bad fd: %d\n", event.data.fd);
return 5;
}
}
close(epollfd);
close(sockfd);
return 0;
}

*nix & C++ writing a non-blocking socket server

I'm experiencing some issues with rewriting my blocking socket server to a non-blocking version.
Actually, I can't seem to even get socket connected anymore, I've been googling for the most of today, and trying different solutions I find here and there, but none of them seem to work properly...
Currently my server loop just keeps timeouting the select() call, with no new sockets accepted.
Client socket seems to connect on some level, since if I start it, it will block trying to write, and if I close the server, it will inform that connection was reset by peer.
Is the following a correct assumption?
With non-blocking server I should normally open the socket, then set it's flags to non-blocking, bind it, and the start calling select for read file descriptor and wait for it to populate ?
I need to remove old blocking "accept()" call, which was waiting endlessly..
If I try calling accept, it will -1 on me now...
Here is the relevant code I'm trying now
fd_set incoming_sockets;
....
int listener_socket, newsockfd, portno;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
....
listener_socket = socket(AF_INET, SOCK_STREAM, 0); //get socket handle
int flags = fcntl(listener_socket, F_GETFL, 0);
if( fcntl(listener_socket, F_SETFL, flags | O_NONBLOCK) < 0 )
log_writer->write_to_error_log("Error setting listening socket to non blocking", false);
memset(&serv_addr, 0, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
....
if (bind(listener_socket, (struct sockaddr *) &serv_addr,
sizeof(struct sockaddr_in)) < 0)
{
log_writer->write_to_error_log("Unable to bind socket, aborting!", true);
}
....
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;
int ready_sockets = 0;
listen(listener_socket,1);
FD_ZERO(&incoming_sockets);
FD_SET(listener_socket, &incoming_sockets);
while(true)
{
ready_sockets = select(listener_socket + 1 , &incoming_sockets, (fd_set * ) 0, (fd_set * ) 0, &timeout );
if(ready_sockets == 0)
{
//I loop here now for ever
std::cout << "no new sockets available, snooze 2\n";
sleep(2);
} else
{
std::cout << "connection received!\n";
Since you don't show the whole loop, I don't know if you do it later, but you should initialize the descriptor sets and timeout structure before every call to select.
You should mover the fd_zero() fd_set() macros inside the loop, select will actually change the bitmasks in the fd_sets (and the timeout value). Reinitialise them on every iteration. Also check for select returning -1 and the associated errno (EPIPE ...)
while(true)
{
FD_ZERO(&incoming_sockets);
FD_SET(listener_socket, &incoming_sockets);
ready_sockets = select(listener_socket + 1 , &incoming_sockets, (fd_set * ) 0, (fd_set * ) 0, &timeout );
if(ready_sockets == 0)
{
... }

OPENSSL with c++ and posix

I have a quick question. I set this client up as a sort of example, so I do not do a lot of extra work with it; I wanted to get the basic idea working first. I have it working so far, with one exception:
If I start it up, I can see the data being sent across on the other side (I use python+twisted). I can write with no problems, the problem comes when I read. On the server side, I am able to see the text coming in and being sent back out again. But on the client side, things are delayed. I have to send three commands to see something coming out.
for example:
I send hello <newline> cruel <newline> world<newline> and get hello echoed back to me, only after I hit enter on world.
Could someone point out why or give me some hints?
Here is the code.
#include <openssl/ssl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <cstdio>
//used for printing an error and then exiting.
inline void error(const char* message)
{
fprintf(stderr, "%s\n", message);
exit(EXIT_FAILURE);
}
//the buffer size we will be working with:
#define MAX_BUFF 4096
int main()
{
int ret; //used for holding bytes read.
int flag = 1; //our IOCTL flag.
char buff[MAX_BUFF]; //a buffer for holding i/o data.
fd_set rdesc, wdesc, srset, swset; //file descriptor sets.
timeval tv; //used for holding the time select should wait.
SSL_CTX* context = NULL; //ssl context.
SSL* ssl = NULL; //main ssl object.
sockaddr_in addr; //server socket address.
int sock = 0;
//clean out the struct:
bzero(&addr, sizeof(sockaddr_in));
//then fill it in.
addr.sin_family = AF_INET;
addr.sin_port = htons(4000);
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr);
//create the socket
sock=socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
error("Error creating initial socket.");
}
//initialize SSL.
SSL_load_error_strings();
SSL_library_init();
//create the ssl context
context = SSL_CTX_new(TLSv1_client_method());
if (!context)
{
error("Could not create SSL context.");
}
//connect the socket to the server.
if (connect(sock, (sockaddr*)&addr, sizeof(sockaddr_in)) < 0)
{
error("Could not connect to specified socket.");
}
//create the ssl object.
ssl = SSL_new(context);
if (!ssl)
{
error("Could not create ssl object.");
}
//try to set the socket as the fd for the ssl object.
if (!SSL_set_fd(ssl, sock))
{
error("Error, could not bind fd to the ssl object.");
}
//link ssl up with the socket.
if (!SSL_connect(ssl))
{
error("Could not perform ssl handshake.");
}
ioctl(sock, FIONBIO, &flag);
//set our file descriptor sets.
FD_SET(fileno(stdin), &wdesc);
FD_SET(sock, &rdesc);
//wait for data, read, then print.
while (1)
{
//we need to zero out our i/o buffer.
bzero(buff, MAX_BUFF);
//initialize our temp fd sets.
srset = rdesc;
swset = wdesc;
//each time select finishes it changes this to how much time it actually slept, so we need to reset it.
tv.tv_usec = 50*1000; //50 ms
tv.tv_sec = 0;
//perform the actual select operation.
select(sock+1, &srset, &swset, NULL, &tv);
//check to see if data was written on stdin (user input)
if (FD_ISSET(fileno(stdin), &swset))
{
//read inputted data.
ret = read(fileno(stdin), buff, MAX_BUFF);
if (ret)
{
//write it to the socket.
SSL_write(ssl, buff, ret);
}
}
//check to see if we received anything.
if (FD_ISSET(sock, &srset))
{
printf("in if.\n");
//read it
ret = SSL_read(ssl, buff, MAX_BUFF);
printf("%d\n", ret);
if (ret)
{
//write it to screen.
printf("%s\n", buff);
}
}
}
return 0;
}