Get
Get

Reputation: 1

BackToSchool - TCP; root-me; should work but doesn't - c++ C++

I am trying to solve a root-me.org challenge, it is a pretty easy one but i am still stuck and cannot figure out what the problem is. The problem :

Statement

To start this test using the TCP protocol, you need to connect to a program on a network socket.

Calculate the square root of number 1 and multiply by number 2.

Then round the result to two decimal places.

You have 2 seconds to send the correct answer from the moment the program sends you the calculation.

The answer must be sent in the form of int

According to what I have discovered so far the issue lies with the part where I try to send my response to the server.

Link to the challenge I am trying to solve : https://www.root-me.org/en/Challenges/Programming/TCP-Back-to-school

I have tried four different ways to send the answer to send my answer : using a float, a string and an integer, at one point only I got a response from the server, it said that I sended the wrong type, "answer has to be int or float", I don't even remember how I did to get that response.

Additionally, note that I am only able to send strings with the right value and precision as floats lack too much precision in c++. For example: sending doubles results in sending a number with the right precision (38559.390000) but the decimals are filled up by "0" sending float results in wrong result : (8807.008807) sending string works but apparently the server doesn't support it.

My code :

// TCP - Retour au collège
#include <sys/socket.h> 
#include <iostream>
#include <cmath>
#include <cstring>
#include <netinet/in.h>
#include <unistd.h>
#include <iomanip>
#include <arpa/inet.h>
#include <string>

int main() {
    // Connect to root-me.org with tcp (this script is client side)
    int client = socket(AF_INET6, SOCK_STREAM, 0);
    
    // set socket address type to network type 
    struct sockaddr_in6 serv;
    std::memset(&serv, 0, sizeof(serv));
    // set type to ipv6
    serv.sin6_family = AF_INET6;
    // Set port
    serv.sin6_port = htons(52002);
    //convert to bin and add actual adress
    inet_pton(AF_INET6, "2001:bc8:35b0:c166::151", &serv.sin6_addr);
    
    // Bind the client to the parameters
    connect(client, (struct sockaddr*)&serv, sizeof(serv)); 
    std::cout << "port 52002" << std::endl;
    
    //Receive data and processing
    char buffer[1024] = { 0 }; 
    recv(client, buffer, sizeof(buffer), 0); 
    std::cout << "Message from serv: " << buffer << std::endl;
    std::string str = buffer;
    size_t startPos1 = str.find("square root of ") + strlen("square root of ");
    size_t startPos2 = str.find(" and multiply by ") + strlen(" and multiply by ");
    size_t endPos1 = str.find(" and multiply by ") - 1;
    size_t endPos2 = str.find(" =") - 1;
    std::string number1 = str.substr(startPos1, endPos1 - startPos1 + 1);
    int number1_ = std::stoi(number1);
    std::string number2 = str.substr(startPos2, endPos2 - startPos2 + 1);
    int number2_ = std::stoi(number2);
    double res = (std::sqrt((number1_))) * number2_; //static_cast<double>
    std::cout << std::setprecision (15) << res << std::endl;
    
    
    //seend - HERE I TRIED MANY DIFFERENT THINGS, SENDING A STRING, A DOUBLE AND A FLOAT
    //float res_ = static_cast<float>(res);
    //std::cout << res_ << std::endl;
    //std::string res_=std::to_string(res);
    //res_.erase(res_.size() - 4);
    //std::cout << "string:" << res_ << std::endl;
    //char buffer2[sizeof(res_)];
    //std::memcpy(buffer2, &res_, sizeof(res_));
    res = std::ceil(res * 100.0) / 100.0;
    std::string a = std::to_string(res);
    size_t pos = (a.find(".")+3);
    std::string b = a.substr(0, pos);
    std::cout << b << std::endl;
    const char* message = b.c_str(); 
    send(client, message, strlen(b.c_str()), 0); 
    std::cout << "sent :" << b.c_str() << ";" << message << std::endl;
    
    //Response?
    char buffer1[1024] = { 0 }; 
    recv(client, buffer1, sizeof(buffer1), 0); 
    std::cout << "Message from serv: " << buffer1 << std::endl;
   
    //closing&ending
    close(client); 
    return 0;
}


The outputs : THE ONLY TIME I GOT A RESPONSE FROM SERV :

port 52002
Message from serv: 
====================
 GO BACK TO COLLEGE
====================
You should tell me the answer of this math operation in less than 2 seconds !

Calculate the square root of 1 and multiply by 8807 = 
8807.00
sent :8807.008807
Message from serv: [!] Please only send int/float !

Out 1 -

port 52002
Message from serv: 
==================== GO BACK TO COLLEGE====================
You should tell me the answer of this math operation in less than 2 seconds !
Calculate the square root of 207 and multiply by 2092 = 
30098.6386403106
sent :30098.64

Out 2 -

port 52002
Message from serv: 
====================
 GO BACK TO COLLEGE
====================
You should tell me the answer of this math operation in less than 2 seconds !

Calculate the square root of 120 and multiply by 3958 = 
43357.7176521089
43357.72
sent :43357.72;43357.72

Out 3 -

port 52002
Message from serv: 
====================
 GO BACK TO COLLEGE
====================
You should tell me the answer of this math operation in less than 2 seconds !

Calculate the square root of 87 and multiply by 4134 = 
38559.3850054692
string:38559.39
sent :38559.39;38559.390000

Out 4 -

port 52002
Message from serv: 
====================
 GO BACK TO COLLEGE
====================
You should tell me the answer of this math operation in less than 2 seconds !

Calculate the square root of 56 and multiply by 9112 = 
68187.9642165683
68187.97sent :68187.97;R�����@R�����@�x�

Upvotes: 0

Views: 1515

Answers (2)

user4581301
user4581301

Reputation: 33932

recv(client, buffer1, sizeof(buffer1), 0); 

Ignores the return value. TCP is a streaming protocol and doesn't respect any messaging you implied with calls to send. Multiple sends can be packed into a single packet, the contents of a single send may be fragmented into multiple packets, and all manner of ugliness may happen as packets are transmitted. The only guarantee you get is the data will be delivered in the order it was sent or you get an error message. To ensure you got all the data you need you need to check recv's return code and loop until you get all of the data you expected. Since you seem to be sending and receiving null-terminated strings, you need to loop until you get the null terminator.

char ch = ' '; // init to anything but a null
while (ch) // loop until ch is null
{
    int rval = recv(client, &ch, 1, 0); 
    switch (rval)
    { 
        case 0: // disconnected without finishing message
            // log and exit
            return EXIT_FAILURE;
        case 1: // got a character. 
            // Put it in buffer
            break;
        default: // error code
            // decode error message into human readable string
            // log error 
            return EXIT_FAILURE;
    }
}

This is slow and ugly, but simple. Once you get this working, look into reading into a buffer and scanning the buffer for the null terminator. If the null is in the buffer, pass that message on for handling and preserve any data in the buffer after the null for future processing. If no null, recv again and keep going until the terminator is found, it's no longer possible that the collected data is a message, or the buffer is full.

In addition,

send(client, message, strlen(b.c_str()), 0); 

leaves out the null terminator (and doesn't check the return code to ensure the entire message was sent. Never ignore the return code!). The receiver has no way to know when to stop reading, and most likely keeps reading into garbage land or writes what it got into a character array and the display code doesn't know when to stop reading.

Solution: Send the null terminator

send(client, message, strlen(b.c_str()) + 1, 0); 

And do NOT forget to check the return code from send. send does not promise to send the entire message. It only promises to send what it can. The return code will tell you how much data was sent or that an error occurred. Note that not being able to send all of the data you intended to send is NOT considered an error.

Upvotes: 0

Get
Get

Reputation: 1

Okay, I actually solved it by coding the solution in python and thus unlocking the "solutions" category of website where I could find a version of the code in C that i used to inspire me.

Here is basically what I did (without spoiling) :

First I deleted the part between the two following code samples and restarted from scratch for the sending of the data as it is just an enormous mess

double res = (std::sqrt((number1_))) * number2_; //static_cast<double>
std::cout << std::setprecision (15) << res << std::endl;

===============================DELETE=========================================

//Response?
char buffer1[1024] = { 0 }; 

At the end of the processing of the data I output a double double res = (std::sqrt((number1_))) * number2_; //static_cast<double>

After what I use "osstring":std::ostringstream oss; to define "oss", i then assign the double "res" to "oss" (something like oss << [...] << std::setprecision(2) << res) That forces the string "oss" to have 2 decimals and they are accurate because I was using a double, which is more accurate than a float object in c++.

I then send back my data (after changing it from oss to a std::string ofc) : send(client, strn.c_str(), strlen(strn), 0);

Finally, I listen for the servers response as shown in the code above.

Upvotes: -1

Related Questions