Search code examples
c++socketsc++11tcptelnet

TCP server message extra characters c++


I think I am missing something and doesn't make sense.

I am writing pretty simple TCP server, everything works pretty much as expected, but when a message saying "500 LOGIN FAILED" gets send over network, it get interpreted as "$500 LOGIN FAILED".

I am testing my server using telnet on localhost

here is simplified version of my code

recv(c_sockfd, buf, BUFFSIZE, 0))
inBuffer.push_back(buf);
auto messageToSend = checkResponse(parseBuffer(inBuffer.back()));
//get the second thing in the tuple
outBuffer.push_back(std::get<1>(messageToSend));
bzero(buf, sizeof(buf));
send(c_sockfd, &outBuffer.back(), sizeof( outBuffer.back() ), 0)

in the checkResponse func, I am implementing logic to decide what message to send, and somehow when I send ERROR message the extra character is added at the beginning of the message.

EXAMPLE 1:

Connected to localhost.
Escape character is '^]'.
200 LOGIN
Robot345\r\n
201 PASSWORD
674\r\n
202 OK
INFO iasdijasdjiajsdiajdijasidjiansdjsdvhdf dfvsdfsdf\r\n
&501 SYNTAX ERROR 

Notice the "&" character

EXAMPLE 2:

Connected to localhost.
Escape character is '^]'.
200 LOGIN
Robot345\r\n
201 PASSWORD
456\r\n
$500 LOGIN FAILED

Notice the "$" character

Does anyone have any idea where the extra characters could be added to the string?

I didn't want to include full code, because the requirement was to have all in one file, which makes it difficult to read. Here it goes tho.

FULL CODE:

#include <iostream>
#include <regex>
#include <iterator>
#include <vector>
#include <sstream>
#include <string>
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <tuple>

#define MIN_PORT 3000
#define MAX_PORT 3999
#define BUFFSIZE 1000
/**
 0 - LOGIN SUCCESSFUL, USERNAME IS IN THE BUFFER
 1 - PASSWORD CHECK
 2 - PASSWORD OK, COMMUNICATING
 */
int state = 0;
std::string username, password;
/**
 CHECKS ENTERED PASSWORD BASES ON THE SUM OF ASCII VALUES OF USERNAME
 @return: true on success, false otherwise
 */
bool checkPassword(std::string password){
    std::istringstream sst;
    sst.str(username);
    unsigned char byte = '\0';
    int value = 0;
//    std::cout << "byte poprve: " << byte << std::endl;
//    std::cout << "byte poprve INT: " << (int) byte << std::endl;

    while (sst >> byte) {
        std::cout << "podruhe: " << byte << std::endl;
        std::cout << "podruhe INT: " << (int) byte << std::endl;
        std::cout << "Prubezna SUMA: " << (int) value << std::endl;
        value += byte;
    }
    std::cout << "suma: " << value << std::endl;
    // Check the entered password
    if (password == std::to_string(value)) {
        return true;
    }

    return false;
}
/**
 CHECKS MESSAGE SYNTAX BASED ON THE STATE WE ARE IN
 CHECKS PASSWORD
 CHECKS CHECK SUM
 @param response <string type (if available), string message to parse>
 @return TRUE on success, FALSE otherwise
 */
bool checkMessage(std::tuple<std::string,std::string> response){

    auto messageToParse = std::get<1>(response);
    std::string delimeter = "\r\n";
    std::string::size_type pos = messageToParse.find(delimeter);
    //INITIAL CHECK
    if (pos < 1){
        return false;
    }
    //somehow you have to multiply the length by 2
    auto parsedMessage = messageToParse.substr(0,pos - 2*delimeter.length());
    std::cout << parsedMessage << " : THIS IS YOUR PARSED MESSAGE";

    //USERNAME
    if (state == 0) {
        username = parsedMessage;
        return true;
    }

    //PASSWORD CHECK
    if (state == 1 && checkPassword(parsedMessage)) {
        password = parsedMessage;
        return true;
    }

    if (state == 2) {
        std::string type = std::get<0>(response);

        //INFO
        if( type == "I" ){
            return true;
        }
        //PHOTO
        if ( type == "F") {
            return true;
        }

    }

    return false;
}
/**
 THIS FUNC WILL CHECK RESPONSE FROM THE ROBOT, AND DECIDE WHAT TO DO BASED ON THE STATE
 @return tuple<bool TRUE if everything is right,std::string MESSAGE to send to the robot>
 */
std::tuple<bool,std::string> checkResponse(std::tuple<std::string, std::string> response){

    if (state == 0) {
        if (checkMessage(response)) {
            std::cout << state << " / / state" << std::endl;
            return std::make_tuple(true, "201 PASSWORD\r\n");
        }
    }
    if (state == 1) {
        // TADY BUDE JESTE PODMINKA, ZE HESLO JE SPRAVNE
        if(checkMessage(response)){
            std::cout << state << " / / / state" << std::endl;
            return std::make_tuple(true, "202 OK\r\n");
        }else{
            std::cout << state << " / / / / state" << std::endl;
            return std::make_tuple(false, "500 LOGIN FAILED\r\n");
        }
    }
    if (state == 2) {

        if (checkMessage(response)) {
            std::cout << state << " / / / / / state" << std::endl;
            return std::make_tuple(true, "202 OK\r\n");
        }else{
            std::cout << state << " / / / / / / state" << std::endl;
            return std::make_tuple(false, "501 SYNTAX ERROR \r\n");
        }
    }
    std::cout << state << " / / / / / / / / state" << std::endl;
    return std::make_tuple(false, "unexpected result");
}
/**
 This func will parse the incoming buffer
 @param buffer incoming buffer
 @return tuple <String type of message (U,I,P,F),  String actual message>
 */
std::tuple<std::string, std::string> parseBuffer(std::string buffer){
    if (state == 0) {
        return std::make_tuple("U", buffer);
    }
    if (state == 1) {
        return std::make_tuple("P", buffer);
    }else{
        std::string delimeter = " ";
        std::string::size_type pos = buffer.find(delimeter);
        std::string type = buffer.substr(0, pos );
        std::string message = buffer.erase(0, pos + delimeter.length());

        return std::make_tuple(type, message);
    }


}


int main(int argc, char *argv[])
{
    char   buf[BUFFSIZE];
    std::vector<std::string> outBuffer;
    std::vector<std::string> inBuffer;
    int    sockfd, c_sockfd;
    sockaddr_in my_addr, rem_addr;
    socklen_t    rem_addr_length;
    int    mlen;
    const int PORT_NUM = atoi(argv[1]);

    if( (PORT_NUM > MAX_PORT) || (PORT_NUM < MIN_PORT)){
        perror("Port number is not acceptable");
         exit(-1);
    }

    if ((sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
    {
        perror("Socket nelze otevrit");
        exit(-1);
    }

    bzero(&my_addr, sizeof(my_addr));
    my_addr.sin_family = AF_INET;
    my_addr.sin_port = htons(PORT_NUM);
    std::cout << PORT_NUM << " PORT NUM" << std::endl;
    if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)) == -1)
    {
        perror("Chyba v bind");
        close(sockfd); exit(1);
    }

    if (listen(sockfd, SOMAXCONN) == -1)
    {
        perror("Nelze provest listen");
        close(sockfd); exit(1);
    }

    while (1)
    {
        rem_addr_length=sizeof(rem_addr);
        c_sockfd = accept(sockfd, (struct sockaddr*) &rem_addr, &rem_addr_length);
        if ( c_sockfd == -1)
        {
            perror("Nelze accept");
            close(sockfd); exit(1);
        }
        ///FIRST MESSAGE
        std::string ok = "200 LOGIN\r\n";
        send(c_sockfd, &ok, sizeof(std::string), 0);

        if ((mlen = recv(c_sockfd, buf, BUFFSIZE, 0)) == -1)
            perror("Chyba pri cteni");
        else{

            while (mlen)
            {

                ///---------- MAIN PART--------------
                //This is where comunication is happening
                inBuffer.push_back(buf);
                //Parse the buffer, check the message and
                auto messageToSend = checkResponse(parseBuffer(inBuffer.back()));
                //get the second thing in the tuple
                outBuffer.push_back(std::get<1>(messageToSend));
                bzero(buf, sizeof(buf));
                ///---------- MAIN PART--------------
                state++;
                std::cout << state << " state num" << std::endl;
                if (send(c_sockfd, &outBuffer.back(), sizeof( outBuffer.back() ), 0) == -1)
                {
                    perror("Chyba pri zapisu");
                    break;
                }else{

                }

                std::cout << inBuffer.back() << std::endl;

                if ((mlen = recv(c_sockfd, buf, BUFFSIZE, 0)) == -1)
                {
                    perror("Chyba pri cteni");
                    break;
                }
            }

            close(c_sockfd);

            }
    }
}

Solution

  • The problem is with this:

    std::vector<std::string> outBuffer;
    

    and this:

    send(c_sockfd, &outBuffer.back(), sizeof( outBuffer.back() ), 0)
    

    You can't send std::string objects over the network. You must send the string it contains. Those are two very different things.

    For a simple fix, do e.g.

    send(c_sockfd, outBuffer.back().c_str(), outBuffer.back().length(), 0)
    

    If you want to send the terminating null then add one to the length to send.


    For more details, while implementations of std::string is allowed to optimize small strings to be contained inside the actual object, otherwise a std::string object is really nothing more than a size and a pointer to the actual string (implementations might have other members as well).

    A pointer is unique to the currently running process on the host system. You can't transfer a pointer over the network. You can't even save a pointer to a file and then load it again and have it working in a new process (even if it's a process from the same program).

    By sending the std::string object, all you're really sending is this pointer. So on the receiving side it have no idea what you're really sending and how it should treat that.