API in C using winsock: Difference between revisions
Jump to navigation
Jump to search
m link to API |
m →See also=: added link to API in C |
||
Line 923: | Line 923: | ||
==See also=== | ==See also=== | ||
[[API_in_C | Original implementation for Linux]] | |||
[[API]] | [[API]] |
Revision as of 15:26, 31 August 2011
This is an implementation of RouterOS API written on C that using winsock2 for compatibility with Windows operation systems. It's based on API in C (You should use all sources from here, except mikrotik-api.c, that incompatible with Windows). Use compatible mikrotik-api.c source from below.
If your compiler does not support "#pragma comment", add ws2_32.lib to your linker manually.
RouterOS API Source file (mikrotik-api.c)
/******************************************************************** * Some definitions * Word = piece of API code * Sentence = multiple words * Block = multiple sentences (usually in response to a sentence request) * int fdSock; int iLoginResult; struct Sentence stSentence; struct Block stBlock; fdSock = apiConnect("10.0.0.1", 8728); // attempt login iLoginResult = login(fdSock, "admin", "adminPassword"); if (!iLoginResult) { apiDisconnect(fdSock); printf("Invalid username or password.\n"); exit(1); } // initialize, fill and send sentence to the API initializeSentence(&stSentence); addWordToSentence(&stSentence, "/interface/getall"); writeSentence(fdSock, &stSentence); // receive and print block from the API stBlock = readBlock(fdSock); printBlock(&stBlock); apiDisconnect(fdSock); ********************************************************************/ // C implementation of Mikrotik's API rewritten for Winsock2 (for windows) // Updated 28 August 2011 by hel #include <stdio.h> #include <winsock2.h> #pragma comment(lib, "ws2_32.lib") #include <windows.h> #include <string.h> #include <stdlib.h> #include "md5.h" #include "mikrotik-api.h" /******************************************************************** * Connect to API * Returns a socket descriptor ********************************************************************/ int apiConnect(char *szIPaddr, int iPort) { int fdSock; struct sockaddr_in address; int iConnectResult; int iLen; WORD versionWanted = MAKEWORD(1,1); WSADATA wsaData; WSAStartup(versionWanted, &wsaData); fdSock = socket(AF_INET, SOCK_STREAM, 0); address.sin_family = AF_INET; address.sin_addr.s_addr = inet_addr(szIPaddr); address.sin_port = htons(iPort); iLen = sizeof(address); DEBUG ? printf("Connecting to %s\n", szIPaddr) : 0; iConnectResult = connect(fdSock, (struct sockaddr *)&address, iLen); if(iConnectResult==-1) { perror ("Connection problem"); exit(1); } else { DEBUG ? printf("Successfully connected to %s\n", szIPaddr) : 0; } // determine endianness of this machine // iLittleEndian will be set to 1 if we are // on a little endian machine...otherwise // we are assumed to be on a big endian processor iLittleEndian = isLittleEndian(); return fdSock; } /******************************************************************** * Disconnect from API * Close the API socket ********************************************************************/ void apiDisconnect(int fdSock) { DEBUG ? printf("Closing socket\n") : 0; closesocket(fdSock); } /******************************************************************** * Login to the API * 1 is returned on successful login * 0 is returned on unsuccessful login ********************************************************************/ int login(int fdSock, char *username, char *password) { struct Sentence stReadSentence; struct Sentence stWriteSentence; char *szMD5Challenge; char *szMD5ChallengeBinary; char *szMD5PasswordToSend; char *szLoginUsernameResponseToSend; char *szLoginPasswordResponseToSend; md5_state_t state; md5_byte_t digest[16]; char cNull[1] = {0}; writeWord(fdSock, "/login"); writeWord(fdSock, ""); stReadSentence = readSentence(fdSock); DEBUG ? printSentence (&stReadSentence) : 0; if (stReadSentence.iReturnValue != DONE) { printf("error.\n"); exit(0); } // extract md5 string from the challenge sentence szMD5Challenge = strtok(stReadSentence.szSentence[1], "="); szMD5Challenge = strtok(NULL, "="); DEBUG ? printf("MD5 of challenge = %s\n", szMD5Challenge) : 0; // convert szMD5Challenge to binary szMD5ChallengeBinary = md5ToBinary(szMD5Challenge); // get md5 of the password + challenge concatenation md5_init(&state); md5_append(&state, cNull, 1); md5_append(&state, (const md5_byte_t *)password, strlen(password)); md5_append(&state, (const md5_byte_t *)szMD5ChallengeBinary, 16); md5_finish(&state, digest); // convert this digest to a string representation of the hex values // digest is the binary format of what we want to send // szMD5PasswordToSend is the "string" hex format szMD5PasswordToSend = md5DigestToHexString(digest); DEBUG ? printf("szPasswordToSend = %s\n", szMD5PasswordToSend) : 0; // put together the login sentence initializeSentence(&stWriteSentence); addWordToSentence(&stWriteSentence, "/login"); addWordToSentence(&stWriteSentence, "=name="); addPartWordToSentence(&stWriteSentence, username); addWordToSentence(&stWriteSentence, "=response=00"); addPartWordToSentence(&stWriteSentence, szMD5PasswordToSend); DEBUG ? printSentence(&stWriteSentence) : 0; writeSentence(fdSock, &stWriteSentence); stReadSentence = readSentence(fdSock); DEBUG ? printSentence (&stReadSentence) : 0; if (stReadSentence.iReturnValue == DONE) { return 1; } else { return 0; } } /******************************************************************** * Encode message length and write it out to the socket ********************************************************************/ void writeLen(int fdSock, int iLen) { char *cEncodedLength; // encoded length to send to the api socket char *cLength; // exactly what is in memory at &iLen integer cLength = calloc(sizeof(int), 1); cEncodedLength = calloc(sizeof(int), 1); // set cLength address to be same as iLen cLength = (char *)&iLen; DEBUG ? printf("length of word is %d\n", iLen) : 0; // write 1 byte if (iLen < 0x80) { cEncodedLength[0] = (char)iLen; send (fdSock, cEncodedLength, 1, 0); } // write 2 bytes else if (iLen < 0x4000) { DEBUG ? printf("iLen < 0x4000.\n") : 0; if (iLittleEndian) { cEncodedLength[0] = cLength[1] | 0x80; cEncodedLength[1] = cLength[0]; } else { cEncodedLength[0] = cLength[2] | 0x80; cEncodedLength[1] = cLength[3]; } send (fdSock, cEncodedLength, 2, 0); } // write 3 bytes else if (iLen < 0x200000) { DEBUG ? printf("iLen < 0x200000.\n") : 0; if (iLittleEndian) { cEncodedLength[0] = cLength[2] | 0xc0; cEncodedLength[1] = cLength[1]; cEncodedLength[2] = cLength[0]; } else { cEncodedLength[0] = cLength[1] | 0xc0; cEncodedLength[1] = cLength[2]; cEncodedLength[2] = cLength[3]; } send (fdSock, cEncodedLength, 3, 0); } // write 4 bytes // this code SHOULD work, but is untested... else if (iLen < 0x10000000) { DEBUG ? printf("iLen < 0x10000000.\n") : 0; if (iLittleEndian) { cEncodedLength[0] = cLength[3] | 0xe0; cEncodedLength[1] = cLength[2]; cEncodedLength[2] = cLength[1]; cEncodedLength[3] = cLength[0]; } else { cEncodedLength[0] = cLength[0] | 0xe0; cEncodedLength[1] = cLength[1]; cEncodedLength[2] = cLength[2]; cEncodedLength[3] = cLength[3]; } send (fdSock, cEncodedLength, 4, 0); } else // this should never happen { printf("length of word is %d\n", iLen); printf("word is too long.\n"); exit(1); } } /******************************************************************** * Write a word to the socket ********************************************************************/ void writeWord(int fdSock, char *szWord) { DEBUG ? printf("Word to write is %s\n", szWord) : 0; writeLen(fdSock, strlen(szWord)); send(fdSock, szWord, strlen(szWord), 0); } /******************************************************************** * Write a sentence (multiple words) to the socket ********************************************************************/ void writeSentence(int fdSock, struct Sentence *stWriteSentence) { int iIndex; if (stWriteSentence->iLength == 0) { return; } DEBUG ? printf("Writing sentence\n"): 0; DEBUG ? printSentence(stWriteSentence) : 0; for (iIndex=0; iIndex<stWriteSentence->iLength; iIndex++) { writeWord(fdSock, stWriteSentence->szSentence[iIndex]); } writeWord(fdSock, ""); } /******************************************************************** * Read a message length from the socket * * 80 = 10000000 (2 character encoded length) * C0 = 11000000 (3 character encoded length) * E0 = 11100000 (4 character encoded length) * * Message length is returned ********************************************************************/ int readLen(int fdSock) { char cFirstChar; // first character read from socket char *cLength; // length of next message to read...will be cast to int at the end int *iLen; // calculated length of next message (Cast to int) cLength = calloc(sizeof(int), 1); DEBUG ? printf("start readLen()\n") : 0; recv(fdSock, &cFirstChar, 1, 0); DEBUG ? printf("byte1 = %#x\n", cFirstChar) : 0; // read 4 bytes // this code SHOULD work, but is untested... if ((cFirstChar & 0xE0) == 0xE0) { DEBUG ? printf("4-byte encoded length\n") : 0; if (iLittleEndian) { cLength[3] = cFirstChar; cLength[3] &= 0x1f; // mask out the 1st 3 bits recv(fdSock, &cLength[2], 1, 0); recv(fdSock, &cLength[1], 1, 0); recv(fdSock, &cLength[0], 1, 0); } else { cLength[0] = cFirstChar; cLength[0] &= 0x1f; // mask out the 1st 3 bits recv(fdSock, &cLength[1], 1, 0); recv(fdSock, &cLength[2], 1, 0); recv(fdSock, &cLength[3], 1, 0); } iLen = (int *)cLength; } // read 3 bytes else if ((cFirstChar & 0xC0) == 0xC0) { DEBUG ? printf("3-byte encoded length\n") : 0; if (iLittleEndian) { cLength[2] = cFirstChar; cLength[2] &= 0x3f; // mask out the 1st 2 bits recv(fdSock, &cLength[1], 1, 0); recv(fdSock, &cLength[0], 1, 0); } else { cLength[1] = cFirstChar; cLength[1] &= 0x3f; // mask out the 1st 2 bits recv(fdSock, &cLength[2], 1, 0); recv(fdSock, &cLength[3], 1, 0); } iLen = (int *)cLength; } // read 2 bytes else if ((cFirstChar & 0x80) == 0x80) { DEBUG ? printf("2-byte encoded length\n") : 0; if (iLittleEndian) { cLength[1] = cFirstChar; cLength[1] &= 0x7f; // mask out the 1st bit recv(fdSock, &cLength[0], 1, 0); } else { cLength[2] = cFirstChar; cLength[2] &= 0x7f; // mask out the 1st bit recv(fdSock, &cLength[3], 1, 0); } iLen = (int *)cLength; } // assume 1-byte encoded length...same on both LE and BE systems else { DEBUG ? printf("1-byte encoded length\n") : 0; iLen = malloc(sizeof(int)); *iLen = (int)cFirstChar; } return *iLen; } /******************************************************************** * Read a word from the socket * The word that was read is returned as a string ********************************************************************/ char *readWord(int fdSock) { int iLen = readLen(fdSock); int iBytesToRead = 0; int iBytesRead = 0; char *szWord; char *szRetWord; char *szTmpWord; DEBUG ? printf("readWord iLen=%x\n", iLen) : 0; if (iLen > 0) { // allocate memory for strings szRetWord = calloc(sizeof(char), iLen + 1); szTmpWord = calloc(sizeof(char), 1024 + 1); while (iLen != 0) { // determine number of bytes to read this time around // lesser of 1024 or the number of byes left to read // in this word iBytesToRead = iLen > 1024 ? 1024 : iLen; // read iBytesToRead from the socket iBytesRead = recv(fdSock, szTmpWord, iBytesToRead, 0); // terminate szTmpWord szTmpWord[iBytesRead] = 0; // concatenate szTmpWord to szRetWord strcat(szRetWord, szTmpWord); // subtract the number of bytes we just read from iLen iLen -= iBytesRead; } // deallocate szTmpWord free(szTmpWord); DEBUG ? printf("word = %s\n", szRetWord) : 0; return szRetWord; } else { return NULL; } } /******************************************************************** * Read a sentence from the socket * A Sentence struct is returned ********************************************************************/ struct Sentence readSentence(int fdSock) { struct Sentence stReturnSentence; char *szWord; int i=0; int iReturnLength=0; DEBUG ? printf("readSentence\n") : 0; initializeSentence(&stReturnSentence); while (szWord = readWord(fdSock)) { addWordToSentence(&stReturnSentence, szWord); // check to see if we can get a return value from the API if (strstr(szWord, "!done") != NULL) { DEBUG ? printf("return sentence contains !done\n") : 0; stReturnSentence.iReturnValue = DONE; } else if (strstr(szWord, "!trap") != NULL) { DEBUG ? printf("return sentence contains !trap\n") : 0; stReturnSentence.iReturnValue = TRAP; } else if (strstr(szWord, "!fatal") != NULL) { DEBUG ? printf("return sentence contains !fatal\n") : 0; stReturnSentence.iReturnValue = FATAL; } } // if any errors, get the next sentence if (stReturnSentence.iReturnValue == TRAP || stReturnSentence.iReturnValue == FATAL) { readSentence(fdSock); } if (DEBUG) { for (i=0; i<stReturnSentence.iLength; i++) { printf("stReturnSentence.szSentence[%d] = %s\n", i, stReturnSentence.szSentence[i]); } } return stReturnSentence; } /******************************************************************** * Read sentence block from the socket...keeps reading sentences * until it encounters !done, !trap or !fatal from the socket ********************************************************************/ struct Block readBlock(int fdSock) { struct Sentence stSentence; struct Block stBlock; initializeBlock(&stBlock); DEBUG ? printf("readBlock\n") : 0; do { stSentence = readSentence(fdSock); DEBUG ? printf("readSentence succeeded.\n") : 0; addSentenceToBlock(&stBlock, &stSentence); DEBUG ? printf("addSentenceToBlock succeeded\n") : 0; } while (stSentence.iReturnValue == 0); DEBUG ? printf("readBlock completed successfully\n") : 0; return stBlock; } /******************************************************************** * Initialize a new block * Set iLength to 0. ********************************************************************/ void initializeBlock(struct Block *stBlock) { DEBUG ? printf("initializeBlock\n") : 0; stBlock->iLength = 0; } /******************************************************************** * Clear an existing block * Free all sentences in the Block struct and set iLength to 0. ********************************************************************/ void clearBlock(struct Block *stBlock) { DEBUG ? printf("clearBlock\n") : 0; free(stBlock->stSentence); initializeBlock(&stBlock); } /******************************************************************** * Print a block. * Output a Block with printf. ********************************************************************/ void printBlock(struct Block *stBlock) { int i; DEBUG ? printf("printBlock\n") : 0; DEBUG ? printf("block iLength = %d\n", stBlock->iLength) : 0; for (i=0; i<stBlock->iLength; i++) { printSentence(stBlock->stSentence[i]); } } /******************************************************************** * Add a sentence to a block * Allocate memory and add a sentence to a Block. ********************************************************************/ void addSentenceToBlock(struct Block *stBlock, struct Sentence *stSentence) { int iNewLength; iNewLength = stBlock->iLength + 1; DEBUG ? printf("addSentenceToBlock iNewLength=%d\n", iNewLength) : 0; // allocate mem for the new Sentence position if (stBlock->iLength == 0) { stBlock->stSentence = malloc(1 * sizeof stBlock->stSentence); } else { stBlock->stSentence = realloc(stBlock->stSentence, iNewLength * sizeof stBlock->stSentence + 1); } // allocate mem for the full sentence struct stBlock->stSentence[stBlock->iLength] = malloc(sizeof *stSentence); // copy actual sentence struct to the block position memcpy(stBlock->stSentence[stBlock->iLength], stSentence, sizeof *stSentence); // update iLength stBlock->iLength = iNewLength; DEBUG ? printf("addSentenceToBlock stBlock->iLength=%d\n", stBlock->iLength) : 0; } /******************************************************************** * Initialize a new sentence ********************************************************************/ void initializeSentence(struct Sentence *stSentence) { DEBUG ? printf("initializeSentence\n") : 0; stSentence->iLength = 0; stSentence->iReturnValue = 0; } /******************************************************************** * Clear an existing sentence ********************************************************************/ void clearSentence(struct Sentence *stSentence) { DEBUG ? printf("initializeSentence\n") : 0; free(stSentence->szSentence); initializeSentence(stSentence); } /******************************************************************** * Add a word to a sentence struct ********************************************************************/ void addWordToSentence(struct Sentence *stSentence, char *szWordToAdd) { int iNewLength; iNewLength = stSentence->iLength + 1; // allocate mem for the new word position if (stSentence->iLength == 0) { stSentence->szSentence = malloc(1 * sizeof stSentence->szSentence); } else { stSentence->szSentence = realloc(stSentence->szSentence, iNewLength * sizeof stSentence->szSentence + 1); } // allocate mem for the full word string stSentence->szSentence[stSentence->iLength] = malloc(strlen(szWordToAdd) + 1); // copy word string to the sentence strcpy(stSentence->szSentence[stSentence->iLength], szWordToAdd); // update iLength stSentence->iLength = iNewLength; } /******************************************************************** * Add a partial word to a sentence struct...useful for concatenation ********************************************************************/ void addPartWordToSentence(struct Sentence *stSentence, char *szWordToAdd) { int iIndex; iIndex = stSentence->iLength - 1; // reallocate memory for the new partial word stSentence->szSentence[iIndex] = realloc(stSentence->szSentence[iIndex], strlen(stSentence->szSentence[iIndex]) + strlen(szWordToAdd) + 1); // concatenate the partial word to the existing sentence strcat (stSentence->szSentence[iIndex], szWordToAdd); } /******************************************************************** * Print a Sentence struct ********************************************************************/ void printSentence(struct Sentence *stSentence) { int i; DEBUG ? printf("Sentence iLength = %d\n", stSentence->iLength) : 0; DEBUG ? printf("Sentence iReturnValue = %d\n", stSentence->iReturnValue) : 0; printf("Sentence iLength = %d\n", stSentence->iLength); printf("Sentence iReturnValue = %d\n", stSentence->iReturnValue); for (i=0; i<stSentence->iLength; i++) { printf(">>> %s\n", stSentence->szSentence[i]); } printf("\n"); } /******************************************************************** * MD5 helper function to convert an md5 hex char representation to * binary representation. ********************************************************************/ char *md5ToBinary(char *szHex) { int di; char cBinWork[3]; char *szReturn; // allocate 16 + 1 bytes for our return string szReturn = malloc((16 + 1) * sizeof *szReturn); // 32 bytes in szHex? if (strlen(szHex) != 32) { return NULL; } for (di=0; di<32; di+=2) { cBinWork[0] = szHex[di]; cBinWork[1] = szHex[di + 1]; cBinWork[2] = 0; DEBUG ? printf("cBinWork = %s\n", cBinWork) : 0; szReturn[di/2] = hexStringToChar(cBinWork); } return szReturn; } /******************************************************************** * MD5 helper function to calculate and return hex representation * of an MD5 digest stored in binary. ********************************************************************/ char *md5DigestToHexString(md5_byte_t *binaryDigest) { int di; char *szReturn; // allocate 32 + 1 bytes for our return string szReturn = malloc((32 + 1) * sizeof *szReturn); for (di = 0; di < 16; ++di) { sprintf(szReturn + di * 2, "%02x", binaryDigest[di]); } return szReturn; } /******************************************************************** * Quick and dirty function to convert hex string to char... * the toConvert string MUST BE 2 characters + null terminated. ********************************************************************/ char hexStringToChar(char *cToConvert) { char cConverted; unsigned int iAccumulated=0; char cString0[2] = {cToConvert[0], 0}; char cString1[2] = {cToConvert[1], 0}; // look @ first char in the 16^1 place if (cToConvert[0] == 'f' || cToConvert[0] == 'F') { iAccumulated += 16*15; } else if (cToConvert[0] == 'e' || cToConvert[0] == 'E') { iAccumulated += 16*14; } else if (cToConvert[0] == 'd' || cToConvert[0] == 'D') { iAccumulated += 16*13; } else if (cToConvert[0] == 'c' || cToConvert[0] == 'C') { iAccumulated += 16*12; } else if (cToConvert[0] == 'b' || cToConvert[0] == 'B') { iAccumulated += 16*11; } else if (cToConvert[0] == 'a' || cToConvert[0] == 'A') { iAccumulated += 16*10; } else { iAccumulated += 16 * atoi(cString0); } // now look @ the second car in the 16^0 place if (cToConvert[1] == 'f' || cToConvert[1] == 'F') { iAccumulated += 15; } else if (cToConvert[1] == 'e' || cToConvert[1] == 'E') { iAccumulated += 14; } else if (cToConvert[1] == 'd' || cToConvert[1] == 'D') { iAccumulated += 13; } else if (cToConvert[1] == 'c' || cToConvert[1] == 'C') { iAccumulated += 12; } else if (cToConvert[1] == 'b' || cToConvert[1] == 'B') { iAccumulated += 11; } else if (cToConvert[1] == 'a' || cToConvert[1] == 'A') { iAccumulated += 10; } else { iAccumulated += atoi(cString1); } DEBUG ? printf("%d\n", iAccumulated) : 0; return (char)iAccumulated; } /******************************************************************** * Test whether or not this system is little endian at RUNTIME * Courtesy: http://download.osgeo.org/grass/grass6_progman/endian_8c_source.html ********************************************************************/ int isLittleEndian(void) { union { int testWord; char testByte[sizeof(int)]; } endianTest; endianTest.testWord = 1; if (endianTest.testByte[0] == 1) return 1; /* true: little endian */ return 0; /* false: big endian */ }