Community discussions

MikroTik App
 
DavidHigh
just joined
Topic Author
Posts: 7
Joined: Thu Jun 04, 2009 4:24 pm

Need help in writing a pure Win32 C API application...

Thu Jun 04, 2009 5:25 pm

Hi everybody!
This is the first time I post here.

I need to write a Win32 application to interact with a RouterOS CPE... a kind of "very simplified" WinBox, with just some basic fuctions.

I've been asked to develop this application in pure C under the Win32 API, so to make a small, portable and high performant application which can start quite immediately on every Windows platform.

My project is to make a quite simple application, but I obviously need it to be multithreaded and, most of all, I need it to make use of sockets to interact with the RouterOS API!

I've made a testing app just to try the connection and communication between my C code and the RouterOS API... but unfortunally something seems to go wrong!

I don't know exactly what's happening, because if I try to communicate via socket with other applications, I don't seem to have problems... but I have big problems when I try to communicate with the CPE!
If I exchange messages via socket with a testing application I run on my PC (Serv/Term 1.0 from PC-Tools.net) as a server, I can perfectly send and receive messages in plain text.

With the CPE I can connect the socket without any problem... and I don't seem to have any problem in sending messages (even if I don't know what the CPE receives!!!), but I have problems in receiving messages back!

If I connect with the API port, the 8728, I simply don't receive ANYTHING back!!!
Nothing back after the connect(), nothing back after sending the "/login" string!!!

Just for testing, I've tried to "talk" with the CPE via the port 23, the Telnet standard... which SHOULD talk plain text... and after the connect() what I get back is this:

ÿýÿý ÿý#ÿý'

As you can imagine, if I try to send something to the CPE I can't do much... since I don't know what the CPE is answering me! And even if I try to send the username and then the password... nothing useful seems to happen!
The strange thing is that even the Serv/Term 1.0 application, started as a client, receives EXACTLY the same message from the CPE immediately after the connection!
It's not that good, but alas the problem seems to be not just on my code!

Can anybody help me?

Here is some snippet of the code I'm using for this tests:

/* some includes and declarations */

#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include <tchar.h>
#include <winsock.h>
#include "resource.h"

SOCKET clientSocket;

...

/* the code used to connect the socket... it seems to work well... */

SOCKADDR_IN addr;
short port;
char ipAddr[15]="192.168.168.112"; // the ip of the CPE I'm using for the tests

port=8728; // to test the API
//port=23;    // to test the telnet connection

WORD wVersionRequested = MAKEWORD(2,2); 
WSADATA wsaData; 
WSAStartup(wVersionRequested, &wsaData); 

addr.sin_family=AF_INET;
addr.sin_addr.S_un.S_addr=inet_addr(ipAddr);
addr.sin_port=htons(port);

clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(connect(clientSocket, (LPSOCKADDR)&addr, sizeof(addr))<0)
{
      MessageBox(hwndDlg, "Error connecting the socket!", "Connection error!", MB_OK | MB_ICONERROR);
      WSACleanup();
}
break;

...

/* the code used to send a message and receive an answer */

/*
    since at the present moment this code isn't multithreaded,
    it hangs the application if not receiving anything...
    I'll check it after being capable of communicating good!
*/

char* msg; // this will contain the API command line... "/login" for example...
char buf[256];
int bytesreceived=SOCKET_ERROR;

// these two lines get the command line from a control on the application GUI...
msg = (char*)GlobalAlloc(GPTR, len+1);
GetDlgItemText(hwndDlg, ID_COMMANDLINE, msg, len+1);
						
ZeroMemory(buf, sizeof(buf)); // I clear the buffer...

send(clientSocket, msg, len+1,0); // and send my message on the socket!

while(bytesreceived==SOCKET_ERROR)
{
      // this line hangs the application if trying to communicate using the API because of no response
      // and gets that bunch of strange characters posted before using the port 23
      bytesreceived = recv(clientSocket, buf, sizeof(buf)-1, 0);

      if (bytesreceived == 0 || bytesreceived == WSAECONNRESET)
     {
            MessageBox(hwndDlg, "Client closed the connection", "Connection closed!", MB_OK);
            break;
      }
      buf[bytesreceived] = '\0'; // I add the terminator to the received message...

      MessageBox(hwndDlg, buf, "Message received from socket:", MB_OK);
}

GlobalFree(msg);

This code works perfectly with the testing application mentioned before, Serv/Term 1.0 from PC-Tools.net, started as a server on the desired port.

The API service is obviously started in RouterOS, and it accepts connections from 0.0.0.0/0 (which should be ANYTHING, right?).

Any kind of hint that can help me to solve this problem is welcome!!! :)
 
User avatar
mrz
MikroTik Support
MikroTik Support
Posts: 7053
Joined: Wed Feb 07, 2007 12:45 pm
Location: Latvia
Contact:

Re: Need help in writing a pure Win32 C API application...

Fri Jun 05, 2009 7:30 am

Please read wiki article how API stream is formatted
http://wiki.mikrotik.com/wiki/API#Protocol

If you send something to router that is not followed by formatting rules, most likely you will not get a reply.
At the end of wiki article you can find python example that you can easily convert to C.
Also read this
http://mum.mikrotik.com/presentations/CZ09/townet.pdf
and here is the SDK
http://www.wispmax.com/media/CPE_SDK.zip
 
User avatar
Chupaka
Forum Guru
Forum Guru
Posts: 8709
Joined: Mon Jun 19, 2006 11:15 pm
Location: Minsk, Belarus
Contact:

Re: Need help in writing a pure Win32 C API application...

Fri Jun 05, 2009 12:50 pm

yeah, RouterOS API is not Telnet, it's more complex thing for human. and is very simple for programs =)
Just for testing, I've tried to "talk" with the CPE via the port 23, the Telnet standard... which SHOULD talk plain text...
wrong. Telnet is plaintext commands + control bytes, which are, for example,
ÿýÿý ÿý#ÿý'
:)
 
DavidHigh
just joined
Topic Author
Posts: 7
Joined: Thu Jun 04, 2009 4:24 pm

Re: Need help in writing a pure Win32 C API application...

Fri Jun 05, 2009 1:07 pm

wrong. Telnet is plaintext commands + control bytes...
OK... it's my fault! :?

I've checked that later... but thank you again for specification! ;)

I've never tried before to talk with devices using telnet protocol... just plain text between applications via sockets! Sorry about my lack of knowledge!!! :)

Anyway... I'm trying to understand the API stream protocol right now... but since I don't "speak" python it's not so easy for me to get to the point.
Other languages are confusing too... C# for example is too "high level" to be "translated" to pure C.

I hope to understand how the protocol works as soon as possible... in the meantime, as usual... hints are appreciated! ;)
 
User avatar
Chupaka
Forum Guru
Forum Guru
Posts: 8709
Joined: Mon Jun 19, 2006 11:15 pm
Location: Minsk, Belarus
Contact:

Re: Need help in writing a pure Win32 C API application...

Fri Jun 05, 2009 1:11 pm

the best hint is here: http://wiki.mikrotik.com/wiki/API#Protocol - it's not pure C, but it's pure English =) I made my Delphi implementation of API Client based only on information from that page
 
DavidHigh
just joined
Topic Author
Posts: 7
Joined: Thu Jun 04, 2009 4:24 pm

Re: Need help in writing a pure Win32 C API application...

Fri Jun 05, 2009 1:23 pm

the best hint is here: http://wiki.mikrotik.com/wiki/API#Protocol - it's not pure C, but it's pure English =) I made my Delphi implementation of API Client based only on information from that page
Mmm... the main problem is that english is not my native tongue!
There's something in that explanation that's not extremely clear to me... :?

Anyway... I'll be trying to understand how to translate that english explanation in C language!
Maybe I can look into the code of your Delphi client too... I've studied Pascal a lot of years ago... if I still remember something about it, it can come in handy! ;)
 
User avatar
Chupaka
Forum Guru
Forum Guru
Posts: 8709
Joined: Mon Jun 19, 2006 11:15 pm
Location: Minsk, Belarus
Contact:

Re: Need help in writing a pure Win32 C API application...

Fri Jun 05, 2009 1:57 pm

you may ask here if you have any questions - we'll help :)
 
DavidHigh
just joined
Topic Author
Posts: 7
Joined: Thu Jun 04, 2009 4:24 pm

Re: Need help in writing a pure Win32 C API application...

Wed Jun 10, 2009 1:49 pm

Hi again!

I've finally succeded in putting together some code to communicate with the API respecting the protocol! :D

Now I'm having some problems to generate the code required for the authentication!

The documentation does not explain the required passages to generate a correct code one by one, and I've tried to understand them from the exampe code in the other languages.
But since C is different from Delphi, Python, C#... and definitely much more "low level" than all of these... there must be something I'm lacking to obtain a working code!!!

Anyway... here's what I've understood... I hope someone of you could help me to fill what I'm missing!

1) you send the login request to the API
2) the API answers you with a challenging code, a string of 32 characters.
3) you put together a string made of the hex character 0x00, the password and the challenging code, all appended one by one in this order.

I create this string using this C code:
wsprintf(hashcode, "\x0%x%s", password, hashin);
I've tried the wsprintf format in all these ways:
"\x0%s%s", "\x00%s%s", "\x0%x%s", "\x00%x%s", "00%x%s", "\x0%x%x", "\x00%x%x"
and similar combinations.
Unfortunately no one seems to be the one I need.

4) you generate the md5 hash of this string (let's call this "hashcode")
5) you send the login request together with the data to the API using this format:
/login
=name=admin
=response=001ea726ed53ae38520c8334f82d44c9f2
with the "response field" value= "00"+hashcode (to write it in a metalanguage format), exactly as stated in the wiki.

Well... at this point I always get the !trap code and the answer is that I cannot login!

Since the username and the password are correct, the only solution is that I miss something in generating the hashcode.

I believe there's something I miss between the steps 2) and 3)... mainly in the 3)!!!

From the code written in other languages is difficult to understand what exactly is being done, since a lot of mechanisms are automatic working at a "higher level" than C. In C you have to manage everything!

Maybe I need to convert the "hashin" code in some way?
I've tried processing that in step 3) as a pure string... but since it's obviously an hexadecimal string, I believe it must be converted in some way. So I've tried processing every 2 characters of the code and converting it in a single char using the strtol function with a base 16. But it doesn't work! Maybe it must be processed in a different way, but I don't understand how!

So... anyone has an idea of what I must do to successfully login??? ;)

Thank you very much!
 
User avatar
Chupaka
Forum Guru
Forum Guru
Posts: 8709
Joined: Mon Jun 19, 2006 11:15 pm
Location: Minsk, Belarus
Contact:

Re: Need help in writing a pure Win32 C API application...

Thu Jun 11, 2009 11:56 am

challenge code should be converted from hex notation to binary string. also make sure that you are not using null-terminated strings, because anything you add after \x00 will be ignored. check your resulting string length after concatenation
 
DavidHigh
just joined
Topic Author
Posts: 7
Joined: Thu Jun 04, 2009 4:24 pm

Re: Need help in writing a pure Win32 C API application...

Fri Jun 12, 2009 11:45 am

OK... so...

I've written a "stub" to test the code generating procedure. JUST that procedure... since it's my main problem now!

I took a challenging code and a known result, and tried to test them in both PHP and C.

I've tried doing a 1:1 confrontation with PHP code...

Here's the testing stub in PHP...
$hashin = "cd9d84bd036d939af3b56b290d563c30";
$expectedcode="00211ad3fc0d80666b063ced749f792ace";
$username="admin";
$password="";
				
echo "Challenge code is: <b>".$hashin."</b><br>";

$a=chr(0);
$b=$password;
$c=pack('H*', $hashin);

echo "The code is being processed from: <b>".$a."</b> + <b>".$b."</b> + <b>".$c."</b><br>";

$generatedcode="00".md5($a.$b.$c);

echo "MD5 encoding of this code is: <b>".$generatedcode."</b><br>";

echo "Code expected by the API is: <b>".$expectedcode."</b><br>";

if($expectedcode==$generatedcode)
	echo "Codes are <b>EQUAL!</b>";
else
	echo "Codes are <b>DIFFERENT!!!</b>";
This code, obviously, WORKS PERFECTLY!!!

The big BIG problem working in C is the "chr(0);", which of course is needed, but in C stands for "string terminator"!!!

You say to beware of string terminators, and you are right... but how can I beware of them if I must MD5 process them??? :?

Here's the testing stub I'm using in C at the present moment:
char hashin[BUFFER_SIZE]="cd9d84bd036d939af3b56b290d563c30";
char hashout[BUFFER_SIZE];
char asciicode[BUFFER_SIZE];
char expectedcode[BUFFER_SIZE]="00211ad3fc0d80666b063ced749f792ace";

char username[]="admin";
char password[]="";

hex2bin(asciicode, hashin); // this function converts the Hex challenge code to ASCII... it WORKS as in PHP... TESTED!

wsprintf(asciicode, "%s%s%s", 0x00, password, asciicode);

md5_hash(asciicode, hashout); // MD5 function... tested without string terminator and it WORKS PERFECTLY as in PHP!

wsprintf(buf, "MD5 encoding of this code is: 00%s\r\nCode expected by the API is: %s", hashout, expectedcode);

I've tried to change this call:
wsprintf(asciicode, "%s%s%s", 0x00, password, asciicode);
with all of these:
wsprintf(asciicode, "%s%s%s", 0x00, password, asciicode);
wsprintf(asciicode, "%c%s%s", 0x00, password, asciicode);
wsprintf(asciicode, "%d%s%s", 0x00, password, asciicode);
wsprintf(asciicode, "%x%s%s", 0x00, password, asciicode);
wsprintf(asciicode, "%s%s%s", "\x0", password, asciicode);
wsprintf(asciicode, "%c%s%s", "\x0", password, asciicode);
wsprintf(asciicode, "%d%s%s", "\x0", password, asciicode);
wsprintf(asciicode, "%x%s%s", "\x0", password, asciicode);
wsprintf(asciicode, "%s%s%s", "\x00", password, asciicode);
wsprintf(asciicode, "%c%s%s", "\x00", password, asciicode);
wsprintf(asciicode, "%d%s%s", "\x00", password, asciicode);
wsprintf(asciicode, "%x%s%s", "\x00", password, asciicode);
obviously, no one of these works!!!

This is my testing stub! Starting from the same challenging code, PHP produces the expected hash code, while C doesn't!

The silly thing is that if I do the processing WITHOUT that damn "chr(0);", the MD5 produced code is EXACTLY the same both in C and PHP... and this means that the problem is in that character, which in C is considered a string terminator... but must be processed to obtain the expected code!!! And I can't find a working workaround!!!

Just for completeness, here are the hex2ascii and md5_hash C functions I use:
int unhex(char c) {
	if (c >= '0' && c <= '9')
		return (c - '0');
	if (c >= 'a' && c <= 'f')
		return (c - 'a' + 10);
	if (c >= 'A' && c <= 'F')
		return (c - 'A' + 10);
	return (-1);
}

int hex2bin(char *bin, const char *hex) {
	int i = 0;
	int j = 0;

	/* Trim string if comments present */
	if (strchr(hex, '#') != NULL)
		*strchr(hex, '#') = 0;
	if (strchr(hex, '*') != NULL)
		*strchr(hex, '*') = 0;
	if (strchr(hex, '\'') != NULL)
		*strchr(hex, '\'') = 0;

	for (i = 0; i < strlen(hex); i++)
		{
			if (hex[i] >= '0' && unhex(hex[i]) < 0)
			{
				return(-1);
			}
		}
	for (i = 0; i < strlen(hex); i++)
		{
			if (hex[i] < '0')
				continue;
			if (hex[i] >= '0' && hex[i+1] >= '0')
			{
				bin[j++] = unhex(hex[i])*16+unhex(hex[i+1]);
				i++;    // skip one
				continue;
			}
			if (hex[i] >= '0')
			{
				bin[j++] = unhex(hex[i]);
			}
		}
	return (j);
}
The MD5 function has been produced starting from the .c code and the .h files (and including them!) from the official MD5 rfc!
#include "md5.h"

int md5_hash(const char* strin, char* strout)
{
	int status = 0;
	if(strlen(strin)>0){
		md5_state_t state;
		md5_byte_t digest[16];
		char hex_output[16*2 + 1];
		int di;

		md5_init(&state);
		md5_append(&state, (md5_byte_t *)strin,strlen(strin));
		md5_finish(&state, digest);

		for(di=0;di<16;++di)
		{
			wsprintf(hex_output + di * 2, "%02x", digest[di]);
		}
		strcpy(strout, hex_output);
		status=1;
	}
	return status;
}
 
User avatar
Chupaka
Forum Guru
Forum Guru
Posts: 8709
Joined: Mon Jun 19, 2006 11:15 pm
Location: Minsk, Belarus
Contact:

Re: Need help in writing a pure Win32 C API application...

Fri Jun 12, 2009 12:07 pm

at first, see md5_hash function - if first byte of strin is 0x00 - it won't work, length(strin) will be zero

I may advise you not to use char* for that - try to use byte arrays, it will work with 0x00 normally. because there sometimes zeroes in returned values (for example, AgentID in DHCP Server Leases), so it will be hard to work with null-terminated strings
 
Mplsguy
MikroTik Support
MikroTik Support
Posts: 227
Joined: Fri Jun 06, 2008 5:06 pm

Re: Need help in writing a pure Win32 C API application...

Fri Jun 12, 2009 4:52 pm

You do not have to concat everything in one string to do md5 - you can update hash part by part. Note that challenge, when converted to binary may also contain 0x0, so reffer to it by length you already know (16 bytes)!

      // password - null terminated password string
      // challenge - challenge received from router converted to binary 16 byte array

      static char zero[1] = {0}; // necessary zero byte

      md5_state_t state;
      md5_byte_t digest[16];
      char hex_output[16*2 + 1];
      int di;

      md5_init(&state);
      md5_append(&state, (md5_byte_t *)zero, 1);
      md5_append(&state, (md5_byte_t *)password,strlen(password));
      md5_append(&state, (md5_byte_t *)challenge,16); // do NOT use strlen here!
      md5_finish(&state, digest);

      for(di=0;di<16;++di)
      {
         wsprintf(hex_output + di * 2, "%02x", digest[di]);
      }
      strcpy(strout, hex_output);
 
DavidHigh
just joined
Topic Author
Posts: 7
Joined: Thu Jun 04, 2009 4:24 pm

Re: Need help in writing a pure Win32 C API application...

Fri Jun 12, 2009 6:50 pm

IT WORKS!!!! :D :D :D

Mplsguy you are great!!! ;)

I've been spending almost all the day trying to solve this mistery. I already came to the solution you exposed, and I wrote a function to create the code exactly the same way you wrote!

But I was missing one thing... this:
static char zero[1] = {0}; // necessary zero byte
I was insisting in using that damn '\x0' character... or similar solutions... but, obviously, I've never been able to produce a valid MD5 code!
The idea of representing the zero byte that way was brilliant, and it resolved my login problems!!! :D

Thank you very much!!! ;)
 
fischer
just joined
Posts: 2
Joined: Fri Sep 07, 2012 1:21 pm

Re: Need help in writing a pure Win32 C API application...

Mon Sep 10, 2012 11:22 am

Hi DavidHigh,
I am trying to make the same application. Could you send me the code please? I would really appreciate your help.
 
User avatar
boen_robot
Forum Guru
Forum Guru
Posts: 2400
Joined: Thu Aug 31, 2006 4:43 pm
Location: europe://Bulgaria/Plovdiv

Re: Need help in writing a pure Win32 C API application...

Wed Apr 24, 2013 6:21 pm

I've tried doing a 1:1 confrontation with PHP code...Welcome to contace me.
This forum doesn't allow members to PM each other, so I'll ask you publicly:

What do you mean by that? There are already several PHP clients, including one of my own.

Who is online

Users browsing this forum: Jonty, phongle and 38 guests