1
votes

I am trying to develop a mumble client. To connect to a mumble server (also called murmur) I need to follow steps listed in wiki "https://mumble-protocol.readthedocs.org/en/latest/establishing_connection.html#connect".

I am coding in Windows Visual Studios in C++.

Step 1 is to establish a TCP connection to server and do a TSLv1 handshake.

I tried to establish a TCP connection and successfully did a TSL handshake. Then I tried to read data using SSL_read(ssl, buf, sizeof(buf)), the function returns 55 (which is number of bytes it read). However, the buffer is still empty when I try to log it out to console.

I log it out in 3 ways:

  1. printf() - since buf is declared as char array, I print it out as using %s in printf()
  2. cout - print out raw buffer using std::cout
  3. I was aware the murmur might send binary data back to me, so I tried to force cast the first 4 bytes into a uint32_t and tried to see if I am even getting an data back. It printed 0, meaning the data was just nothing.

I know that my client is reading some data because of 3 reasons:

  1. SSL_read returns number of data read which is 55 in this case.
  2. I tried to connect to google IP at port 433. The program kept running after the connection was done. It never finished running and it SSL_read didn't return any data. (google doesn't send data like that, so this makes sense)
  3. Mumble Wiki says after successful connection, the server should send its version information.

My question is that why can't I see the data that is being read. Is there anything that I am missing in connecting to SSL or is there something wrong with the way I am reading from server.

For those who has made a mumble client: How to get version information from the murmur server ?

Here is my code. Check my output

#include "stdafx.h"

#define _WINSOCK_DEPRECATED_NO_WARNINGS

#pragma comment(lib,"Ws2_32.lib")
/*****************************************************************************/
/*** ssl_client.c                                                          ***/
/***                                                                       ***/
/*** Demonstrate an SSL client.                                            ***/
/*****************************************************************************/

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <memory.h>
#include <errno.h>
#include <sys/types.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>

//#include <sys/socket.h>
//#include <resolv.h>
//#include <netdb.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <stdint.h>
#define FAIL    -1

/*---------------------------------------------------------------------*/
/*--- OpenConnection - create socket and connect to server.         ---*/
/*---------------------------------------------------------------------*/
SOCKET OpenConnection(const char *hostname, int port)
{
    SOCKET sd;
    struct hostent *host;
    struct sockaddr_in addr;

    WSADATA wsadata;

    int error = WSAStartup(0x0202, &wsadata);

    //Did something happen?
    if (error)
        return false;

    //Did we get the right Winsock version?
    if (wsadata.wVersion != 0x0202)
    {
        WSACleanup(); //Clean up Winsock
        return false;
    }


    printf("Starting connection\n");
    /*
    if ((host = gethostbyname(hostname)) == NULL)
    {
        printf("No Host\n");
        perror(hostname);
        abort();
    }*/

    //memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(hostname);

    sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    int err = connect(sd, (SOCKADDR *)&addr, sizeof(addr));
    printf("Socket: %d\n", sd);
    if (err!= 0)
    {
        closesocket(sd);
        perror(hostname);
    }
    else 
        printf("Connected to %s:%d\n", hostname, port);
        return sd;
}

/*---------------------------------------------------------------------*/
/*--- InitCTX - initialize the SSL engine.                          ---*/
/*---------------------------------------------------------------------*/
SSL_CTX* InitCTX(void)
{
    const SSL_METHOD *method;
    SSL_CTX *ctx;
    SSL_library_init(); /* load encryption & hash algorithms for SSL */
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();       /* Load cryptos, et.al. */      /* Bring in and register error messages */
    method = TLSv1_client_method();     /* Create new client-method instance */
    ctx = SSL_CTX_new(method);          /* Create new context */
    if (ctx == NULL)
    {
        ERR_print_errors_fp(stderr);
    }
    return ctx;
}

/*---------------------------------------------------------------------*/
/*--- ShowCerts - print out the certificates.                       ---*/
/*---------------------------------------------------------------------*/
void ShowCerts(SSL* ssl)
{
    X509 *cert;
    char *line;

    cert = SSL_get_peer_certificate(ssl);   /* get the server's certificate */
    if (cert != NULL)
    {
        printf("Server certificates:\n");
        line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
        printf("\t Subject: %s\n", line);
        OPENSSL_free(line);                         /* free the malloc'ed string */
        line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
        printf("\t Issuer: %s\n", line);
        OPENSSL_free(line);                         /* free the malloc'ed string */
        X509_free(cert);                    /* free the malloc'ed certificate copy */
    }
    else
        printf("No certificates.\n");
}

/*---------------------------------------------------------------------*/
/*--- main - create SSL context and connect                         ---*/
/*---------------------------------------------------------------------*/
int main()
{
    SSL_CTX *ctx;
    SOCKET server;
    SSL *ssl;
    char buf[1024];
    int bytes;
    char *hostname;
    hostname = <put host name here>;
    int portnum = <put port here>;

    ctx = InitCTX();
    printf("Initialised SSL\n");
    server = OpenConnection(hostname, portnum);
    ssl = SSL_new(ctx);                     /* create new SSL connection state */
    SSL_set_fd(ssl, server);                /* attach the socket descriptor */
    if (SSL_connect(ssl) == FAIL)           /* perform the connection */
        ERR_print_errors_fp(stderr);
    else
    {
        int handshake = SSL_do_handshake(ssl);
        printf("Handshake Status: %i\n", handshake);

        char *msg = "Hello???";
        printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
        ShowCerts(ssl);/* get any certs */

        const char* state = SSL_state_string(ssl);
        printf("State: %s\n", state);

        //SSL_write(ssl, msg, strlen(msg));         /* encrypt & send message */
        bytes = SSL_read(ssl, buf, sizeof(buf));    /* get reply & decrypt */
        buf[bytes] = '\0';
        printf("Received %d bytes: \"%s\"\n", bytes,buf);

        std::cout <<"Raw Buffer: " << buf[2] << "\n";

        uint32_t *test = (uint32_t *)buf;
        printf("Buffer force casted to 32 bit int: %u\n", *test);

        SSL_free(ssl);                              /* release connection state */
    }
    closesocket(server);                                    /* close socket */
    SSL_CTX_free(ctx);                              /* release context */
    return 0;
}
2

2 Answers

1
votes

Assuming you are trying to read the first (Version) message, which you didn't state, and assuming that 'common string' means 'null-terminated string', and assuming you read a complete message into buf in the first place:

printf("Version %04x release %s OS %s OS version %s\n",
    *(int*)buf,
    buf+4,
    buf+4+strlen(buf+4)+1,
    buf+4+strlen(buf+4+strlen(buf+4)+1)+1
    );

... or similar. Note that the version info is in hex not decimal.

E&OE

1
votes

Through EJP answer, I was able to find a solution to my problem. Basically, Mumble uses google protocol buffer to send and receive data. Google protocol buffers sends 6 bytes of information regarding the data before actually sending the data itself. Those 6 bytes in my case were '/0' for some reason. So the reason my force cast didn't work was that there was nothing read in those 6 bytes.

However, when I printed the buffer using EJP's, code

printf("Version %04x release %s OS %s OS version %s\n",
*(int*)buf,
buf+4,
buf+4+strlen(buf+4)+1,
buf+4+strlen(buf+4+strlen(buf+4)+1)+1
);

I was able to see the data after those 6 bytes. The correct way to process data is not by neglecting the first 6 bytes, but to implement google protocol buffers to interpret those 6 bytes and the data sent by protocol buffers. This way you can get data in any language you want(that is what protocol buffer does).

To get more info visit Google Protocol Buffers.

If you are creating a mumble client in C++ ,you might also want to visit Mumlib Library.