SSL_connect() not working with select() - c++

I have a non blocking Socket and I'm using openssl for creating a ssl connection. My problem is the way SSL_connect works. It Returns SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE in these cases SSL_connect has to be called again. Now this is actually working. The issue I have is, that the select() which should make the program wait for the socket to be ready is not working. This results in over 2000 calls to SSL_connect which is not nice.
Here is my code for this Part of the Program
int test = 0;
while (test != 1) {
test = SSL_connect(sslHandle);
if (test != 1) { //test again or we would quit if we made a connection
if (SSL_get_error(sslHandle, test) != SSL_ERROR_WANT_READ && SSL_get_error(sslHandle, test) != SSL_ERROR_WANT_WRITE) {
//error happend
return -1;
}
FD_ZERO(&socketSet); //reset socketSet
FD_SET(socket, &socketSet); //init socketSet with our socket
if (test == SSL_ERROR_WANT_READ) {
result = select(socket + 1, &socketSet, NULL, NULL, &timeoutCopy); //wait for the socket to be readable
if (result == 0) {
//timeout
return -1;
} else if (result == -1) {
//error
return -1;
}
} else {
result = select(socket + 1, NULL, &socketSet, NULL, &timeoutCopy); //wait for the socket to be writable
if (result == 0) {
//timeout
return -1;
} else if (result == -1) {
//error
return -1;
}
}
}
}
I Removed all the error handling stuff and logs here so it is better readable. timeoutCopy is defined elsewhere. I'm using the same kind of selects for other parts of the program there it is working just fine.
As I said the program is working, but just not waiting at the select. I could just sleep, but that is not a nice solution. Thanks in advance!

Ok I'm a dummy.
Issue is in line
if (test == SSL_ERROR_WANT_READ)
This has to be
if (SSL_get_error(sslHandle, sslConnectResult) == SSL_ERROR_WANT_READ)
Than the code works just fine. It iterates 4 to 5 times and than has the connection.

Related

PseudoTerminal - Not read from stdin

I am creating a class where I can create multiple threads that are pseudo terminals, in order to talk to each one of them I have to create multiple files / Fifos to talk to each pseudo terminal slave, because taslking to the stdin makes any created pseudoterminal listen, the problem is that when using a fifo for input it does not work.
Here is the code
void * Terminal::tTerminal(void * pvParameters)
{
Terminal (*self) = reinterpret_cast<Terminal*>(pvParameters);
fd_set inFds;
//dup2(self->in, STDIN_FILENO);
for (;;)
{
FD_ZERO(&inFds);
FD_SET(self->in, &inFds);
FD_SET(self->masterFd, &inFds);
if (select(self->masterFd + 1, &inFds, &inFds, NULL, NULL) == -1)
{
printf("select");
}
if (FD_ISSET(self->in, &inFds))
{
self->numRead = read(self->in, self->buf, BUF_SIZE);
if (self->numRead <= 0)
exit(EXIT_SUCCESS);
if (write(self->masterFd, self->buf, self->numRead) != self->numRead)
printf("partial/failed write (masterFd)");
}
else
{
printf("partial/failed write (masterFd)");
fflush(stdout);
}
if (FD_ISSET(self->masterFd, &inFds))
{
self->numRead = read(self->masterFd, self->buf, BUF_SIZE);
if (self->numRead <= 0)
exit(EXIT_SUCCESS);
if (write(self->out, self->buf, self->numRead) != self->numRead)
printf("partial/failed write (STDOUT_FILENO)");
}
else
{
printf("partial/failed write (STDOUT_FILENO)");
fflush(stdout);
}
}
For further notices the Fifos are created correctly, the file descriptors are not 0, the master and slave are running, the only problem is in:
FD_ISSET(self->in, &inFds)
Which means it is not set,
Thanks
You should make sure the first argument to select() is the highest of all possible filedescriptors plus one, so:
select(std::max(self->masterFd, self->in) + 1, &inFds, &inFds, NULL, NULL)

server and multiple clients using pthreads and select()

consider the next piece of code -
int get_ready_connection(int s) {
/* socket of connection */
int caller;
if ((caller = accept(s,NULL,NULL)) < SUCCESS)
{
server_log->write_to_log(sys_call_error(SERVER, "accept"));
return FAILURE;
}
return caller;
}
int establish_connection(sockaddr_in& connection_info)
{
// Create socket
if ((server_sock = socket(AF_INET, SOCK_STREAM, 0)) < SUCCESS)
{
server_log->write_to_log(sys_call_error(SERVER, "socket"));
return FAILURE;
}
// Bind `sock` with given addresses
if (bind(server_sock, (struct sockaddr *) &connection_info, \
sizeof(struct sockaddr_in)) < SUCCESS)
{
close(server_sock);
server_log->write_to_log(sys_call_error(SERVER, "bind"));
return FAILURE;
}
// Max # of queued connects
if (listen(server_sock, MAX_PENDING_CONNECTIONS) < SUCCESS)
{
server_log->write_to_log(sys_call_error(SERVER, "listen"));
return FAILURE;
}
// Create a set of file descriptors and empty it.
fd_set set;
bool is_inside;
int ret_val;
while(true)
{
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
FD_SET(server_sock, &set);
struct timeval tv = {2, 0};
ret_val = select(server_sock + 1, &set, NULL, NULL, &tv); // TODO ret_val
is_inside = FD_ISSET(STDIN_FILENO, &set);
if(is_inside)
{
// get user input
string user_input;
getline(cin, user_input);
if ((strcasecmp(user_input.c_str(), EXIT_TEXT) == 0))
{
return SUCCESS;
}
}
is_inside = FD_ISSET(server_sock, &set);
if(is_inside)
{
// get the first connection request
int current_connection = get_ready_connection(server_sock);
if (current_connection == FAILURE) {
free_allocated_memory();
exit_write_close(server_log, sys_call_error(SERVER, "accept"),
ERROR);
}
// if exit was not typed by the server's stdin, process the request
pthread_t thread;
// create thread
if (pthread_create(&thread, NULL, command_thread_func, &current_connection) != 0)
{
free_allocated_memory();
exit_write_close(server_log, sys_call_error(SERVER, "pthread_create"), ERROR);
}
}
}
}
All im trying to do, is to "listen" to STDIN for the user to type 'EXIT' in server's shell, and to wait for clients to pass commands from their shells (every time a command is recieved by the server from the user, the server parses it, and the server creates a thread that handles execution of the command)
To do it simultaniously, i used select().
When i work with a single thread, everything's perfect. But the problem is when im connecting another client i get a seg fault. i suspect that the problem is right here. any suggestions?
Hard to know if this is your exact problem, but this is definitely a problem:
You can't call pthread_create and provide a pointer to a stack variable (&current_connection) as your thread function's argument. For one thing, it's subject to immediate destruction as soon as the parent exits that scope.
Secondly, it will be overwritten on the next call to get_ready_connection.

Is poll() an edge triggered function?

I am responsible for a server that exports data over a TCP connection. With each data record that the server transmits, it requires the client to send a short "\n" acknowledgement message back. I have a customer who claims that the acknowledgement that he sends is not read from the web server. The following is code that I am using for I/O on the socket:
bool can_send = true;
char tx_buff[1024];
char rx_buff[1024];
struct pollfd poll_descriptor;
int rcd;
poll_descriptor.fd = socket_handle;
poll_descriptor.events = POLLIN | POLLOUT;
poll_descriptor.revents = 0;
while(!should_quit && is_connected)
{
// if we know that data can be written, we need to do this before we poll the OS for
// events. This will prevent the 100 msec latency that would otherwise occur
fill_write_buffer(write_buffer);
while(can_send && !should_quit && !write_buffer.empty())
{
uint4 tx_len = write_buffer.copy(tx_buff, sizeof(tx_buff));
rcd = ::send(
socket_handle,
tx_buff,
tx_len,
0);
if(rcd == -1 && errno != EINTR)
throw SocketException("socket write failure");
write_buffer.pop(rcd);
if(rcd > 0)
on_low_level_write(tx_buff, rcd);
if(rcd < tx_len)
can_send = false;
}
// we will use poll for up to 100 msec to determine whether the socket can be read or
// written
if(!can_send)
poll_descriptor.events = POLLIN | POLLOUT;
else
poll_descriptor.events = POLLIN;
poll(&poll_descriptor, 1, 100);
// check to see if an error has occurred
if((poll_descriptor.revents & POLLERR) != 0 ||
(poll_descriptor.revents & POLLHUP) != 0 ||
(poll_descriptor.revents & POLLNVAL) != 0)
throw SocketException("socket hung up or socket error");
// check to see if anything can be written
if((poll_descriptor.revents & POLLOUT) != 0)
can_send = true;
// check to see if anything can be read
if((poll_descriptor.revents & POLLIN) != 0)
{
ssize_t bytes_read;
ssize_t total_bytes_read = 0;
int bytes_remaining = 0;
do
{
bytes_read = ::recv(
socket_handle,
rx_buff,
sizeof(rx_buff),
0);
if(bytes_read > 0)
{
total_bytes_read += bytes_read;
on_low_level_read(rx_buff,bytes_read);
}
else if(bytes_read == -1)
throw SocketException("read failure");
ioctl(
socket_handle,
FIONREAD,
&bytes_remaining);
}
while(bytes_remaining != 0);
// recv() will return 0 if the socket has been closed
if(total_bytes_read > 0)
read_event::cpost(this);
else
{
is_connected = false;
closed_event::cpost(this);
}
}
}
I have written this code based upon the assumption that poll() is a level triggered function and will unblock immediately as long as there is data to be read from the socket. Everything that I have read seems to back up this assumption. Is there a reason that I may have missed that would cause the above code to miss a read event?
It is not edge triggered. It is always level triggered. I will have to read your code to answer your actual question though. But that answers the question in the title. :-)
I can see no clear reason in your code why you might be seeing the behavior you are seeing. But the scope of your question is a lot larger than the code you're presenting, and I cannot pretend that this is a complete problem diagnosis.
It is level triggered. POLLIN fires if there is data in the socket receive buffer when you poll, and POLLOUT fires if there is room in the socket send buffer (which there almost always is).
Based on your own assessment of the problem (that is, you are blocked on poll when you expect to be able to read the acknowledgement), then you will eventually get a timeout.
If the customer's machine is more than 50ms away from your server, then you will always timeout on the connection before receiving the acknowledgement, since you only wait 100ms. This is because it will take a minimum of 50ms for the data to reach the customer, and a minimum of 50ms for the acknowledgement to return.

Losing data with GetOverlappedResult?

I have a thread that constantly looks for new data and if the data is not already in the serial buffer, ReadFile and GetOverlappedResult seem to tell me there's data, and that it read it, but not transfer it into my buffer...
func read()
{
if(state == 0)
{
memset(bytes, '\0', sizeof(amount_to_read));
readreturn = ReadFile(h, bytes, amount_to_read,NULL, osReader);
if(readreturn <= 0)
{
errorcode = GetLastError();
if(errorcode != ERROR_IO_PENDING)
{
SetEAIError(ERROR_INTERNALERROR);
return -1;
}
}
}
if (GetOverlappedResult(h, osReader, &dwRead, FALSE) == false)
{
errorcode = GetLastError();
if (errorcode == ERROR_IO_INCOMPLETE || errorcode == 0)
{
if(dwRead > 0)
{
return 1;
}
//timeout
SetEAIError(ERROR_EAITIMEOUT);
return -1;
}
else
{
//other error
SetEAIError(ERROR_WIN_ERROR);
return -1;
}
}
else
{
//read succeded, check if we read the amount required
if(dwRead != amount_to_read)
{
if(dwRead == 0)
{
//nothing read, treat as timeout
SetEAIError(ERROR_EAINOREAD);
return -1;
}
else
{
//memcpy_s(bytes, sizeof(bytes), readbuf, dwRead);
SetEAIError(ERROR_PARTIALREAD);
*_bytesRead = dwRead;
return -1;
}
}
else
{
if(strlen((char*)bytes) == 0)
{
//nothing read, treat as timeout
SetEAIError(ERROR_EAINOREAD);
return -1;
}
//memcpy_s(bytes, sizeof(bytes), readbuf, dwRead);
*_bytesRead = dwRead;
return 0;
}
}
}
This is what the error codes mean:
ERROR_TIMEOUT - switches the state to 1 so that it does not read again, which calls GetOverlappedResult again
INTERNALERROR,ERROR_EAINOREAD - it resets state to 0
ERROR_PARTIALREAD - starts a new read with new amount of bytes to read
If I swtich GetOverlappedResult to blocking (pass TRUE) it works every time.
If I switch my thread to only read when I know there is data there it works every time.
But if there is not data there, when there is data there it seems to "lose" the data, it my read amount parameter dwRead shows the correct number of bytes read (can see it read with a port monitor) but the bytes are not stored in my char*.
I constantly get ERROR_EAINOREAD
What am I doing wrong?
I do not want to use flags, I want to just use ReadFile and GetOverlappedResult, I should be able to accomplish this with the code I have....... I assume
The problem was exactly what was stated the data was getting lost... the REASON it was getting lost is because the bytes parameter passed into the readfile is a local variable in the parents thread. being local it gets re initialized each cycle so after I come into the read again, skip the readfile and go to the overlappedresults, I am now potentially working with a different area of memory

What might cause an infinite loop error

I am working on a network programming and I have this code
void WorkHandler::workLoop(){
.
.
.
while(1){
if(remainLength >= MAX_LENGTH)
currentSentLength = send(client->getFd(), sBuffer, MAX_LENGTH, MSG_NOSIGNAL);
else
currentSentLength = send(client->getFd(), sBuffer, remainLength,MSG_NOSIGNAL);
if(currentSentLength == -1){
log("WorkHandler::workLoop, connection has been lost \n");
break;
}
sBuffer += currentSentLength;
remainLength -= currentSentLength;
if(remainLength == 0)
break;
}
}
Also, I am creating a child thread like this
bool WorkHandler::initThreads(){
for(int i=0; i < m_maxThreads; i++){
pthread_t *thread(new pthread_t);
m_workThreadList.push_back(thread);
if(pthread_create(thread, NULL, runWorkThread, reinterpret_cast<void *>(this))!=0){
log("WorkHandler::initThreads, pthread_create error \n");
return false;
}
pthread_detach(*thread);
}
return true;
}
void* WorkHandler::runWorkThread(void *delegate){
printf("WorkHandler::runWorkThread, called\n");
WorkHandler *ptr = reinterpret_cast<WorkHandler*>(delegate);
ptr->workLoop();
return NULL;
}
I am running this code on gdb and it doesn't blow up but it gets stuck at the second send function in the if then else loop. I put log statements every single line and it prints a log right above the second send function and stopped.
currentSentLength = send(client->getFd(), sBuffer, remainLength, MSG_NOSIGNAL);
What might cause this problem and how do I fix this issue?
Thanks in advance..
With blocking IO send will block if the kernel buffer is full and will block untill the clients have read the data. Do you send large chunks? If so, check your client.
If you don't trust clients (they can abuse this to do denial of service attacks) there are a couple of ways to do this properly: poll (with timeout) on the sockets for writeability, send with timeout, use nonblocking I/O, ...
I guess you're calling send() with a negative size...
Your test to exit the while should be
remainLength <= 0
and not
remainLength == 0