Search code examples
c++irc

libircclient : Selective connection absolutely impossible to debug


I'm not usually the type to post a question, and more to search why something doesn't work first, but this time I did everything I could, and I just can't figure out what is wrong.

So here's the thing:

I'm currently programming an IRC Bot, and I'm using libircclient, a small C library to handle IRC connections. It's working pretty great, it does the job and is kinda easy to use, but ...

I'm connecting to two different servers, and so I'm using the custom networking loop, which uses the select function. On my personal computer, there's no problem with this loop, and everything works great.

But (Here's the problem), on my remote server, where the bot will be hosted, I can connect to one server but not the other.

I tried to debug everything I could. I even went to examine the sources of libircclient, to see how it worked, and put some printfs where I could, and I could see where does it comes from, but I don't understand why it does this.

So here's the code for the server (The irc_session_t objects are encapsulated, but it's normally kinda easy to understand. Feel free to ask for more informations if you want to):

// Connect the first session
first.connect();

// Connect the osu! session
second.connect();

// Initialize sockets sets
fd_set sockets, out_sockets;

// Initialize sockets count
int sockets_count;

// Initialize timeout struct
struct timeval timeout;

// Set running as true
running = true;

// While the server is running (Which means always)
while (running)
{
    // First session has disconnected
    if (!first.connected())
        // Reconnect it
        first.connect();

    // Second session has disconnected
    if (!second.connected())
        // Reconnect it
        second.connect();

    // Reset timeout values
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;

    // Reset sockets count
    sockets_count = 0;

    // Reset sockets and out sockets
    FD_ZERO(&sockets);
    FD_ZERO(&out_sockets);

    // Add sessions descriptors
    irc_add_select_descriptors(first.session(), &sockets, &out_sockets, &sockets_count);
    irc_add_select_descriptors(second.session(), &sockets, &out_sockets, &sockets_count);

    // Select something. If it went wrong
    int available = select(sockets_count + 1, &sockets, &out_sockets, NULL, &timeout);

    // Error
    if (available < 0)
        // Error
        Utils::throw_error("Server", "run", "Something went wrong when selecting a socket");

    // We have a socket
    if (available > 0)
    {
        // If there was something wrong when processing the first session
        if (irc_process_select_descriptors(first.session(), &sockets, &out_sockets))
            // Error
            Utils::throw_error("Server", "run", Utils::string_format("Error with the first session: %s", first.get_error()));

        // If there was something wrong when processing the second session
        if (irc_process_select_descriptors(second.session(), &sockets, &out_sockets))
            // Error
            Utils::throw_error("Server", "run", Utils::string_format("Error with the second session: %s", second.get_error()));
    }

The problem in this code is that this line:

irc_process_select_descriptors(second.session(), &sockets, &out_sockets)

Always return an error the first time it's called, and only for one server. The weird thing is that on my Windows computer, it works perfectly, while on the Ubuntu server, it just doesn't want to, and I just can't understand why.

I did some in-depth debug, and I saw that libircclient does this:

if (session->state == LIBIRC_STATE_CONNECTING && FD_ISSET(session->sock, out_set))

And this is where everything goes wrong. The session state is correctly set to LIBIRC_STATE_CONNECTING, but the second thing, FD_ISSET(session->sock, out_set) always return false. It returns true for the first session, but for the second session, never.

The two servers are irc.twitch.tv:6667 and irc.ppy.sh:6667. The servers are correctly set, and the server passwords are correct too, since everything works fine on my personal computer.

Sorry for the very long post.

Thanks in advance !


Solution

  • Alright, after some hours of debug, I finally got the problem.

    So when a session is connected, it will enter in the LIBIRC_STATE_CONNECTING state, and then when calling irc_process_select_descriptors, it will check this:

    if (session->state == LIBIRC_STATE_CONNECTING && FD_ISSET(session->sock, out_set))
    

    The problem is that select() will alter the sockets sets, and will remove all the sets that are not relevant.

    So if the server didn't send any messages before calling the irc_process_select_descriptors, FD_ISSET will return 0, because select() thought that this socket is not relevant.

    I fixed it by just writing

    if (session->state == LIBIRC_STATE_CONNECTING)
    {
        if(!FD_ISSET(session->sock, out_set))
            return 0;
    
        ...
    }
    

    So it will make the program wait until the server has sent us anything.

    Sorry for not having checked everything !