diff --git a/tools/bifcl/CMakeLists.txt b/tools/bifcl/CMakeLists.txt new file mode 100644 index 0000000000..f13498b554 --- /dev/null +++ b/tools/bifcl/CMakeLists.txt @@ -0,0 +1,71 @@ +project(BifCl C CXX) + +cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) + +include(cmake/CommonCMakeConfig.cmake) + +include(FindRequiredPackage) + +FindRequiredPackage(BISON) +FindRequiredPackage(FLEX) + +if ( MISSING_PREREQS ) + foreach (prereq ${MISSING_PREREQ_DESCS}) + message(SEND_ERROR ${prereq}) + endforeach () + message(FATAL_ERROR "Configuration aborted due to missing prerequisites") +endif () + +include_directories(BEFORE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +set(BISON_FLAGS "--debug") + +# BIF parser/scanner +bison_target(BIFParser builtin-func.y + ${CMAKE_CURRENT_BINARY_DIR}/bif_parse.cc + HEADER ${CMAKE_CURRENT_BINARY_DIR}/bif_parse.h + #VERBOSE ${CMAKE_CURRENT_BINARY_DIR}/bif_parse.output + COMPILE_FLAGS "${BISON_FLAGS}") +flex_target(BIFScanner builtin-func.l ${CMAKE_CURRENT_BINARY_DIR}/bif_lex.cc) +add_flex_bison_dependency(BIFScanner BIFParser) + +set(bifcl_SRCS + ${BISON_BIFParser_INPUT} + ${FLEX_BIFScanner_INPUT} + ${BISON_BIFParser_OUTPUTS} + ${FLEX_BIFScanner_OUTPUTS} + bif_arg.cc + bif_arg.h + module_util.cc + module_util.h +) + +add_executable(bifcl ${bifcl_SRCS}) + +install(TARGETS bifcl DESTINATION bin) + +if (CMAKE_BUILD_TYPE) + string(TOUPPER ${CMAKE_BUILD_TYPE} BuildType) +endif () + +message( + "\n====================| Bifcl Build Summary |=====================" + "\n" + "\nBuild type: ${CMAKE_BUILD_TYPE}" + "\nBuild dir: ${CMAKE_BINARY_DIR}" + "\nInstall prefix: ${CMAKE_INSTALL_PREFIX}" + "\nDebug mode: ${ENABLE_DEBUG}" + "\n" + "\nCC: ${CMAKE_C_COMPILER}" + "\nCFLAGS: ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BuildType}}" + "\nCXX: ${CMAKE_CXX_COMPILER}" + "\nCXXFLAGS: ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BuildType}}" + "\nCPP: ${CMAKE_CXX_COMPILER}" + "\n" + "\n================================================================\n" +) + +include(UserChangedWarning) diff --git a/tools/bifcl/README b/tools/bifcl/README index e69de29bb2..0ec59344ec 100644 --- a/tools/bifcl/README +++ b/tools/bifcl/README @@ -0,0 +1,19 @@ +.. _Bro: https://bro.org + +================ +Bro BIF Compiler +================ + +The ``bifcl`` program simply takes a ``.bif`` file as input and +generates C++ header/source files along with a ``.bro`` script +that all-together provide the declaration and implementation of Bro_ +Built-In-Functions (BIFs), which can then be compiled and shipped +as part of a Bro plugin. + +A BIF allows one to write arbitrary C++ code and access it via a +function call inside a Bro script. In this way, they can also be +used to access parts of Bro's internal C++ API that aren't already +exposed via their own BIFs. + +At the moment, learning the format of a ``.bif`` file is likely easiest +by just taking a look at the ``.bif`` files inside the Bro source-tree. diff --git a/tools/bifcl/bif_arg.cc b/tools/bifcl/bif_arg.cc new file mode 100644 index 0000000000..f83333220d --- /dev/null +++ b/tools/bifcl/bif_arg.cc @@ -0,0 +1,82 @@ + +#include +#include +using namespace std; + +#include + +#include "bif_arg.h" + +static struct { + const char* bif_type; + const char* bro_type; + const char* c_type; + const char* accessor; + const char* constructor; +} builtin_func_arg_type[] = { +#define DEFINE_BIF_TYPE(id, bif_type, bro_type, c_type, accessor, constructor) \ + {bif_type, bro_type, c_type, accessor, constructor}, +#include "bif_type.def" +#undef DEFINE_BIF_TYPE +}; + +extern const char* arg_list_name; + +BuiltinFuncArg::BuiltinFuncArg(const char* arg_name, int arg_type) + { + name = arg_name; + type = arg_type; + type_str = ""; + attr_str = ""; + } + +BuiltinFuncArg::BuiltinFuncArg(const char* arg_name, const char* arg_type_str, + const char* arg_attr_str) + { + name = arg_name; + type = TYPE_OTHER; + type_str = arg_type_str; + attr_str = arg_attr_str; + + for ( int i = 0; builtin_func_arg_type[i].bif_type[0] != '\0'; ++i ) + if ( ! strcmp(builtin_func_arg_type[i].bif_type, arg_type_str) ) + { + type = i; + type_str = ""; + } + } + +void BuiltinFuncArg::PrintBro(FILE* fp) + { + fprintf(fp, "%s: %s%s %s", name, builtin_func_arg_type[type].bro_type, + type_str, attr_str); + } + +void BuiltinFuncArg::PrintCDef(FILE* fp, int n) + { + fprintf(fp, + "\t%s %s = (%s) (", + builtin_func_arg_type[type].c_type, + name, + builtin_func_arg_type[type].c_type); + + char buf[1024]; + snprintf(buf, sizeof(buf), "(*%s)[%d]", arg_list_name, n); + // Print the accessor expression. + fprintf(fp, builtin_func_arg_type[type].accessor, buf); + + fprintf(fp, ");\n"); + } + +void BuiltinFuncArg::PrintCArg(FILE* fp, int n) + { + const char* ctype = builtin_func_arg_type[type].c_type; + char buf[1024]; + + fprintf(fp, "%s %s", ctype, name); + } + +void BuiltinFuncArg::PrintBroValConstructor(FILE* fp) + { + fprintf(fp, builtin_func_arg_type[type].constructor, name); + } diff --git a/tools/bifcl/builtin-func.l b/tools/bifcl/builtin-func.l new file mode 100644 index 0000000000..2d59408f83 --- /dev/null +++ b/tools/bifcl/builtin-func.l @@ -0,0 +1,422 @@ +%{ +#include +#include +#include +#include "bif_arg.h" +#include "bif_parse.h" + +char* copy_string(const char* s) + { + char* c = new char[strlen(s)+1]; + strcpy(c, s); + return c; + } + +int line_number = 1; + +extern int in_c_code; + +int check_c_mode(int t) + { + if ( ! in_c_code ) + return t; + + yylval.str = copy_string(yytext); + return TOK_C_TOKEN; + } +%} + +WS [ \t]+ +OWS [ \t]* + /* Note, bifcl only accepts a single "::" in IDs while the policy + layer acceptes multiple. (But the policy layer doesn't have + a hierachy. */ +IDCOMPONENT [A-Za-z_][A-Za-z_0-9]* +ID {IDCOMPONENT}(::{IDCOMPONENT})? +ESCSEQ (\\([^\n]|[0-7]+|x[[:xdigit:]]+)) +DEC [[:digit:]]+ +HEX [0-9a-fA-F]+ + + +%option nodefault + +%% + +#.* { + yylval.str = copy_string(yytext); + return TOK_COMMENT; + } + +\n { + ++line_number; + return TOK_LF; + } + +{WS} { + yylval.str = copy_string(yytext); + return TOK_WS; + } + +[=,:;] return check_c_mode(yytext[0]); + +"%{" return TOK_LPB; +"%}" return TOK_RPB; +"%%{" return TOK_LPPB; +"%%}" return TOK_RPPB; + +"%(" return check_c_mode(TOK_LPP); +"%)" return check_c_mode(TOK_RPP); +"..." return check_c_mode(TOK_VAR_ARG); +"function" return check_c_mode(TOK_FUNCTION); +"event" return check_c_mode(TOK_EVENT); +"const" return check_c_mode(TOK_CONST); +"enum" return check_c_mode(TOK_ENUM); +"type" return check_c_mode(TOK_TYPE); +"record" return check_c_mode(TOK_RECORD); +"set" return check_c_mode(TOK_SET); +"table" return check_c_mode(TOK_TABLE); +"vector" return check_c_mode(TOK_VECTOR); +"of" return check_c_mode(TOK_OF); +"opaque" return check_c_mode(TOK_OPAQUE); +"module" return check_c_mode(TOK_MODULE); + +"@ARG@" return TOK_ARG; +"@ARGS@" return TOK_ARGS; +"@ARGC@" return TOK_ARGC; + +"T" yylval.val = 1; return TOK_BOOL; +"F" yylval.val = 0; return TOK_BOOL; + +{DEC} { + yylval.str = copy_string(yytext); + return TOK_INT; + } + +"0x"{HEX} { + yylval.str = copy_string(yytext); + return TOK_INT; + } + + +{ID} { + yylval.str = copy_string(yytext); + return TOK_ID; + } + + /* + Hacky way to pass along arbitrary attribute expressions since the BIF parser + has little understanding of valid Bro expressions. With this pattern, the + attribute expression should stop when it reaches another attribute, another + function argument, or the end of the function declaration. + */ +&{ID}({OWS}={OWS}[^&%;,]+)? { + int t = check_c_mode(TOK_ATTR); + + if ( t == TOK_ATTR ) + { + yylval.str = copy_string(yytext); + return TOK_ATTR; + } + else + return t; + } + +\"([^\\\n\"]|{ESCSEQ})*\" { + yylval.str = copy_string(yytext); + return TOK_CSTR; + } + +\'([^\\\n\']|{ESCSEQ})*\' { + yylval.str = copy_string(yytext); + return TOK_CSTR; + } + +. { + yylval.val = yytext[0]; + return TOK_ATOM; + } +%% + +int yywrap() + { + yy_delete_buffer(YY_CURRENT_BUFFER); + return 1; + } + +extern int yyparse(); +char* input_filename = 0; +char* input_filename_with_path = 0; +char* plugin = 0; +int alternative_mode = 0; + +FILE* fp_bro_init = 0; +FILE* fp_func_def = 0; +FILE* fp_func_h = 0; +FILE* fp_func_init = 0; +FILE* fp_func_register = 0; +FILE* fp_netvar_h = 0; +FILE* fp_netvar_def = 0; +FILE* fp_netvar_init = 0; + +void remove_file(const char *surfix); +void err_exit(void); +FILE* open_output_file(const char* surfix); +void close_if_open(FILE **fpp); +void close_all_output_files(void); + + +FILE* open_output_file(const char* surfix) + { + char fn[1024]; + FILE* fp; + + snprintf(fn, sizeof(fn), "%s.%s", input_filename, surfix); + if ( (fp = fopen(fn, "w")) == NULL ) + { + fprintf(stderr, "Error: cannot open file: %s\n", fn); + err_exit(); + } + + return fp; + } + +void usage() + { + fprintf(stderr, "usage: bifcl [-p | -s] *.bif\n"); + exit(1); + } + +void init_alternative_mode() + { + fp_bro_init = open_output_file("bro"); + fp_func_h = open_output_file("h"); + fp_func_def = open_output_file("cc"); + fp_func_init = open_output_file("init.cc"); + fp_func_register = plugin ? open_output_file("register.cc") : NULL; + + fp_netvar_h = fp_func_h; + fp_netvar_def = fp_func_def; + fp_netvar_init = fp_func_init; + + int n = 1024 + strlen(input_filename); + char auto_gen_comment[n]; + + snprintf(auto_gen_comment, n, + "This file was automatically generated by bifcl from %s (%s mode).", + input_filename_with_path, plugin ? "plugin" : "alternative"); + + fprintf(fp_bro_init, "# %s\n\n", auto_gen_comment); + fprintf(fp_func_def, "// %s\n\n", auto_gen_comment); + fprintf(fp_func_h, "// %s\n\n", auto_gen_comment); + fprintf(fp_func_init, "// %s\n\n", auto_gen_comment); + + if ( fp_func_register ) + fprintf(fp_func_register, "// %s\n\n", auto_gen_comment); + + static char guard[1024]; + if ( getcwd(guard, sizeof(guard)) == NULL ) + { + fprintf(stderr, "Error: cannot get current working directory\n"); + err_exit(); + } + strncat(guard, "/", sizeof(guard) - strlen(guard) - 1); + strncat(guard, input_filename, sizeof(guard) - strlen(guard) - 1); + + for ( char* p = guard; *p; p++ ) + { + if ( ! isalnum(*p) ) + *p = '_'; + } + + fprintf(fp_func_h, "#if defined(BRO_IN_NETVAR) || ! defined(%s)\n", guard); + + fprintf(fp_func_h, "#ifndef BRO_IN_NETVAR\n"); + fprintf(fp_func_h, "#ifndef %s\n", guard); + fprintf(fp_func_h, "#define %s\n", guard); + fprintf(fp_func_h, "#include \"bro-bif.h\"\n"); + fprintf(fp_func_h, "#endif\n"); + fprintf(fp_func_h, "#endif\n"); + fprintf(fp_func_h, "\n"); + + fprintf(fp_func_def, "\n"); + fprintf(fp_func_def, "#include \"%s.h\"\n", input_filename); + fprintf(fp_func_def, "\n"); + + static char name[1024]; + strncpy(name, input_filename, sizeof(name)); + char* dot = strchr(name, '.'); + if ( dot ) + *dot = '\0'; + + if ( plugin ) + { + static char plugin_canon[1024]; + strncpy(plugin_canon, plugin, sizeof(plugin_canon)); + char* colon = strstr(plugin_canon, "::"); + + if ( colon ) { + *colon = '_'; + memmove(colon + 1, colon + 2, plugin_canon + strlen(plugin_canon) - colon); + } + + fprintf(fp_func_init, "\n"); + fprintf(fp_func_init, "#include \n"); + fprintf(fp_func_init, "#include \n"); + fprintf(fp_func_init, "#include \"plugin/Plugin.h\"\n"); + fprintf(fp_func_init, "#include \"%s.h\"\n", input_filename); + fprintf(fp_func_init, "\n"); + fprintf(fp_func_init, "namespace plugin { namespace %s {\n", plugin_canon); + fprintf(fp_func_init, "\n"); + fprintf(fp_func_init, "void __bif_%s_init(plugin::Plugin* plugin)\n", name); + fprintf(fp_func_init, "\t{\n"); + + fprintf(fp_func_register, "#include \"plugin/Manager.h\"\n"); + fprintf(fp_func_register, "\n"); + fprintf(fp_func_register, "namespace plugin { namespace %s {\n", plugin_canon); + fprintf(fp_func_register, "void __bif_%s_init(plugin::Plugin* plugin);\n", name); + fprintf(fp_func_register, "::plugin::__RegisterBif __register_bifs_%s_%s(\"%s\", __bif_%s_init);\n", plugin_canon, name, plugin, name); + fprintf(fp_func_register, "} }\n"); + } + } + +void finish_alternative_mode() + { + fprintf(fp_func_h, "\n"); + fprintf(fp_func_h, "#endif\n"); + + if ( plugin ) + { + fprintf(fp_func_init, "\n"); + fprintf(fp_func_init, "\t}\n"); + fprintf(fp_func_init, "} }\n"); + fprintf(fp_func_init, "\n"); + fprintf(fp_func_init, "\n"); + } + } + +int main(int argc, char* argv[]) + { + int opt; + + while ( (opt = getopt(argc, argv, "p:s")) != -1 ) + { + switch ( opt ) { + case 'p': + alternative_mode = 1; + plugin = optarg; + break; + + case 's': + alternative_mode = 1; + break; + + default: + usage(); + } + } + + for ( int i = optind; i < argc; i++ ) + { + FILE* fp_input; + char* slash; + + input_filename = input_filename_with_path = argv[i]; + slash = strrchr(input_filename, '/'); + + if ( (fp_input = fopen(input_filename, "r")) == NULL ) + { + fprintf(stderr, "Error: cannot open file: %s\n", input_filename); + /* no output files open. can simply exit */ + exit(1); + } + + if ( slash ) + input_filename = slash + 1; + + if ( ! alternative_mode ) + { + fp_bro_init = open_output_file("bro"); + fp_func_h = open_output_file("func_h"); + fp_func_def = open_output_file("func_def"); + fp_func_init = open_output_file("func_init"); + fp_netvar_h = open_output_file("netvar_h"); + fp_netvar_def = open_output_file("netvar_def"); + fp_netvar_init = open_output_file("netvar_init"); + + int n = 1024 + strlen(input_filename); + char auto_gen_comment[n]; + + snprintf(auto_gen_comment, n, + "This file was automatically generated by bifcl from %s.", + input_filename); + + fprintf(fp_bro_init, "# %s\n\n", auto_gen_comment); + fprintf(fp_func_def, "// %s\n\n", auto_gen_comment); + fprintf(fp_func_h, "// %s\n\n", auto_gen_comment); + fprintf(fp_func_init, "// %s\n\n", auto_gen_comment); + fprintf(fp_netvar_def, "// %s\n\n", auto_gen_comment); + fprintf(fp_netvar_h, "// %s\n\n", auto_gen_comment); + fprintf(fp_netvar_init, "// %s\n\n", auto_gen_comment); + } + + else + init_alternative_mode(); + + yy_switch_to_buffer(yy_create_buffer(fp_input, YY_BUF_SIZE)); + yyparse(); + + if ( alternative_mode ) + finish_alternative_mode(); + + fclose(fp_input); + close_all_output_files(); + + } + } + +void close_if_open(FILE **fpp) + { + if (*fpp) + fclose(*fpp); + *fpp = NULL; + } + +void close_all_output_files(void) + { + close_if_open(&fp_bro_init); + close_if_open(&fp_func_h); + close_if_open(&fp_func_def); + close_if_open(&fp_func_init); + close_if_open(&fp_func_register); + + if ( ! alternative_mode ) + { + close_if_open(&fp_netvar_h); + close_if_open(&fp_netvar_def); + close_if_open(&fp_netvar_init); + } + } + +void remove_file(const char *surfix) + { + char fn[1024]; + + snprintf(fn, sizeof(fn), "%s.%s", input_filename, surfix); + unlink(fn); + } + +void err_exit(void) + { + close_all_output_files(); + /* clean up. remove all output files we've generated so far */ + remove_file("bro"); + remove_file("func_h"); + remove_file("func_def"); + remove_file("func_init"); + remove_file("func_register"); + remove_file("netvar_h"); + remove_file("netvar_def"); + remove_file("netvar_init"); + exit(1); + } + diff --git a/tools/bifcl/builtin-func.y b/tools/bifcl/builtin-func.y new file mode 100644 index 0000000000..0f895ced52 --- /dev/null +++ b/tools/bifcl/builtin-func.y @@ -0,0 +1,785 @@ +%{ +#include +#include +#include +#include + +using namespace std; + +#include +#include + +#include "module_util.h" + +using namespace std; + +extern int line_number; +extern char* input_filename; +extern char* plugin; + +#define print_line_directive(fp) fprintf(fp, "\n#line %d \"%s\"\n", line_number, input_filename) + +extern FILE* fp_bro_init; +extern FILE* fp_func_def; +extern FILE* fp_func_h; +extern FILE* fp_func_init; +extern FILE* fp_netvar_h; +extern FILE* fp_netvar_def; +extern FILE* fp_netvar_init; + +int in_c_code = 0; +string current_module = GLOBAL_MODULE_NAME; +int definition_type; +string type_name; + + +enum { + C_SEGMENT_DEF, + FUNC_DEF, + EVENT_DEF, + TYPE_DEF, + CONST_DEF, +}; + +// Holds the name of a declared object (function, enum, record type, event, +// etc. and information about namespaces, etc. +struct decl_struct { + string module_name; + string bare_name; // name without module or namespace + string c_namespace_start; // "opening" namespace for use in netvar_* + string c_namespace_end; // closing "}" for all the above namespaces + string c_fullname; // fully qualified name (namespace::....) for use in netvar_init + string bro_fullname; // fully qualified bro name, for netvar (and lookup_ID()) + string bro_name; // the name as we read it from input. What we write into the .bro file + + // special cases for events. Events have an EventHandlerPtr + // and a generate_* function. This name is for the generate_* function + string generate_bare_name; + string generate_c_fullname; + string generate_c_namespace_start; + string generate_c_namespace_end; +} decl; + +void set_definition_type(int type, const char *arg_type_name) + { + definition_type = type; + if ( type == TYPE_DEF && arg_type_name ) + type_name = string(arg_type_name); + else + type_name = ""; + } + +void set_decl_name(const char *name) + { + decl.bare_name = extract_var_name(name); + + // make_full_var_name prepends the correct module, if any + // then we can extract the module name again. + string varname = make_full_var_name(current_module.c_str(), name); + decl.module_name = extract_module_name(varname.c_str()); + + decl.c_namespace_start = ""; + decl.c_namespace_end = ""; + decl.c_fullname = ""; + decl.bro_fullname = ""; + decl.bro_name = ""; + + decl.generate_c_fullname = ""; + decl.generate_bare_name = string("generate_") + decl.bare_name; + decl.generate_c_namespace_start = ""; + decl.generate_c_namespace_end = ""; + + switch ( definition_type ) { + case TYPE_DEF: + decl.c_namespace_start = "namespace BifType { namespace " + type_name + "{ "; + decl.c_namespace_end = " } }"; + decl.c_fullname = "BifType::" + type_name + "::"; + break; + + case CONST_DEF: + decl.c_namespace_start = "namespace BifConst { "; + decl.c_namespace_end = " } "; + decl.c_fullname = "BifConst::"; + break; + + case FUNC_DEF: + decl.c_namespace_start = "namespace BifFunc { "; + decl.c_namespace_end = " } "; + decl.c_fullname = "BifFunc::"; + break; + + case EVENT_DEF: + decl.c_namespace_start = ""; + decl.c_namespace_end = ""; + decl.c_fullname = "::"; // need this for namespace qualified events due do event_c_body + decl.generate_c_namespace_start = "namespace BifEvent { "; + decl.generate_c_namespace_end = " } "; + decl.generate_c_fullname = "BifEvent::"; + break; + + default: + break; + } + + if ( decl.module_name != GLOBAL_MODULE_NAME ) + { + decl.c_namespace_start += "namespace " + decl.module_name + " { "; + decl.c_namespace_end += string(" }"); + decl.c_fullname += decl.module_name + "::"; + decl.bro_fullname += decl.module_name + "::"; + + decl.generate_c_namespace_start += "namespace " + decl.module_name + " { "; + decl.generate_c_namespace_end += " } "; + decl.generate_c_fullname += decl.module_name + "::"; + } + + decl.bro_fullname += decl.bare_name; + if ( definition_type == FUNC_DEF ) + decl.bare_name = string("bro_") + decl.bare_name; + + decl.c_fullname += decl.bare_name; + decl.bro_name += name; + decl.generate_c_fullname += decl.generate_bare_name; + + } + +const char* arg_list_name = "BiF_ARGS"; + +#include "bif_arg.h" + +/* Map bif/bro type names to C types for use in const declaration */ +static struct { + const char* bif_type; + const char* bro_type; + const char* c_type; + const char* accessor; + const char* constructor; +} builtin_types[] = { +#define DEFINE_BIF_TYPE(id, bif_type, bro_type, c_type, accessor, constructor) \ + {bif_type, bro_type, c_type, accessor, constructor}, +#include "bif_type.def" +#undef DEFINE_BIF_TYPE +}; + +int get_type_index(const char *type_name) + { + for ( int i = 0; builtin_types[i].bif_type[0] != '\0'; ++i ) + { + if ( strcmp(builtin_types[i].bif_type, type_name) == 0 ) + return i; + } + return TYPE_OTHER; + } + + +int var_arg; // whether the number of arguments is variable +std::vector args; + +extern int yyerror(const char[]); +extern int yywarn(const char msg[]); +extern int yylex(); + +char* concat(const char* str1, const char* str2) + { + int len1 = strlen(str1); + int len2 = strlen(str2); + + char* s = new char[len1 + len2 +1]; + + memcpy(s, str1, len1); + memcpy(s + len1, str2, len2); + + s[len1+len2] = '\0'; + + return s; + } + +// Print the bro_event_* function prototype in C++, without the ending ';' +void print_event_c_prototype(FILE *fp, bool is_header) + { + if ( is_header ) + fprintf(fp, "%s void %s(analyzer::Analyzer* analyzer%s", + decl.generate_c_namespace_start.c_str(), decl.generate_bare_name.c_str(), + args.size() ? ", " : "" ); + else + fprintf(fp, "void %s(analyzer::Analyzer* analyzer%s", + decl.generate_c_fullname.c_str(), + args.size() ? ", " : "" ); + for ( int i = 0; i < (int) args.size(); ++i ) + { + if ( i > 0 ) + fprintf(fp, ", "); + args[i]->PrintCArg(fp, i); + } + fprintf(fp, ")"); + if ( is_header ) + fprintf(fp, "; %s\n", decl.generate_c_namespace_end.c_str()); + else + fprintf(fp, "\n"); + } + +// Print the bro_event_* function body in C++. +void print_event_c_body(FILE *fp) + { + fprintf(fp, "\t{\n"); + fprintf(fp, "\t// Note that it is intentional that here we do not\n"); + fprintf(fp, "\t// check if %s is NULL, which should happen *before*\n", + decl.c_fullname.c_str()); + fprintf(fp, "\t// %s is called to avoid unnecessary Val\n", + decl.generate_c_fullname.c_str()); + fprintf(fp, "\t// allocation.\n"); + fprintf(fp, "\n"); + + fprintf(fp, "\tval_list* vl = new val_list;\n\n"); + BuiltinFuncArg *connection_arg = 0; + + for ( int i = 0; i < (int) args.size(); ++i ) + { + fprintf(fp, "\t"); + fprintf(fp, "vl->append("); + args[i]->PrintBroValConstructor(fp); + fprintf(fp, ");\n"); + + if ( args[i]->Type() == TYPE_CONNECTION ) + { + if ( connection_arg == 0 ) + connection_arg = args[i]; + else + { + // We are seeing two connection type arguments. + yywarn("Warning: with more than connection-type " + "event arguments, bifcl only passes " + "the first one to EventMgr as cookie."); + } + } + } + + fprintf(fp, "\n"); + fprintf(fp, "\tmgr.QueueEvent(%s, vl, SOURCE_LOCAL, analyzer->GetID(), timer_mgr", + decl.c_fullname.c_str()); + + if ( connection_arg ) + // Pass the connection to the EventMgr as the "cookie" + fprintf(fp, ", %s", connection_arg->Name()); + + fprintf(fp, ");\n"); + fprintf(fp, "\t} // event generation\n"); + //fprintf(fp, "%s // end namespace\n", decl.generate_c_namespace_end.c_str()); + } + +void record_bif_item(const char* id, const char* type) + { + if ( ! plugin ) + return; + + fprintf(fp_func_init, "\tplugin->AddBifItem(\"%s\", plugin::BifItem::%s);\n", id, type); + } + +%} + +%token TOK_LPP TOK_RPP TOK_LPB TOK_RPB TOK_LPPB TOK_RPPB TOK_VAR_ARG +%token TOK_BOOL +%token TOK_FUNCTION TOK_EVENT TOK_CONST TOK_ENUM TOK_OF +%token TOK_TYPE TOK_RECORD TOK_SET TOK_VECTOR TOK_OPAQUE TOK_TABLE TOK_MODULE +%token TOK_ARGS TOK_ARG TOK_ARGC +%token TOK_ID TOK_ATTR TOK_CSTR TOK_LF TOK_WS TOK_COMMENT +%token TOK_ATOM TOK_INT TOK_C_TOKEN + +%left ',' ':' + +%type TOK_C_TOKEN TOK_ID TOK_CSTR TOK_WS TOK_COMMENT TOK_ATTR TOK_INT opt_ws type attr_list opt_attr_list opt_func_attrs +%type TOK_ATOM TOK_BOOL + +%union { + const char* str; + int val; +} + +%% + +builtin_lang: definitions + { + fprintf(fp_bro_init, "} # end of export section\n"); + fprintf(fp_bro_init, "module %s;\n", GLOBAL_MODULE_NAME); + } + + + +definitions: definitions definition opt_ws + { + if ( in_c_code ) + fprintf(fp_func_def, "%s", $3); + else + fprintf(fp_bro_init, "%s", $3); + } + | opt_ws + { + fprintf(fp_bro_init, "%s", $1); + fprintf(fp_bro_init, "export {\n"); + } + ; + +definition: event_def + | func_def + | c_code_segment + | enum_def + | const_def + | type_def + | module_def + ; + + +module_def: TOK_MODULE opt_ws TOK_ID opt_ws ';' + { + current_module = string($3); + fprintf(fp_bro_init, "module %s;\n", $3); + } + + // XXX: Add the netvar glue so that the event engine knows about + // the type. One still has to define the type in bro.init. + // Would be nice, if we could just define the record type here + // and then copy to the .bif.bro file, but type declarations in + // Bro can be quite powerful. Don't know whether it's worth it + // extend the bif-language to be able to handle that all.... + // Or we just support a simple form of record type definitions + // TODO: add other types (tables, sets) +type_def: TOK_TYPE opt_ws TOK_ID opt_ws ':' opt_ws type_def_types opt_ws ';' + { + set_decl_name($3); + + fprintf(fp_netvar_h, "%s extern %sType * %s; %s\n", + decl.c_namespace_start.c_str(), type_name.c_str(), + decl.bare_name.c_str(), decl.c_namespace_end.c_str()); + fprintf(fp_netvar_def, "%s %sType * %s; %s\n", + decl.c_namespace_start.c_str(), type_name.c_str(), + decl.bare_name.c_str(), decl.c_namespace_end.c_str()); + fprintf(fp_netvar_init, + "\t%s = internal_type(\"%s\")->As%sType();\n", + decl.c_fullname.c_str(), decl.bro_fullname.c_str(), + type_name.c_str()); + + record_bif_item(decl.bro_fullname.c_str(), "TYPE"); + } + ; + +type_def_types: TOK_RECORD + { set_definition_type(TYPE_DEF, "Record"); } + | TOK_SET + { set_definition_type(TYPE_DEF, "Set"); } + | TOK_VECTOR + { set_definition_type(TYPE_DEF, "Vector"); } + | TOK_TABLE + { set_definition_type(TYPE_DEF, "Table"); } + ; + +opt_func_attrs: attr_list opt_ws + { $$ = $1; } + | /* nothing */ + { $$ = ""; } + ; + +event_def: event_prefix opt_ws plain_head opt_func_attrs + { fprintf(fp_bro_init, "%s", $4); } end_of_head ';' + { + print_event_c_prototype(fp_func_h, true); + print_event_c_prototype(fp_func_def, false); + print_event_c_body(fp_func_def); + } + +func_def: func_prefix opt_ws typed_head opt_func_attrs + { fprintf(fp_bro_init, "%s", $4); } end_of_head body + ; + +enum_def: enum_def_1 enum_list TOK_RPB opt_attr_list + { + // First, put an end to the enum type decl. + fprintf(fp_bro_init, "} "); + fprintf(fp_bro_init, "%s", $4); + fprintf(fp_bro_init, ";\n"); + if ( decl.module_name != GLOBAL_MODULE_NAME ) + fprintf(fp_netvar_h, "}; } }\n"); + else + fprintf(fp_netvar_h, "}; }\n"); + + // Now generate the netvar's. + fprintf(fp_netvar_h, "%s extern EnumType * %s; %s\n", + decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str()); + fprintf(fp_netvar_def, "%s EnumType * %s; %s\n", + decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str()); + fprintf(fp_netvar_init, + "\t%s = internal_type(\"%s\")->AsEnumType();\n", + decl.c_fullname.c_str(), decl.bro_fullname.c_str()); + + record_bif_item(decl.bro_fullname.c_str(), "TYPE"); + } + ; + +enum_def_1: TOK_ENUM opt_ws TOK_ID opt_ws TOK_LPB opt_ws + { + set_definition_type(TYPE_DEF, "Enum"); + set_decl_name($3); + fprintf(fp_bro_init, "type %s: enum %s{%s", decl.bro_name.c_str(), $4, $6); + + // this is the namespace were the enumerators are defined, not where + // the type is defined. + // We don't support fully qualified names as enumerators. Use a module name + fprintf(fp_netvar_h, "namespace BifEnum { "); + if ( decl.module_name != GLOBAL_MODULE_NAME ) + fprintf(fp_netvar_h, "namespace %s { ", decl.module_name.c_str()); + fprintf(fp_netvar_h, "enum %s {\n", $3); + } + ; + +enum_list: enum_list TOK_ID opt_ws ',' opt_ws + { + fprintf(fp_bro_init, "%s%s,%s", $2, $3, $5); + fprintf(fp_netvar_h, "\t%s,\n", $2); + } + | enum_list TOK_ID opt_ws '=' opt_ws TOK_INT opt_ws ',' opt_ws + { + fprintf(fp_bro_init, "%s = %s%s,%s", $2, $6, $7, $9); + fprintf(fp_netvar_h, "\t%s = %s,\n", $2, $6); + } + | /* nothing */ + ; + + +const_def: TOK_CONST opt_ws TOK_ID opt_ws ':' opt_ws TOK_ID opt_ws ';' + { + set_definition_type(CONST_DEF, 0); + set_decl_name($3); + int typeidx = get_type_index($7); + char accessor[1024]; + + snprintf(accessor, sizeof(accessor), builtin_types[typeidx].accessor, ""); + + + fprintf(fp_netvar_h, "%s extern %s %s; %s\n", + decl.c_namespace_start.c_str(), + builtin_types[typeidx].c_type, decl.bare_name.c_str(), + decl.c_namespace_end.c_str()); + fprintf(fp_netvar_def, "%s %s %s; %s\n", + decl.c_namespace_start.c_str(), + builtin_types[typeidx].c_type, decl.bare_name.c_str(), + decl.c_namespace_end.c_str()); + fprintf(fp_netvar_init, "\t%s = internal_const_val(\"%s\")%s;\n", + decl.c_fullname.c_str(), decl.bro_fullname.c_str(), + accessor); + + record_bif_item(decl.bro_fullname.c_str(), "CONSTANT"); + } + +attr_list: + attr_list TOK_ATTR + { $$ = concat($1, $2); } + | + TOK_ATTR + ; + +opt_attr_list: + attr_list + | /* nothing */ + { $$ = ""; } + ; + +func_prefix: TOK_FUNCTION + { set_definition_type(FUNC_DEF, 0); } + ; + +event_prefix: TOK_EVENT + { set_definition_type(EVENT_DEF, 0); } + ; + +end_of_head: /* nothing */ + { + fprintf(fp_bro_init, ";\n"); + } + ; + +typed_head: plain_head return_type + { + } + ; + +plain_head: head_1 args arg_end opt_ws + { + if ( var_arg ) + fprintf(fp_bro_init, "va_args: any"); + else + { + for ( int i = 0; i < (int) args.size(); ++i ) + { + if ( i > 0 ) + fprintf(fp_bro_init, ", "); + args[i]->PrintBro(fp_bro_init); + } + } + + fprintf(fp_bro_init, ")"); + + fprintf(fp_bro_init, "%s", $4); + fprintf(fp_func_def, "%s", $4); + } + ; + +head_1: TOK_ID opt_ws arg_begin + { + const char* method_type = 0; + set_decl_name($1); + + if ( definition_type == FUNC_DEF ) + { + method_type = "function"; + print_line_directive(fp_func_def); + } + else if ( definition_type == EVENT_DEF ) + method_type = "event"; + + if ( method_type ) + fprintf(fp_bro_init, + "global %s: %s%s(", + decl.bro_name.c_str(), method_type, $2); + + if ( definition_type == FUNC_DEF ) + { + fprintf(fp_func_init, + "\t(void) new BuiltinFunc(%s, \"%s\", 0);\n", + decl.c_fullname.c_str(), decl.bro_fullname.c_str()); + + fprintf(fp_func_h, + "%sextern Val* %s(Frame* frame, val_list*);%s\n", + decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str()); + + fprintf(fp_func_def, + "Val* %s(Frame* frame, val_list* %s)", + decl.c_fullname.c_str(), arg_list_name); + + record_bif_item(decl.bro_fullname.c_str(), "FUNCTION"); + } + else if ( definition_type == EVENT_DEF ) + { + // TODO: add namespace for events here + fprintf(fp_netvar_h, + "%sextern EventHandlerPtr %s; %s\n", + decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str()); + + fprintf(fp_netvar_def, + "%sEventHandlerPtr %s; %s\n", + decl.c_namespace_start.c_str(), decl.bare_name.c_str(), decl.c_namespace_end.c_str()); + + fprintf(fp_netvar_init, + "\t%s = internal_handler(\"%s\");\n", + decl.c_fullname.c_str(), decl.bro_fullname.c_str()); + + record_bif_item(decl.bro_fullname.c_str(), "EVENT"); + + // C++ prototypes of bro_event_* functions will + // be generated later. + } + } + ; + +arg_begin: TOK_LPP + { args.clear(); var_arg = 0; } + ; + +arg_end: TOK_RPP + ; + +args: args_1 + | opt_ws + { /* empty, to avoid yacc complaint about type clash */ } + ; + +args_1: args_1 ',' opt_ws arg opt_ws opt_attr_list + { if ( ! args.empty() ) args[args.size()-1]->SetAttrStr($6); } + | opt_ws arg opt_ws opt_attr_list + { if ( ! args.empty() ) args[args.size()-1]->SetAttrStr($4); } + ; + +// TODO: Migrate all other compound types to this rule. Once the BiF language +// can parse all regular Bro types, we can throw out the unnecessary +// boilerplate typedefs for addr_set, string_set, etc. +type: + TOK_OPAQUE opt_ws TOK_OF opt_ws TOK_ID + { $$ = concat("opaque of ", $5); } + | TOK_ID + { $$ = $1; } + ; + +arg: TOK_ID opt_ws ':' opt_ws type + { args.push_back(new BuiltinFuncArg($1, $5)); } + | TOK_VAR_ARG + { + if ( definition_type == EVENT_DEF ) + yyerror("events cannot have variable arguments"); + var_arg = 1; + } + ; + +return_type: ':' opt_ws type opt_ws + { + BuiltinFuncArg* ret = new BuiltinFuncArg("", $3); + ret->PrintBro(fp_bro_init); + delete ret; + fprintf(fp_func_def, "%s", $4); + } + ; + +body: body_start c_body body_end + { + fprintf(fp_func_def, " // end of %s\n", decl.c_fullname.c_str()); + print_line_directive(fp_func_def); + } + ; + +c_code_begin: /* empty */ + { + in_c_code = 1; + print_line_directive(fp_func_def); + } + ; + +c_code_end: /* empty */ + { in_c_code = 0; } + ; + +body_start: TOK_LPB c_code_begin + { + int implicit_arg = 0; + int argc = args.size(); + + fprintf(fp_func_def, "{"); + + if ( argc > 0 || ! var_arg ) + fprintf(fp_func_def, "\n"); + + if ( ! var_arg ) + { + fprintf(fp_func_def, "\tif ( %s->length() != %d )\n", arg_list_name, argc); + fprintf(fp_func_def, "\t\t{\n"); + fprintf(fp_func_def, + "\t\treporter->Error(\"%s() takes exactly %d argument(s)\");\n", + decl.bro_fullname.c_str(), argc); + fprintf(fp_func_def, "\t\treturn 0;\n"); + fprintf(fp_func_def, "\t\t}\n"); + } + else if ( argc > 0 ) + { + fprintf(fp_func_def, "\tif ( %s->length() < %d )\n", arg_list_name, argc); + fprintf(fp_func_def, "\t\t{\n"); + fprintf(fp_func_def, + "\t\treporter->Error(\"%s() takes at least %d argument(s)\");\n", + decl.bro_fullname.c_str(), argc); + fprintf(fp_func_def, "\t\treturn 0;\n"); + fprintf(fp_func_def, "\t\t}\n"); + } + + for ( int i = 0; i < (int) args.size(); ++i ) + args[i]->PrintCDef(fp_func_def, i + implicit_arg); + print_line_directive(fp_func_def); + } + ; + +body_end: TOK_RPB c_code_end + { + fprintf(fp_func_def, "}"); + } + ; + +c_code_segment: TOK_LPPB c_code_begin c_body c_code_end TOK_RPPB + ; + +c_body: opt_ws + { fprintf(fp_func_def, "%s", $1); } + | c_body c_atom opt_ws + { fprintf(fp_func_def, "%s", $3); } + ; + +c_atom: TOK_ID + { fprintf(fp_func_def, "%s", $1); } + | TOK_C_TOKEN + { fprintf(fp_func_def, "%s", $1); } + | TOK_ARG + { fprintf(fp_func_def, "(*%s)", arg_list_name); } + | TOK_ARGS + { fprintf(fp_func_def, "%s", arg_list_name); } + | TOK_ARGC + { fprintf(fp_func_def, "%s->length()", arg_list_name); } + | TOK_CSTR + { fprintf(fp_func_def, "%s", $1); } + | TOK_ATOM + { fprintf(fp_func_def, "%c", $1); } + | TOK_INT + { fprintf(fp_func_def, "%s", $1); } + + ; + +opt_ws: opt_ws TOK_WS + { $$ = concat($1, $2); } + | opt_ws TOK_LF + { $$ = concat($1, "\n"); } + | opt_ws TOK_COMMENT + { + if ( in_c_code ) + $$ = concat($1, $2); + else + if ( $2[1] == '#' ) + // This is a special type of comment that is used to + // generate bro script documentation, so pass it through. + $$ = concat($1, $2); + else + $$ = $1; + } + | /* empty */ + { $$ = ""; } + ; + +%% + +extern char* yytext; +extern char* input_filename; +extern int line_number; +void err_exit(void); + +void print_msg(const char msg[]) + { + int msg_len = strlen(msg) + strlen(yytext) + 64; + char* msgbuf = new char[msg_len]; + + if ( yytext[0] == '\n' ) + snprintf(msgbuf, msg_len, "%s, on previous line", msg); + + else if ( yytext[0] == '\0' ) + snprintf(msgbuf, msg_len, "%s, at end of file", msg); + + else + snprintf(msgbuf, msg_len, "%s, at or near \"%s\"", msg, yytext); + + /* + extern int column; + sprintf(msgbuf, "%*s\n%*s\n", column, "^", column, msg); + */ + + if ( input_filename ) + fprintf(stderr, "%s:%d: ", input_filename, line_number); + else + fprintf(stderr, "line %d: ", line_number); + fprintf(stderr, "%s\n", msgbuf); + + delete [] msgbuf; + } + +int yywarn(const char msg[]) + { + print_msg(msg); + return 0; + } + +int yyerror(const char msg[]) + { + print_msg(msg); + + err_exit(); + return 0; + } diff --git a/tools/bifcl/module_util.cc b/tools/bifcl/module_util.cc new file mode 100644 index 0000000000..d5817a7d7a --- /dev/null +++ b/tools/bifcl/module_util.cc @@ -0,0 +1,67 @@ +// +// See the file "COPYING" in the main distribution directory for copyright. + +#include +#include +#include "module_util.h" + +static int streq(const char* s1, const char* s2) + { + return ! strcmp(s1, s2); + } + +// Returns it without trailing "::". +string extract_module_name(const char* name) + { + string module_name = name; + string::size_type pos = module_name.rfind("::"); + + if ( pos == string::npos ) + return string(GLOBAL_MODULE_NAME); + + module_name.erase(pos); + + return module_name; + } + +string extract_var_name(const char *name) + { + string var_name = name; + string::size_type pos = var_name.rfind("::"); + + if ( pos == string::npos ) + return var_name; + + if ( pos + 2 > var_name.size() ) + return string(""); + + return var_name.substr(pos+2); + } + +string normalized_module_name(const char* module_name) + { + int mod_len; + if ( (mod_len = strlen(module_name)) >= 2 && + streq(module_name + mod_len - 2, "::") ) + mod_len -= 2; + + return string(module_name, mod_len); + } + +string make_full_var_name(const char* module_name, const char* var_name) + { + if ( ! module_name || streq(module_name, GLOBAL_MODULE_NAME) || + strstr(var_name, "::") ) + { + if ( streq(GLOBAL_MODULE_NAME, extract_module_name(var_name).c_str()) ) + return extract_var_name(var_name); + + return string(var_name); + } + + string full_name = normalized_module_name(module_name); + full_name += "::"; + full_name += var_name; + + return full_name; + }