How I brought up my server without being able to assign a static IP address to it

How I brought up my server without being able to assign a static IP address to it

I was born in one city, then moved to live in another. There is a PC left in the hometown, which is idle. One day I decided to turn it into a multifunctional remote machine: to code, store files, and host sites/bots. I liked the idea, I installed linux on the machine, put all the disks that were lying around and started to check it all. But here it turned out that the Internet tariff in the parent’s house does not support the possibility of setting a static IP address by default – the address is issued by the provider at random moments of time. This meant that I couldn’t, for example, host a server on that machine. What’s more, I couldn’t even trivially SSH into her after changing her address.

Attempts to find a solution to my problem on the Internet ended in failure. I saw no other way but to change the tariff, but that too seemed impossible.

An idea came to mind…

I was thinking of some mechanism that would allow me to find out the external IP address of the machine without forcing the parents to learn to use a command. And then the idea came to mind to write a Telegram bot that would be hosted on my car. After all, its interface is always accessible regardless of the PC address.

So, let’s get to work. My main language at work and for pet projects is C++. Therefore, I took the tgbot-cpp library to write the robot. The logic of the bot is simple: we get an external address and send it to the user.

void OnGetIPCommandAction(TgBot::Message::Ptr message) const noexcept
try
{
    std::cerr << "[DEBUG] Processing getip command..." << std::endl;

    if (!IsUserLegal(message->from))
    {
        return;
    }

    const auto ip{GetExternalIP()};

    m_bot.getApi().sendMessage(message->chat->id, ip ? ip.value() : "Could not get IP");
}
catch (const std::exception& e)
{
    std::cerr << "[ERROR] " << __func__ << " failed: " << e.what() << std::endl;
}

Since the bot is available to anyone who finds it, a mechanism for restricting access had to be sold. According to the TelegramAPI description, each user has a unique ID. This ID can be used to authenticate users. The bot calls a method before processing each command IsUserLegal, which checks for the current user’s ID among many allowed. The code of the method is trivial – it will not be shown here.

Method GetExternalIP is responsible for obtaining an external address. To do this, he goes to the Internet and receives the content of the page http://myexternalip.com/raw, which this address contains.

static 
std::optional<std::string> GetExternalIP()
{
    constexpr std::string_view URL = "http://myexternalip.com/raw";

    CURL* curlHandle{curl_easy_init()};
    if (!curlHandle)
    {
        return std::nullopt;
    }

    const static auto downloadFunc = [](char* ptr, size_t size, size_t nmemb, std::string* data) -> size_t
    {
        assert(data);
        auto& str = *data;

        try
        {
            str.append(ptr, size * nmemb);
        }
        catch(const std::exception& e)
        {
            std::cerr << "[ERROR] downloadFunc failed: " << e.what() << std::endl;
            return 0;
        }

        return size * nmemb;
    };

    std::string ipStr;
    curl_easy_setopt(curlHandle, CURLOPT_URL, URL.data());
    curl_easy_setopt(curlHandle, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, (size_t(*)(char*, size_t, size_t, std::string*))downloadFunc);
    curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, &ipStr);

    if (const auto retcode{curl_easy_perform(curlHandle)}; retcode != CURLE_OK)
    {
        std::cerr << "[ERROR] " << "curl_easy_perform() failed: " << curl_easy_strerror(retcode) << std::endl;
    }

    curl_easy_cleanup(curlHandle);

    return ipStr;
}

So, the work is written, you can check it.

Chat with a bot

Great, works as intended. It remains to solve the last problem. The bot must be launched together with the computer, otherwise it is of no use. The solution suggests itself – let’s make the robot a system daemon. Here I will not talk about the process of setting them up, but only show the contents of the minimal configuration file.

[Unit]
Description=MyPc Telegram Bot
Requires=network.target
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/pcbot
Restart=always

[Install]
WantedBy=multi-user.target

done Now the bot starts with the computer, restarts in case of a fall and tells its address on request. Now I can use the computer as I originally wanted.

Conclusion

I do not undertake to assert that such a solution is the simplest and most adequate. Perhaps there is a more straightforward way to achieve my goal. But please don’t blame me – I haven’t found another one. I just wanted to share my experience here.

I am very interested in the topic of creating Telegram bots. Therefore, if you have an idea how you can create something useful and interesting with their help, please write about it in the comments.

Related posts