itevad – How to write your own protocol and its stack – part 9

Previous_Part_8

Now we are moving to the last post of ‘itevad’ – writing a stack for decoding text format of Itevad Protocol using Flex and Bison as well as C as a main function. Being different with previous Flex and Bison, our itevadTxtDecoderBin will scan the input from a file instead of stdout. Moreover, itevadTxtDecoderBin would parse the text msg into Itevad msg structure generated by ASN.1 compiler in binary encoding and then print it using XML. Another important thing we need to care about is the flex build issue when certain option is added. The finally solution for this build error is to upgrade your Flex into latest – 2.5.35, my here. Wish you like the series of Itevad’s post. Any question, please feel free to leave a msg or email. God bless us:)

/* Flex + Bison as stack of decoding Text Itevad Protocol */

/* Flex - as scanner */
[root@localhost itevad2]# cat itevadTxtFlex2.l
/*
Itevad Protocol Text Encoding Lexical Analyzer Example
April 18, 2012
http://daveti.blog.com
Reference: itevad.abnf
NOTE: This is a Flex file NOT Lex!
*/

%{
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "ItevadMessage.h"
#include "itevadTxtBison2.tab.h"
%}

/* Configure our lexer */
%option stack noyywrap case-insensitive
%option header-file="itevadTxtFlex2.h"

/*
NOTE: for option 'reentrant', all the generated internal calls of flex would
have a argument called 'yy_scan yy_scanner'! To avoid this, we will not
add option for 'reentrant'.
*/

%%

"." { return DOT; } /* Dot */
"[" { return LSB; } /* Left square bracket */
"]" { return RSB; } /* Right square bracket */
"=" { return EQUAL; } /* Equal */
":" { return COLON; } /* Colon */
"{" { return LBRKT; } /* Left brace */
"}" { return RBRKT; } /* Right brace */
"/" { return SLASH; } /* Slash */
"Itevad" { return ItevadToken; } /* Itevad Token */
"Transaction" { return TransToken; } /* Transaction Token */
"Reply" { return ReplyToken; } /* Reply Token */
"Ask" { return AskToken; } /* Ask Token */
"Answer" { return AnswerToken; } /* Answer Token */
"(.)*" { printf("Debug - quoted string: %sn", yytext);
 /* Delete the prefix and suffix double quotes */
 char *content = strndup( (yytext+1), (strlen(yytext)-2));
 yylval.stringVal = content;
 printf("flex-debug: quotedString: %sn", yylval.stringVal);
 return quotedString;
 } /* Quoted String */
[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3} { /* IPv4 address handling here */
 printf("Debug - IPv4 address: %sn", yytext);
 /* Try to convert the IPv4 format into octet string format
 * to help the parser for value assignment.
 * "49.49.49.49" --> "1111"
 * char '1' has ASCII code "49"
 * NOTE: we do have a bug here as NOT all the IPv4 address
 * could be translated into "xxxx" format string!!!!!!!!
 */
 char *ipv4Address = strdup( yytext);
 char *octecString = calloc( 1, 5);
 int i = 0;
 unsigned char value = '';
 char *headPtr = ipv4Address;
 char *ptr = ipv4Address;
 while ( *ptr != '')
 {
 if ( *ptr == '.')
 {
 *ptr = '';
 value = (unsigned char)strtoul( headPtr, NULL, 10);
 i += snprintf( octecString+i, 5-i, "%c", value);
 headPtr = ptr + 1;
 }
 ptr++;
 }
 /* Convert the last octec char */
 value = (unsigned char)strtoul( headPtr, NULL, 10);
 i += snprintf( octecString+i, 5-i, "%c", value);
 yylval.stringVal = octecString;
 printf("flex-debug: IPv4Address: %sn", yylval.stringVal);
 return IPv4Address;
 } /* IPv4 Address */
[0-9]+ { printf("Debug - number: %sn", yytext);
 yylval.ulongVal = strtoul(yytext, NULL, 10);
 printf("flex-debug: NUMBER: %lun", yylval.ulongVal);
 return NUMBER;
 } /* Number */
[ trnv] { /* Ignore */ } /* White spaces */
. { printf( "Decode failure at %cn", *yytext); }

%%
[root@localhost itevad2]#


/* Flex 2.5.4 bug */
[root@localhost itevad2]# flex itevadTxtFlex2.l
"itevadTxtFlex2.l", line 19: unrecognized %option: header-file
"itevadTxtFlex2.l", line 20: unrecognized %option: reentrant
[root@localhost itevad2]#

/* Upgrade to Flex 2.5.35 */
[root@localhost flex-2.5.35]# which flex
/usr/local/bin/flex
[root@localhost flex-2.5.35]# flex -V
flex 2.5.35
[root@localhost flex-2.5.35]#
/* Bison - as parser */
[root@localhost itevad2]# cat itevadTxtBison2.y
/*
Itevad Protocol Text Encoding Sematic Analyzer Example
April 18, 2012
http://daveti.blog.com
Reference: itevad.abnf
NOTE: This is a Bison file NOT Yacc!
*/

%{
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "ItevadMessage.h"

extern int yylex();
%}

/* Token list */
%token SLASH LBRKT RBRKT COLON EQUAL NUMBER LSB RSB quotedString
%token ItevadToken TransToken ReplyToken AskToken AnswerToken
%token DOT IPv4Address

/* Type list */
%type <ulongVal> version transactionId portNumber NUMBER
%type <stringVal> quotedString IPv4Address
%type <itevadMsgVal> itevadMessage
%type <ipv4AddressVal> ipv4Address
%type <messageBodyVal> messageBody
%type <messageReqVal> messageRequest
%type <messageRepVal> messageReply
%type <askContentVal> askContent
%type <answerContentVal> answerContent

%union
{
 unsigned long ulongVal;
 char * stringVal;
 ItevadMessage_t itevadMsgVal;
 IP4Address_t ipv4AddressVal;
 MessageBody_t messageBodyVal;
 MessageRequest_t messageReqVal;
 MessageReply_t messageRepVal;
 AskContent_t askContentVal;
 AnswerContent_t answerContentVal;
}

%parse-param { ItevadMessage_t *itevadMsg}
%start itevadMessage

%%

itevadMessage:
 ItevadToken SLASH version ipv4Address messageBody
 { $$.version = $3;
 $$.ip4Address = $4;
 $$.messageBody = $5;
 *itevadMsg = $$;
 }
 ;

version:
 NUMBER
 { printf("bison-debug: version: %lun", $1);
 $$ = $1;
 }
 ;

ipv4Address:
 LSB IPv4Address RSB
 { printf("bison-debug: IPv4Address-$2: %sn", $2);
 OCTET_STRING_t myAddress;
 memset( &myAddress, 0, sizeof(OCTET_STRING_t));
 if ( OCTET_STRING_fromString( &myAddress, $2) == -1)
 {
 yyerror( "OCTET_STRING_fromString failure for ip4Address.addressn");
 }
 $$.address = myAddress;
 }
 | LSB IPv4Address RSB COLON portNumber
 { printf("bison-debug: IPv4Address-$2: %sn", $2);
 OCTET_STRING_t myAddress;
 memset( &myAddress, 0, sizeof(OCTET_STRING_t));
 if ( OCTET_STRING_fromString( &myAddress, $2) == -1)
 {
 yyerror( "OCTET_STRING_fromString failure for ip4Address.addressn");
 }
 $$.address = myAddress;
 $$.portNumber = &($5);
 }
 ;

portNumber:
 NUMBER
 { printf("bison-debug: portNumber: %lun", $1);
 $$ = $1;
 }
 ;

messageBody:
 messageRequest
 { $$.present = MessageBody_PR_messageRequest;
 $$.choice.messageRequest = $1;
 }
 | messageReply
 { $$.present = MessageBody_PR_messageReply;
 $$.choice.messageReply = $1;
 }
 ;

messageRequest:
 TransToken EQUAL transactionId LBRKT askContent RBRKT
 { INTEGER_t myTransId;
 memset( &myTransId, 0, sizeof(INTEGER_t));
 if ( asn_long2INTEGER( &myTransId, $3) == -1)
 {
 yyerror( "asn_long2INTEGER failure for transId in messageRequestn");
 }
 $$.transactionId = myTransId;
 $$.askContent = $5;
 }
 ;

messageReply:
 ReplyToken EQUAL transactionId LBRKT answerContent RBRKT
 { INTEGER_t myTransId;
 memset( &myTransId, 0, sizeof(INTEGER_t));
 if ( asn_long2INTEGER( &myTransId, $3) == -1)
 {
 yyerror( "asn_long2INTEGER failure for transId in messageReplyn");
 }
 $$.transactionId = myTransId;
 $$.answerContent = $5;
 }
 ;

transactionId:
 NUMBER
 { printf("bison-debug: transactionId: %lun", $1);
 $$ = $1;
 }
 ;

askContent:
 AskToken EQUAL quotedString
 { printf("bison-debug: askContent-quotedString-$3: %sn", $3);
 AskContent_t myAskContent;
 memset( &myAskContent, 0, sizeof(OCTET_STRING_t));
 if ( OCTET_STRING_fromString( &myAskContent, $3) == -1)
 {
 yyerror( "OCTET_STRING_fromString failure for askContentn");
 }
 $$ = myAskContent;
 }
 ;

answerContent:
 AnswerToken EQUAL quotedString
 { printf("bison-debug: answerContent-quotedString-$3: %sn", $3);
 AnswerContent_t myAnswerContent;
 memset( &myAnswerContent, 0, sizeof(OCTET_STRING_t));
 if ( OCTET_STRING_fromString( &myAnswerContent, $3) == -1)
 {
 yyerror( "OCTET_STRING_fromString failure for answerContentn");
 }
 $$ = myAnswerContent;
 }
 ;

%%

int yyerror( const char *msg)
{
 fprintf( stderr, "yyerror: %sn", msg);
}

[root@localhost itevad2]#
/* main - stack of Itevad decoder */
[root@localhost itevad2]# cat itevadTxtDecoder.c
/*
Itevad Protocol Text Decoder Example
April 18, 2012
http://daveti.blog.com
*/

#include <stdio.h>
#include <sys/types.h>
#include "itevadTxtFlex2.h"
#include "ItevadMessage.h"

extern int yyparse( ItevadMessage_t *itevadMsg);

int main( int ac, char **av)
{
 char buf[ 1024] = {0}; /* Temp buffer - NOTE: may overflow...*/
 ItevadMessage_t *itevadMsg = NULL; /* Type to decode */
 int rval; /* Decoder return value */
 FILE *fp; /* Input file handler */
 size_t size; /* Number of bytes read */
 char *filename; /* Input file name */

 /* Require a single filename argument */
 if ( ac != 2)
 {
 fprintf( stderr, "Usage: %s <file.msg>n", av[ 0]);
 exit(64); /* better, EX_USAGE */
 }
 else
 {
 filename = av[ 1];
 }

 /* Open input file as read-only binary */
 fp = fopen( filename, "rb");
 if ( !fp)
 {
 perror( filename);
 exit(66); /* better, EX_NOINPUT */
 }

 /* Read up to the buffer size */
 size = fread( buf, 1, sizeof( buf), fp);
 fclose( fp);
 if ( !size)
 {
 fprintf( stderr, "%s: Empty or brokenn", filename);
 exit(65); /* better, EX_DATAERR */
 }

 /* Decode the input buffer as Itevad type */
 itevadMsg = calloc( 1, sizeof(ItevadMessage_t));
 if ( itevadMsg == NULL)
 {
 fprintf( stderr, "Memory allocation failure for itevadMsgn");
 exit(65);
 }

 printf("itevadTxtDecoderBin-debug: calling yy_scan_string() - buf:n"
 "%sn", buf);
 yy_scan_string( buf);

 printf("itevadTxtDecoderBin-debug: calling yyparse()n");
 rval = yyparse( itevadMsg);
 if ( rval != 0)
 {
 fprintf( stderr, "%s: Broken ItevadMessage Bison decodingn",
 filename);
 exit(65); /* better, EX_DATAERR */
 }

 /* Print the decoded Itevad type as XML */
 printf("itevadTxtDecoderBin-debug: calling xer_fprint()n");
 xer_fprint( stdout, &asn_DEF_ItevadMessage, itevadMsg);

 /* Decoding finished successfully */
 return 0;
}
[root@localhost itevad2]#
/* Generate and run itevadTxtDecoderBin */
[root@localhost itevad2]# bison -d itevadTxtBison2.y
[root@localhost itevad2]# flex itevadTxtFlex2.l
[root@localhost itevad2]# gcc -o flexBin lex.yy.c -lfl /* delete */
[root@localhost itevad2]# gcc -I. -o itevadTxtDecoderBin *.c -lfl
itevadTxtFlex2.l: In function ‘yylex’:
itevadTxtFlex2.l:38: warning: incompatible implicit declaration of built-in function ‘strndup’
TransactionId.c: In function ‘TransactionId_constraint’:
TransactionId.c:31: warning: this decimal constant is unsigned only in ISO C90
[root@localhost itevad2]# mv itevadTxtDecoderBin ItevadBin
mv: overwrite `ItevadBin/itevadTxtDecoderBin'? yes
[root@localhost itevad2]#

[root@localhost ItevadBin]# ./itevadTxtDecoderBin itevad_text.msg
itevadTxtDecoderBin-debug: calling yy_scan_string() - buf:
Itevad/1 [49.49.49.49]:7777
Transaction = 65535 {
        Ask = "who r you?"
}

itevadTxtDecoderBin-debug: calling yyparse()
Debug - number: 1
flex-debug: NUMBER: 1
bison-debug: version: 1
Debug - IPv4 address: 49.49.49.49
flex-debug: IPv4Address: 1111
Debug - number: 7777
flex-debug: NUMBER: 7777
bison-debug: portNumber: 7777
bison-debug: IPv4Address-$2: 1111
Debug - number: 65535
flex-debug: NUMBER: 65535
bison-debug: transactionId: 65535
Debug - quoted string: "who r you?"
flex-debug: quotedString: who r you?
bison-debug: askContent-quotedString-$3: who r you?
itevadTxtDecoderBin-debug: calling xer_fprint()
<ItevadMessage>
    <version>1</version>
    <ip4Address>
        <address>31 31 31 31</address>
        <portNumber>65535</portNumber>
    </ip4Address>
    <messageBody>
        <messageRequest>
            <transactionId>65535</transactionId>
            <askContent>who r you?</askContent>
        </messageRequest>
    </messageBody>
</ItevadMessage>
[root@localhost ItevadBin]#

About daveti

Interested in kernel hacking, compilers, machine learning and guitars.
This entry was posted in Dave's Tools, H.248/MEGACO/EGCP, Programming, Stuff about Compiler and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.