diff -NraupbwB soc07-5184/nse_init.cc soc07-nse-pcap/nse_init.cc --- soc07-5184/nse_init.cc 2007-07-10 18:15:14.000000000 +0200 +++ soc07-nse-pcap/nse_init.cc 2007-07-11 23:06:41.000000000 +0200 @@ -8,6 +8,8 @@ #include "nbase.h" +#include "nse_nsock.h" + #include "nmap.h" #include "nmap_error.h" #include "NmapOps.h" diff -NraupbwB soc07-5184/nse_init.cc.orig soc07-nse-pcap/nse_init.cc.orig --- soc07-5184/nse_init.cc.orig 1970-01-01 01:00:00.000000000 +0100 +++ soc07-nse-pcap/nse_init.cc.orig 2007-07-10 18:15:14.000000000 +0200 @@ -0,0 +1,703 @@ +#include "nse_init.h" +#include "nse_nmaplib.h" +#include "nse_macros.h" +#include "nse_debug.h" + +// 3rd Party libs +#include "nse_pcrelib.h" + +#include "nbase.h" + +#include "nmap.h" +#include "nmap_error.h" +#include "NmapOps.h" + +#ifndef WIN32 + #include "dirent.h" +#endif + +#include "errno.h" + +#include +int init_setlualibpath(lua_State* l); +int init_loadfile(lua_State* l, char* filename); +int init_loaddir(lua_State* l, char* dirname); +int init_loadcategories(lua_State* l, std::vector categories, std::vector &unusedTags); +int init_scandir(char* dirname, std::vector& result, int files_or_dirs); +int init_fetchfile(char *result, size_t result_max_len, char* file); +int init_updatedb(lua_State* l); +int init_pick_default_categories(std::vector& chosenScripts); + +int check_extension(const char* ext, const char* path); + +extern NmapOps o; + +/* open the standard libs */ +int init_lua(lua_State* l) { + + const luaL_Reg lualibs[] = { + {"", luaopen_base}, + {LUA_LOADLIBNAME, luaopen_package}, + {LUA_TABLIBNAME, luaopen_table}, + {LUA_IOLIBNAME, luaopen_io}, + {LUA_OSLIBNAME, luaopen_os}, + {LUA_STRLIBNAME, luaopen_string}, + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_DBLIBNAME, luaopen_debug}, + {NSE_PCRELIBNAME, luaopen_pcrelib}, + {NULL, NULL} + }; + + const luaL_Reg* lib; + for (lib = lualibs; lib->func; lib++) { + lua_pushcfunction(l, lib->func); + lua_pushstring(l, lib->name); + SCRIPT_ENGINE_LUA_TRY(lua_pcall(l, 1, 0, 0)); + } + + + /* publish the nmap bindings to the script */ + lua_newtable(l); + SCRIPT_ENGINE_TRY(set_nmaplib(l)); + lua_setglobal(l, "nmap"); + SCRIPT_ENGINE_TRY(init_setlualibpath(l)); + + return SCRIPT_ENGINE_SUCCESS; +} + +/*sets two variables, which control where lua looks for modules (implemented in C or lua */ +int init_setlualibpath(lua_State* l){ + char path[MAX_FILENAME_LEN]; + const char*oldpath, *oldcpath; + std::string luapath, luacpath; + /* set the path lua searches for modules*/ + if(nmap_fetchfile(path, MAX_FILENAME_LEN, SCRIPT_ENGINE_LIB_DIR)!=2){ + /*SCRIPT_ENGINE_LIB_DIR is not a directory - error */ + error("%s: %s not a directory\n", SCRIPT_ENGINE, SCRIPT_ENGINE_LIB_DIR); + return SCRIPT_ENGINE_ERROR; + } + /* the path lua uses to search for modules is setted to the + * SCRIPT_ENGINE_LIBDIR/ *.lua with the default path + * (which is read from the package-module) appended - + * the path for C-modules is as above but it searches for shared libs (*.so) */ + luapath= std::string(path) + "?.lua;"; +#ifdef WIN32 + luacpath= std::string(path) + "?.dll;"; +#else + luacpath= std::string(path) + "?.so;"; +#endif + + lua_getglobal(l,"package"); + if(!lua_istable(l,-1)){ + error("%s: the lua global-variable package is not a table?!", SCRIPT_ENGINE); + return SCRIPT_ENGINE_ERROR; + } + lua_getfield(l,-1, "path"); + lua_getfield(l,-2, "cpath"); + if(!lua_isstring(l,-1)||!lua_isstring(l,-2)){ + error("%s: no default paths setted in package table (needed in %s at line %d) -- probably a problem of the lua-configuration?!", SCRIPT_ENGINE, __FILE__, __LINE__); + return SCRIPT_ENGINE_ERROR; + } + oldcpath= lua_tostring(l,-1); + oldpath = lua_tostring(l,-2); + luacpath= luacpath + oldcpath; + luapath= luapath + oldpath; + lua_pop(l,2); + lua_pushstring(l, luapath.c_str()); + lua_setfield(l, -2, "path"); + lua_pushstring(l, luacpath.c_str()); + lua_setfield(l, -2, "cpath"); + lua_getfield(l,-1, "path"); + lua_getfield(l,-2, "cpath"); + SCRIPT_ENGINE_DEBUGGING(log_write(LOG_STDOUT, "%s: Using %s to search for C-modules and %s for Lua-modules\n", SCRIPT_ENGINE, lua_tostring(l,-1), lua_tostring(l,-2));) + /*pop the two strings (luapath and luacpath) and the package table off + * the stack */ + lua_pop(l,3); + return SCRIPT_ENGINE_SUCCESS; +} + +/* if there were no command line arguments specifying + * which scripts should be run, a default script set is + * chosen + * otherwise the script locators given at the command line + * (either directories with lua files or lua files) are + * loaded + * */ +int init_rules(lua_State* l, std::vector chosenScripts) { + char path[MAX_FILENAME_LEN]; + int type; + char* c_iter; + std::vector unusedTags; + + lua_newtable(l); + lua_setglobal(l, PORTTESTS); + + lua_newtable(l); + lua_setglobal(l, HOSTTESTS); + + SCRIPT_ENGINE_TRY(init_pick_default_categories(chosenScripts)); + + // we try to interpret the choices as categories + SCRIPT_ENGINE_TRY(init_loadcategories(l, chosenScripts, unusedTags)); + + // if there's more, we try to interpret as directory or file + std::vector::iterator iter; + bool extension_not_matched = false; + for(iter = unusedTags.begin(); iter != unusedTags.end(); iter++) { + + c_iter = strdup((*iter).c_str()); + type = init_fetchfile(path, sizeof(path), c_iter); + free(c_iter); + + switch(type) { + case 0: // no such path + error("%s: No such category, file or directory: '%s'", SCRIPT_ENGINE, (*iter).c_str()); + return SCRIPT_ENGINE_ERROR; + break; + case 1: // nmap_fetchfile returned a file + if(check_extension(SCRIPT_ENGINE_EXTENSION, path) != MATCH + && extension_not_matched == false) { + error("%s: Warning: Loading '%s' - the recommended file extension is '.nse'.", + SCRIPT_ENGINE, path); + extension_not_matched = true; + } + SCRIPT_ENGINE_TRY(init_loadfile(l, path)); + break; + case 2: // nmap_fetchfile returned a dir + SCRIPT_ENGINE_TRY(init_loaddir(l, path)); + break; + default: + fatal("%s: In: %s:%i This should never happen.", + SCRIPT_ENGINE, __FILE__, __LINE__); + } + } + + // Compute some stats + SCRIPT_ENGINE_DEBUGGING( + int rules_count; + + lua_getglobal(l, HOSTTESTS); + rules_count = lua_objlen(l, -1); + + lua_getglobal(l, PORTTESTS); + rules_count += lua_objlen(l, -1); + lua_pop(l, 2); + log_write(LOG_STDOUT, "%s: Initialized %d rules\n", SCRIPT_ENGINE, rules_count); + ) + + return SCRIPT_ENGINE_SUCCESS; +} + +class ExtensionalCategory { +public: + std::string category; + int option; + + ExtensionalCategory(std::string _category, int _option) { + category = _category; + option = _option; + } +}; + +int init_pick_default_categories(std::vector& chosenScripts) { + std::vector reserved_categories; + std::vector::iterator rcat_iter; + + reserved_categories.push_back(ExtensionalCategory(std::string("version"), o.scriptversion)); + + // if they tried to explicitely select an implicit category, we complain + if(o.script) { + for( rcat_iter = reserved_categories.begin(); + rcat_iter != reserved_categories.end(); + rcat_iter++) { + if( (*rcat_iter).option == 0 + && std::find( + chosenScripts.begin(), + chosenScripts.end(), + (*rcat_iter).category) != chosenScripts.end()) + fatal("%s: specifying the \"%s\" category explicitely is not allowed.", + SCRIPT_ENGINE, (*rcat_iter).category.c_str()); + } + } + + // if no scripts were chosen, we use a default set + if( (o.script == 1 + && chosenScripts.size() == 0) ) + { + chosenScripts.push_back(std::string("safe")); + chosenScripts.push_back(std::string("intrusive")); + // chosenScripts.push_back(std::string("vulnerabilities")); + } + + // we append the implicitely selected categories + for( rcat_iter = reserved_categories.begin(); + rcat_iter != reserved_categories.end(); + rcat_iter++) { + if((*rcat_iter).option == 1) + chosenScripts.push_back((*rcat_iter).category); + } + + return SCRIPT_ENGINE_SUCCESS; +} + +int init_updatedb(lua_State* l) { + char path[MAX_FILENAME_LEN]; + FILE* scriptdb; + std::vector files; + std::vector::iterator iter; + char* c_iter; + + if(nmap_fetchfile(path, sizeof(path)-sizeof(SCRIPT_ENGINE_DATABASE)-1, SCRIPT_ENGINE_LUA_DIR) == 0) { + error("%s: Couldn't find '%s'\n.", SCRIPT_ENGINE, SCRIPT_ENGINE_LUA_DIR); + return SCRIPT_ENGINE_ERROR; + } + + SCRIPT_ENGINE_TRY(init_scandir(path, files, FILES)); + + // we rely on the fact that nmap_fetchfile returned a string which leaves enough room + // to append the db filename (see call to nmap_fetchfile above) + strncat(path, SCRIPT_ENGINE_DATABASE, MAX_FILENAME_LEN-1); + + scriptdb = fopen(path, "w"); + if(scriptdb == NULL) { + error("%s: Could not open '%s' for writing: %s", + SCRIPT_ENGINE, path, strerror(errno)); + return SCRIPT_ENGINE_ERROR; + } + + SCRIPT_ENGINE_DEBUGGING( + log_write(LOG_STDOUT, "%s: Trying to add %d scripts to the database.\n", + SCRIPT_ENGINE, (int) files.size()); + ) + + lua_newtable(l); + /*give the script global namespace access*/ + lua_newtable(l); + lua_getglobal(l, "_G"); + lua_setfield(l, -2, "__index"); + lua_setmetatable(l, -2); + + for(iter = files.begin(); iter != files.end(); iter++) { + c_iter = strdup((*iter).c_str()); + if(check_extension(SCRIPT_ENGINE_EXTENSION, c_iter) == MATCH + && strstr(c_iter, SCRIPT_ENGINE_DATABASE) == NULL) { + + SCRIPT_ENGINE_LUA_TRY(luaL_loadfile(l, c_iter)); + lua_pushvalue(l, -2); + lua_setfenv(l, -2); + SCRIPT_ENGINE_LUA_TRY(lua_pcall(l, 0, 0, 0)); + + lua_getfield(l, -1, "categories"); + if(lua_isnil(l, -1)) { + error("%s: Script '%s' does not contain any category categories.", SCRIPT_ENGINE, c_iter); + return SCRIPT_ENGINE_ERROR; + } + + lua_pushnil(l); + while(lua_next(l, -2) != 0) { + char *filename = path_get_basename(c_iter); + if (filename == NULL) { + error("%s: Could not allocate temporary memory.", SCRIPT_ENGINE); + return SCRIPT_ENGINE_ERROR; + } + fprintf(scriptdb, + "Entry{ category = \"%s\", filename = \"%s\" }\n", + lua_tostring(l, -1), filename); + free(filename); + lua_pop(l, 1); + } + lua_pop(l, 1); // pop the categories table + } + + free(c_iter); + } + lua_pop(l, 1); // pop the closure + + if(fclose(scriptdb) != 0) { + error("%s: Could not close %s: %s", SCRIPT_ENGINE, path, strerror(errno)); + return SCRIPT_ENGINE_ERROR; + } + + return SCRIPT_ENGINE_SUCCESS; +} + +int init_loadcategories(lua_State* l, std::vector categories, std::vector &unusedTags) { + std::vector::iterator iter; + std::vector files; + std::string dbpath = std::string(SCRIPT_ENGINE_LUA_DIR) + std::string(SCRIPT_ENGINE_DATABASE); + char* c_dbpath_buf; + char c_dbpath[MAX_FILENAME_LEN]; + const char* stub = "\ +files = {}\n\ +Entry = function(e)\n\ + if (categories[e.category] ~= nil) then\n\ + categories[e.category] = categories[e.category] + 1\n\ + files[e.filename] = true\n\ + end\n\ + if (categories[\"all\"] ~= nil and e.category ~= \"version\") then\n\ + categories[\"all\"] = categories[\"all\"] + 1\n\ + files[e.filename] = true\n\ + end\n\ +end\n"; + int categories_usage; + char* c_iter; + char script_path[MAX_FILENAME_LEN]; + int type; + + // closure + lua_newtable(l); + + // categories table + lua_newtable(l); + for(iter = categories.begin(); iter != categories.end(); iter++) { + lua_pushinteger(l, 0); + lua_setfield(l, -2, (*iter).c_str()); + } + lua_setfield(l, -2, "categories"); + + // we load the stub + // the strlen is safe in this case because the stub is a constant string + SCRIPT_ENGINE_LUA_TRY(luaL_loadbuffer(l, stub, strlen(stub), "Database Stub")); + lua_pushvalue(l, -2); + lua_setfenv(l, -2); + SCRIPT_ENGINE_LUA_TRY(lua_pcall(l, 0, 0, 0)); + + // if we can't find the database we try to create it + c_dbpath_buf = strdup(dbpath.c_str()); + if(nmap_fetchfile(c_dbpath, sizeof(c_dbpath), c_dbpath_buf) == 0) { + SCRIPT_ENGINE_TRY(init_updatedb(l)); + } + free(c_dbpath_buf); + + SCRIPT_ENGINE_LUA_TRY(luaL_loadfile(l, c_dbpath)); + lua_pushvalue(l, -2); + lua_setfenv(l, -2); + SCRIPT_ENGINE_LUA_TRY(lua_pcall(l, 0, 0, 0)); + + // retrieve the filenames produced by the stub + lua_getfield(l, -1, "files"); + lua_pushnil(l); + while(lua_next(l, -2) != 0) { + if(lua_isstring(l, -2)) + files.push_back(std::string(lua_tostring(l, -2))); + else { + error("%s: One of the filenames in '%s' is not a string?!", + SCRIPT_ENGINE, + SCRIPT_ENGINE_DATABASE); + return SCRIPT_ENGINE_ERROR; + } + lua_pop(l, 1); + } + lua_pop(l, 1); + + // find out which categories didn't produce any filenames + lua_getfield(l, -1, "categories"); + lua_pushnil(l); + while(lua_next(l, -2) != 0) { + categories_usage = lua_tointeger(l, -1); + if(categories_usage == 0) { + unusedTags.push_back(std::string(lua_tostring(l, -2))); + } + lua_pop(l, 1); + } + lua_pop(l, 2); + + // load all the files we have found for the given categories + for(iter = files.begin(); iter != files.end(); iter++) { + c_iter = strdup((*iter).c_str()); + type = init_fetchfile(script_path, sizeof(script_path), c_iter); + + if(type != 1) { + error("%s: %s is not a file.", SCRIPT_ENGINE, c_iter); + return SCRIPT_ENGINE_ERROR; + } + + free(c_iter); + + SCRIPT_ENGINE_TRY(init_loadfile(l, script_path)); + } + + return SCRIPT_ENGINE_SUCCESS; +} + +int init_fetchfile(char *path, size_t path_len, char* file) { + int type; + + type = nmap_fetchfile(path, path_len, file); + + // lets look in /scripts too + if(type == 0) { + char* alt_path = strdup((std::string(SCRIPT_ENGINE_LUA_DIR) + std::string(file)).c_str()); + type = nmap_fetchfile(path, path_len, alt_path); + free(alt_path); + + } + + return type; +} + +/* This is simply the most portable way to check + * if a file has a given extension. + * The portability comes at the price of reduced + * flexibility. + */ +int check_extension(const char* ext, const char* path) { + int pathlen = strlen(path); + int extlen = strlen(ext); + const char* offset; + + if( extlen > pathlen + || pathlen > MAX_FILENAME_LEN) + return -1; + + offset = path + pathlen - extlen; + + if(strcmp(offset, ext) != MATCH) + return 1; + else + return MATCH; +} + +int init_loaddir(lua_State* l, char* dirname) { + std::vector files; + char* c_iter; + + SCRIPT_ENGINE_TRY(init_scandir(dirname, files, FILES)); + + std::vector::iterator iter; + for(iter = files.begin(); iter != files.end(); iter++) { + c_iter = strdup((*iter).c_str()); + SCRIPT_ENGINE_TRY(init_loadfile(l, c_iter)); + free(c_iter); + } + + return SCRIPT_ENGINE_SUCCESS; +} + +#ifdef WIN32 + +int init_scandir(char* dirname, std::vector& result, int files_or_dirs) { + HANDLE dir; + WIN32_FIND_DATA entry; + std::string path; + BOOL morefiles = FALSE; + + dir = FindFirstFile((std::string(dirname) + "\\*").c_str(), &entry); + + if (dir == INVALID_HANDLE_VALUE) + { + error("%s: No files in '%s\\*'", SCRIPT_ENGINE, dirname); + return SCRIPT_ENGINE_ERROR; + } + + while(!(morefiles == FALSE && GetLastError() == ERROR_NO_MORE_FILES)) { + // if we are looking for files and this file doesn't end with .nse or + // is a directory, then we don't look further at it + if(files_or_dirs == FILES) { + if(!( + (check_extension(SCRIPT_ENGINE_EXTENSION, entry.cFileName) == MATCH) + && !(entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + )) { + morefiles = FindNextFile(dir, &entry); + continue; + } + + // if we are looking for dirs and this dir + // isn't a directory, then we don't look further at it + } else if(files_or_dirs == DIRS) { + if(!( + (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + )) { + morefiles = FindNextFile(dir, &entry); + continue; + } + + // they have passed an invalid value for files_or_dirs + } else { + fatal("%s: In: %s:%i This should never happen.", + SCRIPT_ENGINE, __FILE__, __LINE__); + } + + // otherwise we add it to the results + // we assume that dirname ends with a directory separator of some kind + path = std::string(dirname) + std::string(entry.cFileName); + result.push_back(path); + morefiles = FindNextFile(dir, &entry); + } + + + return SCRIPT_ENGINE_SUCCESS; +} + +#else + +int init_scandir(char* dirname, std::vector& result, int files_or_dirs) { + DIR* dir; + struct dirent* entry; + std::string path; + struct stat stat_entry; + + dir = opendir(dirname); + if(dir == NULL) { + error("%s: Could not open directory '%s'.", SCRIPT_ENGINE, dirname); + return SCRIPT_ENGINE_ERROR; + } + + // note that if there is a symlink in the dir, we have to rely on + // the .nse extension + // if they provide a symlink to a dir which ends with .nse, things + // break :/ + while((entry = readdir(dir)) != NULL) { + path = std::string(dirname) + "/" + std::string(entry->d_name); + + if(stat(path.c_str(), &stat_entry) != 0) + fatal("%s: In: %s:%i This should never happen.", + SCRIPT_ENGINE, __FILE__, __LINE__); + + // if we are looking for files and this file doesn't end with .nse and + // isn't a file or a link, then we don't look further at it + if(files_or_dirs == FILES) { + if(!( + (check_extension(SCRIPT_ENGINE_EXTENSION, entry->d_name) == MATCH) + && (S_ISREG(stat_entry.st_mode) + || S_ISLNK(stat_entry.st_mode)) + )) { + continue; + } + + // if we are looking for dirs and this dir + // isn't a dir or a link, then we don't look further at it + } else if(files_or_dirs == DIRS) { + if(!( + (S_ISDIR(stat_entry.st_mode) + || S_ISLNK(stat_entry.st_mode)) + )) { + continue; + } + + // they have passed an invalid value for files_or_dirs + } else { + fatal("%s: In: %s:%i This should never happen.", + SCRIPT_ENGINE, __FILE__, __LINE__); + } + + // otherwise we add it to the results + result.push_back(path); + } + + closedir(dir); + + return SCRIPT_ENGINE_SUCCESS; +} + +#endif + +/* load an nmap-lua script + * create a new closure to store the script + * tell the closure where to find the standard + * lua libs and the nmap bindings + * we do some error checking to make sure that + * the script is well formed + * the script is then added to either the hostrules + * or the portrules + * */ +int init_loadfile(lua_State* l, char* filename) { + int rule_count; + + /* create a closure for encapsuled execution + * give the closure access to the global enviroment + */ + lua_newtable(l); + + /* tell the script about its filename */ + lua_pushstring(l, filename); + lua_setfield(l, -2, "filename"); + + /* we give the script access to the global name space + * */ + lua_newtable(l); + lua_getglobal(l, "_G"); + lua_setfield(l, -2, "__index"); + lua_setmetatable(l, -2); + + /* load the *.nse file, set the closure and execute (init) the test + * */ + SCRIPT_ENGINE_LUA_TRY(luaL_loadfile(l, filename)); + lua_pushvalue(l, -2); + lua_setfenv(l, -2); + SCRIPT_ENGINE_LUA_TRY(lua_pcall(l, 0, 0, 0)); + + /* look at the runlevel, if it is not set, we set it to 1.0 + * */ + lua_getfield(l, -1, RUNLEVEL); + if(lua_isnil(l, -1)) { + lua_pushnumber(l, 1.0); + lua_setfield(l, -3, RUNLEVEL); + } + lua_pop(l, 1); + + /* finally we make sure nobody tampers with the global name space any more + * and prepare for runlevel sorting + * */ + lua_getmetatable(l, -1); + + std::string buf = + (std::string("err = \"Attempted to change the global '\" .. select(2, ...) .. \"' in ") + + std::string(filename) + + std::string(" - use nmap.registry if you really want to share data between scripts.\"") + + std::string("error(err)")); + SCRIPT_ENGINE_LUA_TRY(luaL_loadbuffer(l, buf.c_str(), buf.length(), "Global Access")); + lua_setfield(l, -2, "__newindex"); + + lua_setmetatable(l, -2); + + /* store the initialized test in either + * the hosttests or the porttests + * */ + lua_getfield(l, -1, PORTRULE); + lua_getfield(l, -2, HOSTRULE); + + /* if we are looking at a portrule then store it in the porttestsets table + * if it is a hostrule, then it goes into the hosttestsets table + * otherwise we fail + * if there is no action in the script we also fail + * */ + if(lua_isnil(l, -2) == 0) { + lua_pop(l, 2); + lua_getglobal(l, PORTTESTS); + rule_count = lua_objlen(l, -1); + lua_pushvalue(l, -2); + lua_rawseti(l, -2, (rule_count + 1)); + lua_pop(l, 1); // pop the porttests table + } else if(lua_isnil(l, -1) == 0) { + lua_pop(l, 2); + lua_getglobal(l, HOSTTESTS); + rule_count = lua_objlen(l, -1); + lua_pushvalue(l, -2); + lua_rawseti(l, -2, (rule_count + 1)); + lua_pop(l, 1); // pop the hosttests table + } else { + error("%s: No rules in script '%s'.", SCRIPT_ENGINE, filename); + return SCRIPT_ENGINE_ERROR; + } + + std::vector required_fields; + required_fields.push_back(std::string(ACTION)); + required_fields.push_back(std::string(DESCRIPTION)); + + std::vector::iterator iter; + for(iter = required_fields.begin(); iter != required_fields.end(); iter++) { + lua_getfield(l, -1, (*iter).c_str()); + if(lua_isnil(l, -1) == 1) { + error("%s: No '%s' field in script '%s'.", SCRIPT_ENGINE, (*iter).c_str(), filename); + return SCRIPT_ENGINE_ERROR; + } + lua_pop(l, 1); // pop the action + } + + + lua_pop(l, 1); // pop the closure + + return SCRIPT_ENGINE_SUCCESS; +} + diff -NraupbwB soc07-5184/nse_nmaplib.cc soc07-nse-pcap/nse_nmaplib.cc --- soc07-5184/nse_nmaplib.cc 2007-07-10 18:15:14.000000000 +0200 +++ soc07-nse-pcap/nse_nmaplib.cc 2007-07-11 23:06:41.000000000 +0200 @@ -37,6 +37,8 @@ static int l_get_port_state(lua_State* l static int l_set_port_state(lua_State* l, Target* target, Port* port); static int l_set_port_version(lua_State* l, Target* target, Port* port); +int l_clock_ms(lua_State* l); + /* register the nmap lib * we assume that we can write to a table at -1 on the stack * */ @@ -46,6 +48,9 @@ int set_nmaplib(lua_State* l) { {"set_port_state", l_port_accessor}, {"set_port_version", l_port_accessor}, {"new_socket", l_nsock_new}, + {"new_dnet", l_dnet_new}, + {"get_interface_link", l_dnet_get_interface_link}, + {"clock_ms", l_clock_ms}, {"print_debug_unformatted", l_print_debug_unformatted}, {"new_try", l_exc_newtry}, {NULL, NULL} @@ -61,6 +66,7 @@ int set_nmaplib(lua_State* l) { lua_setfield(l, -2, "registry"); SCRIPT_ENGINE_TRY(l_nsock_open(l)); + SCRIPT_ENGINE_TRY(l_dnet_open(l)); return SCRIPT_ENGINE_SUCCESS; } @@ -181,6 +187,33 @@ void set_hostinfo(lua_State* l, Target * lua_pushstring(l, strncpy(hostname, currenths->HostName(), 1024)); lua_setfield(l, -2, "name"); + if(currenths->directlyConnectedOrUnset() != -1){ + lua_pushboolean(l, currenths->directlyConnectedOrUnset()); + lua_setfield(l, -2, "directly_connected"); + } + + if(currenths->MACAddress()){ // else nil + lua_pushlstring (l, (const char*)currenths->MACAddress() , 6); + lua_setfield(l, -2, "mac_addr"); + } + if(currenths->SrcMACAddress()){ // else nil + lua_pushlstring(l, (const char*)currenths->SrcMACAddress(), 6); + lua_setfield(l, -2, "mac_addr_src"); + } + if(currenths->deviceName()){ + lua_pushstring(l, strncpy(hostname, currenths->deviceName(), 1024)); + lua_setfield(l, -2, "interface"); + } + if( (u32)(currenths->v4host().s_addr) ){ + struct in_addr adr = currenths->v4host(); + lua_pushlstring(l, (char*)&adr, 4); + lua_setfield(l, -2, "bin_ip"); + } + if( (u32)(currenths->v4source().s_addr) ){ + struct in_addr adr = currenths->v4source(); + lua_pushlstring(l, (char*)&adr, 4); + lua_setfield(l, -2, "bin_ip_src"); + } FingerPrintResults *FPR = NULL; int osscanSys; diff -NraupbwB soc07-5184/nse_nmaplib.cc.orig soc07-nse-pcap/nse_nmaplib.cc.orig --- soc07-5184/nse_nmaplib.cc.orig 1970-01-01 01:00:00.000000000 +0100 +++ soc07-nse-pcap/nse_nmaplib.cc.orig 2007-07-10 18:15:14.000000000 +0200 @@ -0,0 +1,446 @@ +#include "nse_nmaplib.h" +#include "nse_nsock.h" +#include "nse_macros.h" +#include "nse_debug.h" + +#include "nmap_error.h" +#include "osscan.h" +#include "NmapOps.h" +#include "nmap_rpc.h" +#include "Target.h" +#include "output.h" +#include "portlist.h" + +#define SCRIPT_ENGINE_GETSTRING(name) \ + char* name; \ + lua_getfield(l, -1, #name); \ + if(lua_isnil(l, -1)) \ + name = NULL; \ + else \ + name = strdup(lua_tostring(l, -1)); \ + lua_pop(l, 1); \ + +#define SCRIPT_ENGINE_PUSHSTRING_NOTNULL(c_str, str) if(c_str != NULL) {\ + lua_pushstring(l, c_str); \ + lua_setfield(l, -2, str); \ +} + +extern NmapOps o; +extern std::map current_hosts; + +void set_version(lua_State* l, struct serviceDeductions sd); + +static int l_exc_newtry(lua_State *l); +static int l_port_accessor(lua_State* l); +static int l_print_debug_unformatted(lua_State *l); +static int l_get_port_state(lua_State* l, Target* target, Port* port); +static int l_set_port_state(lua_State* l, Target* target, Port* port); +static int l_set_port_version(lua_State* l, Target* target, Port* port); + +/* register the nmap lib + * we assume that we can write to a table at -1 on the stack + * */ +int set_nmaplib(lua_State* l) { + static luaL_reg nmaplib [] = { + {"get_port_state", l_port_accessor}, + {"set_port_state", l_port_accessor}, + {"set_port_version", l_port_accessor}, + {"new_socket", l_nsock_new}, + {"print_debug_unformatted", l_print_debug_unformatted}, + {"new_try", l_exc_newtry}, + {NULL, NULL} + }; + + const luaL_Reg* lib; + for (lib = nmaplib; lib->func; lib++) { + lua_pushcfunction(l, lib->func); + lua_setfield(l, -2, lib->name); + } + + lua_newtable(l); + lua_setfield(l, -2, "registry"); + + SCRIPT_ENGINE_TRY(l_nsock_open(l)); + + return SCRIPT_ENGINE_SUCCESS; +} + +/* set some port state information onto the + * table which is currently on the stack + * */ +void set_portinfo(lua_State* l, Port* port) { + struct serviceDeductions sd; + + port->getServiceDeductions(&sd); + + lua_pushnumber(l, (double) port->portno); + lua_setfield(l, -2, "number"); + + lua_pushstring(l, sd.name); + lua_setfield(l, -2, "service"); + + lua_pushstring(l, (port->proto == IPPROTO_TCP)? "tcp": "udp"); + lua_setfield(l, -2, "protocol"); + + lua_newtable(l); + set_version(l, sd); + lua_setfield(l, -2, "version"); + + lua_pushstring(l, statenum2str(port->state)); + lua_setfield(l, -2, "state"); +} + +void set_version(lua_State* l, struct serviceDeductions sd) { + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.name, "name"); + + lua_pushnumber(l, sd.name_confidence); + lua_setfield(l, -2, "name_confidence"); + + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.product, "product"); + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.version, "version"); + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.extrainfo, "extrainfo"); + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.hostname, "hostname"); + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.ostype, "ostype"); + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.devicetype, "devicetype"); + + switch(sd.service_tunnel) { + case(SERVICE_TUNNEL_NONE): + SCRIPT_ENGINE_PUSHSTRING_NOTNULL("none", "service_tunnel"); + break; + case(SERVICE_TUNNEL_SSL): + SCRIPT_ENGINE_PUSHSTRING_NOTNULL("ssl", "service_tunnel"); + break; + default: + fatal("%s: In: %s:%i This should never happen.", + SCRIPT_ENGINE, __FILE__, __LINE__); + break; + } + + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.service_fp, "service_fp"); + + switch(sd.dtype) { + case(SERVICE_DETECTION_TABLE): + SCRIPT_ENGINE_PUSHSTRING_NOTNULL("table", "service_fp"); + break; + case(SERVICE_DETECTION_PROBED): + SCRIPT_ENGINE_PUSHSTRING_NOTNULL("probed", "service_fp"); + break; + default: + fatal("%s: In: %s:%i This should never happen.", + SCRIPT_ENGINE, __FILE__, __LINE__); + break; + } + + switch(sd.rpc_status) { + case(RPC_STATUS_UNTESTED): + SCRIPT_ENGINE_PUSHSTRING_NOTNULL("untested", "rpc_status"); + break; + case(RPC_STATUS_UNKNOWN): + SCRIPT_ENGINE_PUSHSTRING_NOTNULL("unknown", "rpc_status"); + break; + case(RPC_STATUS_GOOD_PROG): + SCRIPT_ENGINE_PUSHSTRING_NOTNULL("good_prog", "rpc_status"); + break; + case(RPC_STATUS_NOT_RPC): + SCRIPT_ENGINE_PUSHSTRING_NOTNULL("not_rpc", "rpc_status"); + break; + default: + fatal("%s: In: %s:%i This should never happen.", + SCRIPT_ENGINE, __FILE__, __LINE__); + break; + } + + if(sd.rpc_status == RPC_STATUS_GOOD_PROG) { + lua_pushnumber(l, sd.rpc_program); + lua_setfield(l, -2, "rpc_program"); + + lua_pushnumber(l, sd.rpc_lowver); + lua_setfield(l, -2, "rpc_lowver"); + + lua_pushnumber(l, sd.rpc_highver); + lua_setfield(l, -2, "rpc_highver"); + } +} + +/* set host ip and host name onto the + * table which is currently on the stack + * set name of the os run by the host onto the + * table which is currently on the stack + * the os name is really an array with perfect + * matches + * if an os scan wasn't performed, the array + * points to nil! + * */ +void set_hostinfo(lua_State* l, Target *currenths) { + unsigned int i; + char hostname[1024]; + + lua_pushstring(l, strncpy(hostname, currenths->targetipstr(), 1024)); + lua_setfield(l, -2, "ip"); + + lua_pushstring(l, strncpy(hostname, currenths->HostName(), 1024)); + lua_setfield(l, -2, "name"); + + + FingerPrintResults *FPR = NULL; + int osscanSys; + + if (currenths->FPR != NULL && currenths->FPR1 == NULL) { + osscanSys = 2; + FPR = currenths->FPR; + } else if(currenths->FPR == NULL && currenths->FPR1 != NULL) { + osscanSys = 1; + FPR = currenths->FPR1; + } + + /* if there has been an os scan which returned a pretty certain + * result, we will use it in the scripts + * matches which aren't perfect are not needed in the scripts + */ + if( currenths->osscanPerformed() && + FPR != NULL && + FPR->overall_results == OSSCAN_SUCCESS && + FPR->num_perfect_matches > 0 && + FPR->num_perfect_matches <= 8 ) { + + lua_newtable(l); + + // this will run at least one time and at most 8 times, see if condition + for(i = 0; FPR->accuracy[i] == 1; i++) { + lua_pushstring(l, FPR->prints[i]->OS_name); + lua_rawseti(l, -2, i); + } + lua_setfield(l, -2, "os"); + } +} + +static int l_port_accessor(lua_State* l) { + int retvalues = 0; + + char* function_name; + char* target_ip; + int portno; + int proto; + + Target* target; + PortList* plist; + Port* port; + + lua_Debug ldebug; + lua_getstack(l, 0, &ldebug); + lua_getinfo(l, "n", &ldebug); + function_name = strdup(ldebug.name); + + luaL_checktype(l, 1, LUA_TTABLE); + luaL_checktype(l, 2, LUA_TTABLE); + + lua_getfield(l, 1, "ip"); + luaL_checktype(l, -1, LUA_TSTRING); + target_ip = strdup(lua_tostring(l, -1)); + lua_pop(l, 1); + + lua_getfield(l, 2, "number"); + luaL_checktype(l, -1, LUA_TNUMBER); + portno = lua_tointeger(l, -1); + lua_pop(l, 1); + + lua_getfield(l, 2, "protocol"); + luaL_checktype(l, -1, LUA_TSTRING); + proto = (strcmp(lua_tostring(l, -1), "tcp") == 0)? IPPROTO_TCP : IPPROTO_UDP; + lua_pop(l, 1); + + std::string key = std::string(target_ip); + std::map::iterator iter = current_hosts.find(key); + if(iter == current_hosts.end()) { + luaL_argerror (l, 1, "Host isn't being processed right now."); + return 0; + } else { + target = (Target*) iter->second; + } + + plist = &(target->ports); + port = NULL; + + while((port = plist->nextPort(port, proto, PORT_UNKNOWN)) != NULL) { + if(port->portno == portno) + break; + } + + // if the port wasn't scanned we return nil + if(port == NULL) + return 0; + + if(strcmp(function_name, "set_port_state") == MATCH) + retvalues = l_set_port_state(l, target, port); + else if(strcmp(function_name, "set_port_version") == MATCH) + retvalues = l_set_port_version(l, target, port); + else if(strcmp(function_name, "get_port_state") == MATCH) + retvalues = l_get_port_state(l, target, port); + + // remove host and port argument from the stack + lua_remove(l, 2); + lua_remove(l, 1); + free(target_ip); + free(function_name); + return retvalues; +} + +/* this function can be called from lua to obtain the port state + * of a port different from the one the script rule is matched + * against + * it retrieves the host.ip of the host on which the script is + * currently running, looks up the host in the table of currently + * processed hosts and returns a table containing the state of + * the port we have been asked for + * this function is useful if we want rules which want to know + * the state of more than one port + * */ +static int l_get_port_state(lua_State* l, Target* target, Port* port) { + lua_newtable(l); + set_portinfo(l, port); + + return 1; +} + +/* unlike set_portinfo() this function sets the port state in nmap. + * if for example a udp port was seen by the script as open instead of + * filtered, the script is free to say so. + * */ +static int l_set_port_state(lua_State* l, Target* target, Port* port) { + char* state; + PortList* plist = &(target->ports); + + luaL_checktype(l, -1, LUA_TSTRING); + state = strdup(lua_tostring(l, -1)); + lua_pop(l, 1); + + switch(state[0]) { + case 'o': + if (strcmp(state, "open")) + luaL_argerror (l, 4, "Invalid port state."); + plist->addPort(port->portno, port->proto, NULL, PORT_OPEN); + port->state = PORT_OPEN; + break; + case 'c': + if (strcmp(state, "closed")) + luaL_argerror (l, 4, "Invalid port state."); + plist->addPort(port->portno, port->proto, NULL, PORT_CLOSED); + port->state = PORT_CLOSED; + break; + default: + luaL_argerror (l, 4, "Invalid port state."); + } + + free(state); + return 0; +} + +static int l_set_port_version(lua_State* l, Target* target, Port* port) { + luaL_checktype(l, 3, LUA_TSTRING); + char* c_probestate = strdup(lua_tostring(l, -1)); + lua_pop(l, 1); + + enum service_tunnel_type tunnel = SERVICE_TUNNEL_NONE; + enum serviceprobestate probestate = PROBESTATE_INITIAL; + + lua_getfield(l, -1, "version"); + SCRIPT_ENGINE_GETSTRING(name); + SCRIPT_ENGINE_GETSTRING(product); + SCRIPT_ENGINE_GETSTRING(version); + SCRIPT_ENGINE_GETSTRING(extrainfo); + SCRIPT_ENGINE_GETSTRING(hostname); + SCRIPT_ENGINE_GETSTRING(ostype); + SCRIPT_ENGINE_GETSTRING(devicetype); + // SCRIPT_ENGINE_GETSTRING(fingerprint); + + SCRIPT_ENGINE_GETSTRING(service_tunnel); + if(strcmp(service_tunnel, "none") == 0) + tunnel = SERVICE_TUNNEL_NONE; + else if(strcmp(service_tunnel, "ssl") == 0) + tunnel = SERVICE_TUNNEL_SSL; + else + luaL_argerror(l, 2, "Invalid value for port.version.service_tunnel"); + lua_pop(l, 1); + + if(strcmp(c_probestate, "hardmatched") == 0) + probestate = PROBESTATE_FINISHED_HARDMATCHED; + else if(strcmp(c_probestate, "softmatched") == 0) + probestate = PROBESTATE_FINISHED_SOFTMATCHED; + else if(strcmp(c_probestate, "nomatch") == 0) + probestate = PROBESTATE_FINISHED_NOMATCH; + else if(strcmp(c_probestate, "tcpwrapped") == 0) + probestate = PROBESTATE_FINISHED_TCPWRAPPED; + else if(strcmp(c_probestate, "incomplete") == 0) + probestate = PROBESTATE_INCOMPLETE; + else + luaL_argerror(l, 3, "Invalid value for probestate."); + +// port->setServiceProbeResults(probestate, name, +// tunnel, product, version, +// extrainfo, hostname, ostype, +// devicetype, fingerprint); + port->setServiceProbeResults(probestate, name, + tunnel, product, version, + extrainfo, hostname, ostype, + devicetype, NULL); + + + + free(service_tunnel); + free(name); + free(product); + free(version); + free(extrainfo); + free(hostname); + free(ostype); + free(devicetype); +// free(fingerprint); + return 0; +} + +static int l_print_debug_unformatted(lua_State *l) { + int verbosity=1; + const char *out; + + if (lua_gettop(l) != 2) return luaL_error(l, "Incorrect number of arguments\n"); + + verbosity = luaL_checkinteger(l, 1); + if (verbosity > o.verbose) return 0; + out = luaL_checkstring(l, 2); + + log_write(LOG_STDOUT, "%s DEBUG: %s\n", SCRIPT_ENGINE, out); + + return 0; +} + +static int l_exc_finalize(lua_State *l) { + if (lua_isnil(l, 1)) { + lua_pushvalue(l, lua_upvalueindex(1)); + lua_call(l, 0, 0); + lua_settop(l, 2); + lua_error(l); + return 0; + } else if(lua_toboolean(l, 1)) { + lua_remove(l, 1); + return lua_gettop(l); + } else { + fatal("%s: In: %s:%i Trying to finalize a non conforming function. Are you sure you return true on success followed by the remaining return values and nil on failure followed by an error string?", + SCRIPT_ENGINE, __FILE__, __LINE__); + + return 0; + } +} + +static int l_exc_do_nothing(lua_State *l) { + (void) l; + return 0; +} + +static int l_exc_newtry(lua_State *l) { + lua_settop(l, 1); + if (lua_isnil(l, 1)) + lua_pushcfunction(l, l_exc_do_nothing); + lua_pushcclosure(l, l_exc_finalize, 1); + return 1; +} + diff -NraupbwB soc07-5184/nse_nsock.cc soc07-nse-pcap/nse_nsock.cc --- soc07-5184/nse_nsock.cc 2007-07-10 18:15:14.000000000 +0200 +++ soc07-nse-pcap/nse_nsock.cc 2007-07-11 23:41:14.000000000 +0200 @@ -6,6 +6,9 @@ #include "nsock.h" #include "nmap_error.h" #include "NmapOps.h" +#include "utils.h" +#include "dnet.h" +#include "tcpip.h" #include #include @@ -41,6 +44,12 @@ static int l_nsock_gc(lua_State* l); static int l_nsock_close(lua_State* l); static int l_nsock_set_timeout(lua_State* l); +static int l_nsock_ncap_open(lua_State* l); +static int l_nsock_ncap_close(lua_State* l); +static int l_nsock_ncap_register(lua_State *l); +static int l_nsock_pcap_receive(lua_State* l); + + void l_nsock_connect_handler(nsock_pool nsp, nsock_event nse, void *lua_state); void l_nsock_send_handler(nsock_pool nsp, nsock_event nse, void *lua_state); void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state); @@ -61,15 +70,65 @@ static luaL_reg l_nsock [] = { {"close", l_nsock_close}, {"set_timeout", l_nsock_set_timeout}, {"__gc",l_nsock_gc}, + {"pcap_open", l_nsock_ncap_open}, + {"pcap_close", l_nsock_ncap_close}, + {"pcap_register", l_nsock_ncap_register}, + {"pcap_receive", l_nsock_pcap_receive}, +// {"callback_test", l_nsock_pcap_callback_test}, {NULL, NULL} }; static nsock_pool nsp; +/* + * Structure with nsock pcap descriptor. + * shared between many lua threads + */ +struct ncap_socket{ + nsock_iod nsiod; /* nsock pcap desc */ + int references; /* how many lua threads use this */ + char *key; /* (free) zero-terminated key used in map to + * address this structure. */ +}; + +/* + * + */ +struct ncap_request{ + int suspended; /* is the thread suspended? (lua_yield) */ + lua_State *l; /* lua_State of current process + * or NULL if process isn't suspended */ + nsock_event_id nseid; /* nse for this specific lua_State */ + struct timeval end_time; + char *key; /* (free) zero-terminated key used in map to + * address this structure (hexified 'test') */ + + bool received; /* are results ready? */ + + bool r_success; /* true-> okay,data ready to pass to user + * flase-> this statusstring contains error description */ + char * r_status; /* errorstring */ + + unsigned char *r_layer2; + size_t r_layer2_len; + unsigned char *r_layer3; + size_t r_layer3_len; + size_t packetsz; + + int ncap_cback_ref; /* just copy of udata->ncap_cback_ref + * because we don't have access to udata in place + * we need to use this. */ +}; + + struct l_nsock_udata { int timeout; nsock_iod nsiod; void *ssl_session; + + struct ncap_socket *ncap_socket; + struct ncap_request *ncap_request; + int ncap_cback_ref; }; int l_nsock_open(lua_State* l) { @@ -90,6 +149,9 @@ int l_nsock_new(lua_State* l) { udata->nsiod = NULL; udata->ssl_session = NULL; udata->timeout = DEFAULT_TIMEOUT; + udata->ncap_socket = NULL; + udata->ncap_request = NULL; + udata->ncap_cback_ref = 0; return 1; } @@ -303,9 +365,9 @@ void l_nsock_trace(nsock_iod nsiod, char char* ipstring_local = (char*) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN); char* ipstring_remote = (char*) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN); + if(!nsi_is_pcap(nsiod)){ status = nsi_getlastcommunicationinfo(nsiod, &protocol, &af, &local, &remote, sizeof(sockaddr)); - log_write(LOG_STDOUT, "SCRIPT ENGINE: %s %s:%d %s %s:%d | %s\n", (protocol == IPPROTO_TCP)? "TCP" : "UDP", inet_ntop_both(af, &local, ipstring_local), @@ -317,6 +379,11 @@ void l_nsock_trace(nsock_iod nsiod, char free(ipstring_local); free(ipstring_remote); + }else{ // is pcap device + log_write(LOG_STDOUT, "SCRIPT ENGINE: %s | %s\n", + (direction == TO)? ">" : "<", + message); + } } char* inet_ntop_both(int af, const void* v_addr, char* ipstring) { @@ -394,10 +461,16 @@ static int l_nsock_gc(lua_State* l){ l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); if(udata->nsiod == NULL) { //socket obviously got closed already - so no finalization needed return 0; - }else{ + } + + /* Never ever collect nse-pcap connections. */ + if(udata->ncap_socket){ + return 0; + } + //FIXME - check wheter close returned true!! l_nsock_close(l); - } + return 0; } static int l_nsock_close(lua_State* l) { @@ -436,3 +509,612 @@ static int l_nsock_set_timeout(lua_State return 0; } +/****************** NCAP_SOCKET ***********************************************/ +#ifdef WIN32 +/* From tcpip.cc. Gets pcap device name from dnet name. */ +bool DnetName2PcapName(const char *dnetdev, char *pcapdev, int pcapdevlen); +#endif + +/* fuckin' C++ maps stuff */ +/* here we store ncap_sockets */ +std::map ncap_socket_map; + +/* receive sthing from socket_map */ +struct ncap_socket *ncap_socket_map_get(char *key){ + std::string skey = key; + return ncap_socket_map[skey]; +} + +/* set sthing on socket_map */ +void ncap_socket_map_set(char *key, struct ncap_socket *ns){ + std::string skey = key; + ncap_socket_map[skey] = ns; + return; +} + +/* receive sthing from socket_map */ +void ncap_socket_map_del(char *key){ + std::string skey = key; + ncap_socket_map.erase(skey); + return; +} + + +/* (static) Dnet-like device name to Pcap-like name */ +char *dnet_to_pcap_device_name(const char *device){ + static char pcapdev[128]; + if( strcmp(device, "any") == 0 ) + return strncpy(pcapdev, "any", sizeof(pcapdev)); + + #ifdef WIN32 + /* Nmap normally uses device names obtained through dnet for interfaces, + * but Pcap has its own naming system. So the conversion is done here */ + if (!DnetName2PcapName(device, pcapdev, sizeof(pcapdev))) { + /* Oh crap -- couldn't find the corresponding dev apparently. + * Let's just go with what we have then ... */ + strncpy(pcapdev, device, sizeof(pcapdev)); + } + #else + strncpy(pcapdev, device, sizeof(pcapdev)); + #endif + return pcapdev; +} + +/* (LUA) Open nsock-pcap socket. + * 1) device - dnet-style network interface name, or "any" + * 2) snaplen - maximum number of bytes to be captured for packet + * 3) promisc - should we set network car in promiscuous mode (0/1) + * 4) callback- callback function, that will create hash string from packet + * 5) bpf - berkeley packet filter, see tcpdump(8) + * */ +static int l_nsock_ncap_open(lua_State* l){ + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + const char* device = luaL_checkstring(l, 2); + int snaplen = luaL_checkint(l, 3); + int promisc = luaL_checkint(l, 4); + luaL_checktype(l, 5, LUA_TFUNCTION); /* callback function that creates hash */ + const char* bpf = luaL_checkstring(l, 6); + + if(udata->nsiod || udata->ncap_request || udata->ncap_socket) { + luaL_argerror(l, 1, "Trying to open nsock-pcap, but this connection is already opened"); + return 0; + } + char *pcapdev = dnet_to_pcap_device_name(device); + if(!strlen(device) || !strlen(pcapdev)) { + luaL_argerror(l, 1, "Trying to open nsock-pcap, but you're passing empty or wrong device name."); + return 0; + } + + lua_pop(l, 1); // pop bpf + /* take func from top of stack and store it in the Registry */ + int hash_func_ref = luaL_ref(l, LUA_REGISTRYINDEX); + /* push function on the registry-stack */ + lua_rawgeti(l, LUA_REGISTRYINDEX, hash_func_ref); + + struct ncap_socket *ns; + + /* create key */ + char key[8192]; + snprintf(key, sizeof(key), "%s|%i|%i|%u|%s", + pcapdev, + snaplen, promisc, + (unsigned int)strlen(bpf), + bpf); + ns = ncap_socket_map_get(key); + if(ns == NULL){ + ns = (struct ncap_socket*)safe_zalloc(sizeof(struct ncap_socket)); + ns->nsiod = nsi_new(nsp, ns); + ns->key = strdup(key); + /* error messages are passed here */ + char *emsg = nsock_pcap_open(nsp, ns->nsiod, pcapdev, snaplen, promisc, bpf); + if(emsg){ + luaL_argerror(l, 1, emsg); + return 0; + } + ncap_socket_map_set(key, ns); + } + ns->references++; + udata->nsiod = ns->nsiod; + udata->ncap_socket = ns; + udata->ncap_cback_ref = hash_func_ref; + return 0; +} + +/* (LUA) Close nsock-pcap socket. + * */ +static int l_nsock_ncap_close(lua_State* l){ + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + struct ncap_socket *ns = udata->ncap_socket; + + if(!udata->nsiod || !udata->ncap_socket) { + luaL_argerror(l, 1, "Trying to close nsock-pcap, but it was never opened."); + return 0; + } + if(udata->ncap_request) { + luaL_argerror(l, 1, "Trying to close nsock-pcap, but it has active event."); + return 0; + } + + assert(ns->references > 0); + + ns->references--; + if(ns->references == 0){ + ncap_socket_map_del(ns->key); + if(ns->key) free(ns->key); + nsi_delete(ns->nsiod, NSOCK_PENDING_NOTIFY); + free(ns); + } + + udata->nsiod = NULL; + udata->ncap_socket = NULL; + lua_unref(l, udata->ncap_cback_ref); + udata->ncap_cback_ref = 0; + + lua_pushboolean(l, true); + return 1; +} + + +/* (static) binary string to hex zero-terminated string */ +char *hex(char *str, unsigned int strsz){ + static char x[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + static char buf[2048]; + unsigned int i; + unsigned char *s; + for(i=0, s=(unsigned char*)str; isuspended_lua_threads */ +std::multimap ncap_request_map; +typedef std::multimap::iterator ncap_request_map_iterator; +typedef std::pair ncap_request_map_ii; + +/* del from multimap */ +void ncap_request_map_del(struct ncap_request *nr){ + ncap_request_map_iterator i; + ncap_request_map_ii ii; + std::string s = nr->key; + ii = ncap_request_map.equal_range(s); + + for(i=ii.first ; i!=ii.second ;i++){ + if(i->second == nr){ + i->second = NULL; + ncap_request_map.erase(i); + return; + } + } + assert(0); +} + + +/* add to multimap */ +void ncap_request_map_add(char *key, struct ncap_request *nr){ + std::string skey = key; + ncap_request_map.insert(std::pair(skey, nr)); + return; +} + +/* (LUA) Register event that will wait for one packet matching hash. + * It's non-blocking method of capturing packets. + * 1) hash - hash for packet that should be matched. or empty string if you + * want to receive first packet + * */ +static int l_nsock_ncap_register(lua_State *l){ + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + size_t testdatasz; + const char* testdata = luaL_checklstring(l, 2, &testdatasz); + + struct timeval now = *nsock_gettimeofday(); + + if(!udata->nsiod || !udata->ncap_socket) { + luaL_argerror(l, 1, "You can't register to nsock-pcap if it wasn't opened."); + return 0; + } + if(udata->ncap_request){ + luaL_argerror(l, 1, "You are already registered to this socket."); + return 0; + } + + struct ncap_request *nr = + (struct ncap_request*)safe_zalloc(sizeof(struct ncap_request)); + + udata->ncap_request = nr; + + TIMEVAL_MSEC_ADD(nr->end_time, now, udata->timeout); + nr->key = strdup(hex((char*)testdata, testdatasz)); + nr->l = l; + nr->ncap_cback_ref = udata->ncap_cback_ref; + /* always create new event. */ + nr->nseid = nsock_pcap_read_packet(nsp, + udata->nsiod, + l_nsock_pcap_receive_handler, + udata->timeout, nr); + + ncap_request_map_add(nr->key, nr); + + /* that's it. return to lua */ + return 0; +} + +/* (LUA) After "register" use this function to block, and wait for packet. + * If packet is already captured, this function will return immidietly. + * + * return values: status(true/false), capture_len/error_msg, layer2data, layer3data + * */ +int l_nsock_pcap_receive(lua_State *l){ + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + if(!udata->nsiod || !udata->ncap_socket) { + luaL_argerror(l, 1, "You can't receive to nsock-pcap if it wasn't opened."); + return 0; + } + if(!udata->ncap_request){ + luaL_argerror(l, 1, "You can't it's not registered"); + return 0; + } + + /* and clear udata->ncap_request, we'll never,ever have access to current + * udata during this request */ + struct ncap_request *nr = udata->ncap_request; + udata->ncap_request = NULL; + + /* ready to receive data? don't suspend thread*/ + if(nr->received) /*data already received*/ + return ncap_restore_lua(nr); + + /* no data yet? suspend thread */ + nr->suspended = 1; + + return lua_yield(l, 0); +} + +/* (free) excute callback function from lua script */ +char* ncap_request_do_callback(nsock_event nse, lua_State *l, int ncap_cback_ref){ + const unsigned char *l2_data, *l3_data; + size_t l2_len, l3_len, packet_len; + nse_readpcap(nse, &l2_data, &l2_len, &l3_data, &l3_len, &packet_len, NULL); + + lua_rawgeti(l, LUA_REGISTRYINDEX, ncap_cback_ref); + lua_pushnumber(l, packet_len); + lua_pushlstring(l, (char*)l2_data, l2_len); + lua_pushlstring(l, (char*)l3_data, l3_len); + + lua_call(l, 3, 1); + + /* get string from top of the stack*/ + size_t testdatasz; + const char* testdata = lua_tolstring(l, -1, &testdatasz); + // lua_pop(l, 1);/* just in case [nope, it's not needed]*/ + + char *key = strdup(hex((char*)testdata, testdatasz)); + return key; +} + + + +/* callback from nsock */ +void l_nsock_pcap_receive_handler(nsock_pool nsp, nsock_event nse, void *userdata){ + int this_event_restored=0; + struct ncap_request *nr = (struct ncap_request *) userdata; + + + switch(nse_status(nse)) { + case NSE_STATUS_SUCCESS:{ + char *key = ncap_request_do_callback(nse, nr->l, nr->ncap_cback_ref); + + /* processes threads that receive every packet */ + this_event_restored += ncap_request_set_results(nse, ""); + + /* process everything that matches test */ + this_event_restored += ncap_request_set_results(nse, key); + free(key); + + + if(!this_event_restored){ + /* okay, we received event but it wasn't handled by the process + * that requested this event. We must query for new event with + * smaller timeout */ + struct timeval now = *nsock_gettimeofday(); + + /*event was successfull so I assert it occured before pr->end_time*/ + int timeout = TIMEVAL_MSEC_SUBTRACT(nr->end_time, now); + if(timeout < 0) /* funny to receive event that should be timeouted in the past. But on windows it can happen*/ + timeout = 0; + nr->nseid = nsock_pcap_read_packet(nsp, + nse_iod(nse), + l_nsock_pcap_receive_handler, + timeout, nr); + /* no need to cancel or delete current nse :) */ + } + return; + } + default: + /* event timeouted */ + ncap_request_map_del(nr); /* delete from map */ + ncap_request_set_result(nse, nr); + if(nr->suspended) /* restore thread */ + ncap_restore_lua(nr); + return; + } +} + + +/* get data from nsock_event, and set result on ncap_requests which mach key */ +int ncap_request_set_results(nsock_event nse, char *key) { + int this_event_restored = 0; + + std::string skey = key; + + ncap_request_map_iterator i; + ncap_request_map_ii ii; + + ii = ncap_request_map.equal_range(skey); + for(i = ii.first; i != ii.second; i++) { + /* tests are successfull, so just restore process */ + ncap_request *nr = i->second; + if(nr->nseid == nse_id(nse)) + this_event_restored = 1; + + ncap_request_set_result(nse, nr); + if(nr->suspended) + ncap_restore_lua(nr); + } + ncap_request_map.erase(ii.first, ii.second); + + return this_event_restored; +} + +/* get data from nsock_event, and set result ncap_request */ +void ncap_request_set_result(nsock_event nse, struct ncap_request *nr) { + enum nse_status status = nse_status(nse); + nr->received = true; + + switch (status) { + case NSE_STATUS_SUCCESS:{ + nr->r_success = true; + + const unsigned char *l2_data, *l3_data; + size_t l2_len, l3_len, packet_len; + nse_readpcap(nse, &l2_data, &l2_len, &l3_data, &l3_len, + &packet_len, NULL); + char *packet = (char*) malloc(l2_len + l3_len); + nr->r_layer2 = (unsigned char*)memcpy(&packet[0], l2_data, l2_len); + nr->r_layer3 = (unsigned char*)memcpy(&packet[l2_len], l3_data, l3_len); + nr->r_layer2_len = l2_len; + nr->r_layer3_len = l3_len; + nr->packetsz = packet_len; + break;} + case NSE_STATUS_ERROR: + case NSE_STATUS_TIMEOUT: + case NSE_STATUS_CANCELLED: + case NSE_STATUS_KILL: + case NSE_STATUS_EOF: + nr->r_success = false; + nr->r_status = strdup( nse_status2str(status) ); + break; + case NSE_STATUS_NONE: + default: + fatal("%s: In: %s:%i This should never happen.", + NSOCK_WRAPPER, __FILE__, __LINE__); + } + + if(nr->nseid != nse_id(nse)){ /* different event, cancel*/ + nsock_event_cancel(nsp, nr->nseid, 0); /* Don't send CANCELED event, just cancel */ + nr->nseid = 0; + }else{ /* this event -> do nothing */ + } + + return; +} + + +/* if lua thread was suspended, restore it. If it wasn't, just return results + * (push them on the stack and return) */ +int ncap_restore_lua(ncap_request *nr){ + lua_State *l = nr->l; + + if(nr->r_success){ + lua_pushboolean(l, true); + lua_pushnumber(l, nr->packetsz); + lua_pushlstring(l, (char*)nr->r_layer2, nr->r_layer2_len); + lua_pushlstring(l, (char*)nr->r_layer3, nr->r_layer3_len); + }else{ + lua_pushnil(l); + lua_pushstring(l, nr->r_status); + lua_pushnil(l); + lua_pushnil(l); + } + bool suspended = nr->suspended; + nr->l = NULL; + nr->ncap_cback_ref = 0; /* this ref is freed in different place (on udata->ncap_cback_ref) */ + if(nr->key) free(nr->key); + if(nr->r_status) free(nr->r_status); + if(nr->r_layer2) free(nr->r_layer2); + /* dont' free r_layer3, it's in the same block as r_layer2*/ + + free(nr); + + if(suspended) /* lua process is suspended */ + return process_waiting2running(l, 4); + else /* not suspended, just pass output */ + return 4; +} + + + + +/****************** DNET ******************************************************/ +static int l_dnet_open_ethernet(lua_State* l); +static int l_dnet_close_ethernet(lua_State* l); +static int l_dnet_send_ethernet(lua_State* l); + +static luaL_reg l_dnet [] = { + {"ethernet_open", l_dnet_open_ethernet}, + {"ethernet_close", l_dnet_close_ethernet}, + {"ethernet_send", l_dnet_send_ethernet}, + {NULL, NULL} +}; + +int l_dnet_open(lua_State* l) { + auxiliar_newclass(l, "dnet", l_dnet); + return NSOCK_WRAPPER_SUCCESS; +} + +struct l_dnet_udata { + char *interface; + eth_t *eth; +}; + +int l_dnet_new(lua_State* l) { + struct l_dnet_udata* udata; + udata = (struct l_dnet_udata*) lua_newuserdata(l, sizeof(struct l_dnet_udata)); + auxiliar_setclass(l, "dnet", -1); + udata->interface= NULL; + udata->eth = NULL; + + return 1; +} + +int l_dnet_get_interface_link(lua_State* l) { + const char* interface_name = luaL_checkstring(l, 1); + + struct interface_info *ii = getInterfaceByName((char*)interface_name); + if(!ii){ + lua_pushnil(l); + return 1; + } + char *s= NULL; + switch(ii->device_type){ + case devt_ethernet: + s = "ethernet"; + break; + case devt_loopback: + s = "loopback"; + break; + case devt_p2p: + s = "p2p"; + break; + case devt_other: + default: + s = NULL; + break; + } + if(s) + lua_pushstring(l, s); + else + lua_pushnil(l); + + return 1; +} + +typedef struct{ + int references; + eth_t *eth; +} dnet_eth_map; + + +std::map dnet_eth_cache; + +eth_t *ldnet_eth_open_cached(const char *device) { + assert(device && *device); + + std::string key = device; + dnet_eth_map *dem = dnet_eth_cache[key]; + if(dem != NULL){ + dem->references++; + return dem->eth; + } + + dem = (dnet_eth_map *)safe_zalloc(sizeof(dnet_eth_map)); + dem->eth = eth_open(device); + if(!dem->eth) + fatal("Unable to open dnet on ethernet interface %s",device); + dem->references = 1; + dnet_eth_cache[key] = dem; + return dem->eth; +} + +/* See the description for eth_open_cached */ +void ldnet_eth_close_cached(const char *device) { + std::string key = device; + dnet_eth_map *dem = dnet_eth_cache[key]; + assert(dem); + dem->references--; + if(dem->references==0){ + dnet_eth_cache.erase(key); + eth_close(dem->eth); + free(dem); + } + return; +} + +static int l_dnet_open_ethernet(lua_State* l){ + l_dnet_udata* udata = (l_dnet_udata*) auxiliar_checkclass(l, "dnet", 1); + const char* interface_name = luaL_checkstring(l, 2); + + struct interface_info *ii = getInterfaceByName((char*)interface_name); + if(!ii || ii->device_type!=devt_ethernet){ + luaL_argerror(l, 2, "device is not valid ethernet interface"); + return 0; + } + udata->interface= strdup(interface_name); + udata->eth = ldnet_eth_open_cached(interface_name); + + return 0; +} + +static int l_dnet_close_ethernet(lua_State* l){ + l_dnet_udata* udata = (l_dnet_udata*) auxiliar_checkclass(l, "dnet", 1); + if(!udata->interface || !udata->eth){ + luaL_argerror(l, 1, "dnet is not valid opened ethernet interface"); + return 0; + } + + udata->eth = NULL; + ldnet_eth_close_cached(udata->interface); + free(udata->interface); + udata->interface = NULL; + return 0; +} + +static int l_dnet_send_ethernet(lua_State* l){ + l_dnet_udata* udata = (l_dnet_udata*) auxiliar_checkclass(l, "dnet", 1); + size_t packetsz = 0; + const char* packet = luaL_checklstring(l, 2, &packetsz); + + if(!udata->interface || !udata->eth){ + luaL_argerror(l, 1, "dnet is not valid opened ethernet interface"); + return 0; + } + eth_send(udata->eth, packet, packetsz); + return 0; +} + +int l_clock_ms(lua_State* l){ + struct timeval tv; + gettimeofday(&tv, NULL); + // no rounding error + // unless the number is greater than 100,000,000,000,000 + double usec = 0.0; //MAX_INT*1000 = 4 294 967 296 000 <- miliseconds since epoch should fit + usec = tv.tv_sec*1000; + usec += (int)(tv.tv_usec/1000); // make sure it's integer. + + lua_pushnumber(l, usec); + return 1; +} + + + + + diff -NraupbwB soc07-5184/nse_nsock.cc.orig soc07-nse-pcap/nse_nsock.cc.orig --- soc07-5184/nse_nsock.cc.orig 1970-01-01 01:00:00.000000000 +0100 +++ soc07-nse-pcap/nse_nsock.cc.orig 2007-07-10 18:15:14.000000000 +0200 @@ -0,0 +1,438 @@ +#include "nse_nsock.h" +#include "nse_auxiliar.h" +#include "nse_macros.h" +#include "nse_string.h" + +#include "nsock.h" +#include "nmap_error.h" +#include "NmapOps.h" + +#include +#include +#include + +#if HAVE_OPENSSL +#include +#endif + +#define SCRIPT_ENGINE "SCRIPT ENGINE" +#define NSOCK_WRAPPER "NSOCK WRAPPER" +#define NSOCK_WRAPPER_SUCCESS 0 +#define NSOCK_WRAPPER_ERROR 2 + +#define FROM 1 +#define TO 2 + +#define DEFAULT_TIMEOUT 30000 + +extern NmapOps o; + +// defined in nse_main.cc but also declared here +// to keep the .h files clean +int process_waiting2running(lua_State* l, int resume_arguments); + +static int l_nsock_connect(lua_State* l); +static int l_nsock_send(lua_State* l); +static int l_nsock_receive(lua_State* l); +static int l_nsock_receive_lines(lua_State* l); +static int l_nsock_receive_bytes(lua_State* l); +static int l_nsock_get_info(lua_State* l); +static int l_nsock_gc(lua_State* l); +static int l_nsock_close(lua_State* l); +static int l_nsock_set_timeout(lua_State* l); + +void l_nsock_connect_handler(nsock_pool nsp, nsock_event nse, void *lua_state); +void l_nsock_send_handler(nsock_pool nsp, nsock_event nse, void *lua_state); +void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state); + +int l_nsock_checkstatus(lua_State* l, nsock_event nse); + +void l_nsock_trace(nsock_iod nsiod, char* message, int direction); +char* inet_ntop_both(int af, const void* v_addr, char* ipstring); +unsigned short inet_port_both(int af, const void* v_addr); + +static luaL_reg l_nsock [] = { + {"connect", l_nsock_connect}, + {"send", l_nsock_send}, + {"receive", l_nsock_receive}, + {"receive_lines", l_nsock_receive_lines}, + {"receive_bytes", l_nsock_receive_bytes}, + {"get_info", l_nsock_get_info}, + {"close", l_nsock_close}, + {"set_timeout", l_nsock_set_timeout}, + {"__gc",l_nsock_gc}, + {NULL, NULL} +}; + +static nsock_pool nsp; + +struct l_nsock_udata { + int timeout; + nsock_iod nsiod; + void *ssl_session; +}; + +int l_nsock_open(lua_State* l) { + auxiliar_newclass(l, "nsock", l_nsock); + + nsp = nsp_new(NULL); + + if (o.scriptTrace()) + nsp_settrace(nsp, 5, o.getStartTime()); + + return NSOCK_WRAPPER_SUCCESS; +} + +int l_nsock_new(lua_State* l) { + struct l_nsock_udata* udata; + udata = (struct l_nsock_udata*) lua_newuserdata(l, sizeof(struct l_nsock_udata)); + auxiliar_setclass(l, "nsock", -1); + udata->nsiod = NULL; + udata->ssl_session = NULL; + udata->timeout = DEFAULT_TIMEOUT; + return 1; +} + +int l_nsock_loop(int tout) { + return nsock_loop(nsp, tout); +} + +int l_nsock_checkstatus(lua_State* l, nsock_event nse) { + enum nse_status status = nse_status(nse); + + switch (status) { + case NSE_STATUS_SUCCESS: + lua_pushboolean(l, true); + return NSOCK_WRAPPER_SUCCESS; + break; + case NSE_STATUS_ERROR: + case NSE_STATUS_TIMEOUT: + case NSE_STATUS_CANCELLED: + case NSE_STATUS_KILL: + case NSE_STATUS_EOF: + lua_pushnil(l); + lua_pushstring(l, nse_status2str(status)); + return NSOCK_WRAPPER_ERROR; + break; + case NSE_STATUS_NONE: + default: + fatal("%s: In: %s:%i This should never happen.", + NSOCK_WRAPPER, __FILE__, __LINE__); + break; + + } + + return -1; +} + +static int l_nsock_connect(lua_State* l) { + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + const char* addr = luaL_checkstring(l, 2); + unsigned short port = (unsigned short) luaL_checkint(l, 3); + const char *how = luaL_optstring(l, 4, "tcp"); + + const char* error; + struct addrinfo *dest; + int error_id; + + + error_id = getaddrinfo(addr, NULL, NULL, &dest); + if (error_id) { + error = gai_strerror(error_id); + lua_pushboolean(l, false); + lua_pushstring(l, error); + return 2; + } + + udata->nsiod = nsi_new(nsp, NULL); + + switch (how[0]) { + case 't': + if (strcmp(how, "tcp")) goto error; + nsock_connect_tcp(nsp, udata->nsiod, l_nsock_connect_handler, + udata->timeout, l, dest->ai_addr, dest->ai_addrlen, port); + break; + case 'u': + if (strcmp(how, "udp")) goto error; + nsock_connect_udp(nsp, udata->nsiod, l_nsock_connect_handler, + l, dest->ai_addr, dest->ai_addrlen, port); + break; + case 's': + if (strcmp(how, "ssl")) goto error; +#ifdef HAVE_OPENSSL + nsock_connect_ssl(nsp, udata->nsiod, l_nsock_connect_handler, + udata->timeout, l, dest->ai_addr, dest->ai_addrlen, port, + udata->ssl_session); + break; +#else + luaL_argerror(l, 4, "Sorry, you don't have openssl."); + return 0; +#endif + default: + goto error; + break; + } + + freeaddrinfo(dest); + return lua_yield(l, 0); + +error: + freeaddrinfo(dest); + luaL_argerror(l, 4, "invalid connection method"); + return 0; +} + +void l_nsock_connect_handler(nsock_pool nsp, nsock_event nse, void *lua_state) { + lua_State* l = (lua_State*) lua_state; + + if(o.scriptTrace()) { + l_nsock_trace(nse_iod(nse), "CONNECT", TO); + } + + if(l_nsock_checkstatus(l, nse) == NSOCK_WRAPPER_SUCCESS) { + process_waiting2running((lua_State*) lua_state, 1); + } else { + process_waiting2running((lua_State*) lua_state, 2); + } +} + +static int l_nsock_send(lua_State* l) { + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + const char* string = luaL_checkstring(l, 2); + size_t string_len = lua_objlen (l, 2); + char* hexified; + + if(udata->nsiod == NULL) { + lua_pushboolean(l, false); + lua_pushstring(l, "Trying to send through a closed socket\n"); + return 2; + } + + if(o.scriptTrace()) { + hexified = nse_hexify((const void*)string, string_len); + l_nsock_trace(udata->nsiod, hexified, TO); + free(hexified); + } + + nsock_write(nsp, udata->nsiod, l_nsock_send_handler, udata->timeout, l, string, string_len); + return lua_yield(l, 0); +} + +void l_nsock_send_handler(nsock_pool nsp, nsock_event nse, void *lua_state) { + lua_State* l = (lua_State*) lua_state; + + if(l_nsock_checkstatus(l, nse) == NSOCK_WRAPPER_SUCCESS) { + process_waiting2running((lua_State*) lua_state, 1); + } else { + process_waiting2running((lua_State*) lua_state, 2); + } +} + +static int l_nsock_receive(lua_State* l) { + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + + if(udata->nsiod == NULL) { + lua_pushboolean(l, false); + lua_pushstring(l, "Trying to receive through a closed socket\n"); + return 2; + } + + nsock_read(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, l); + + return lua_yield(l, 0); +} + +static int l_nsock_receive_lines(lua_State* l) { + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + int nlines = (int) luaL_checknumber(l, 2); + + if(udata->nsiod == NULL) { + lua_pushboolean(l, false); + lua_pushstring(l, "Trying to receive lines through a closed socket\n"); + return 2; + } + + nsock_readlines(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, l, nlines); + + return lua_yield(l, 0); +} + +static int l_nsock_receive_bytes(lua_State* l) { + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + int nbytes = (int) luaL_checknumber(l, 2); + + if(udata->nsiod == NULL) { + lua_pushboolean(l, false); + lua_pushstring(l, "Trying to receive bytes through a closed socket\n"); + return 2; + } + + nsock_readbytes(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, l, nbytes); + + return lua_yield(l, 0); +} + +void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state) { + lua_State* l = (lua_State*) lua_state; + char* rcvd_string; + int rcvd_len = 0; + char* hexified; + + if(l_nsock_checkstatus(l, nse) == NSOCK_WRAPPER_SUCCESS) { + rcvd_string = nse_readbuf(nse, &rcvd_len); + + if(o.scriptTrace()) { + hexified = nse_hexify((const void*) rcvd_string, (size_t) rcvd_len); + l_nsock_trace(nse_iod(nse), hexified, FROM); + free(hexified); + } + + lua_pushlstring(l, rcvd_string, rcvd_len); + process_waiting2running((lua_State*) lua_state, 2); + } else { + process_waiting2running((lua_State*) lua_state, 2); + } +} + +void l_nsock_trace(nsock_iod nsiod, char* message, int direction) { + int status; + int protocol; + int af; + struct sockaddr local; + struct sockaddr remote; + char* ipstring_local = (char*) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN); + char* ipstring_remote = (char*) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN); + + status = nsi_getlastcommunicationinfo(nsiod, &protocol, &af, + &local, &remote, sizeof(sockaddr)); + + log_write(LOG_STDOUT, "SCRIPT ENGINE: %s %s:%d %s %s:%d | %s\n", + (protocol == IPPROTO_TCP)? "TCP" : "UDP", + inet_ntop_both(af, &local, ipstring_local), + inet_port_both(af, &local), + (direction == TO)? ">" : "<", + inet_ntop_both(af, &remote, ipstring_remote), + inet_port_both(af, &remote), + message); + + free(ipstring_local); + free(ipstring_remote); +} + +char* inet_ntop_both(int af, const void* v_addr, char* ipstring) { +// char* ipstring = (char*) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN); + + if(af == AF_INET) { + inet_ntop(AF_INET, &((struct sockaddr_in*) v_addr)->sin_addr, + ipstring, INET6_ADDRSTRLEN); + + return ipstring; + } +#ifdef HAVE_IPV6 + else if(af == AF_INET6) { + inet_ntop(AF_INET6, &((struct sockaddr_in6*) v_addr)->sin6_addr, + ipstring, INET6_ADDRSTRLEN); + return ipstring; + } +#endif + else { + return "unknown protocol"; + } + +} + +unsigned short inet_port_both(int af, const void* v_addr) { + int port; + if(af == AF_INET) { + port = ((struct sockaddr_in*) v_addr)->sin_port; + } +#ifdef HAVE_IPV6 + else if(af == AF_INET6) { + port = ((struct sockaddr_in6*) v_addr)->sin6_port; + } +#endif + else { + port = 0; + } + + return ntohs(port); +} + +static int l_nsock_get_info(lua_State* l) { + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + int status; + + int protocol; // tcp or udp + int af; // address family + struct sockaddr local; + struct sockaddr remote; + char* ipstring_local = (char*) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN); + char* ipstring_remote = (char*) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN); + + if(udata->nsiod == NULL) { + lua_pushboolean(l, false); + lua_pushstring(l, "Trying to get info from a closed socket\n"); + return 2; + } + + status = nsi_getlastcommunicationinfo(udata->nsiod, &protocol, &af, + &local, &remote, sizeof(sockaddr)); + + lua_pushboolean(l, true); + + lua_pushstring(l, inet_ntop_both(af, &local, ipstring_local)); + lua_pushnumber(l, inet_port_both(af, &local)); + + lua_pushstring(l, inet_ntop_both(af, &remote, ipstring_remote)); + lua_pushnumber(l, inet_port_both(af, &remote)); + + free(ipstring_local); + free(ipstring_remote); + return 5; +} +static int l_nsock_gc(lua_State* l){ + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + if(udata->nsiod == NULL) { //socket obviously got closed already - so no finalization needed + return 0; + }else{ + //FIXME - check wheter close returned true!! + l_nsock_close(l); + } + return 0; +} +static int l_nsock_close(lua_State* l) { + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + + if(udata->nsiod == NULL) { + lua_pushboolean(l, false); + lua_pushstring(l, "Trying to close a closed socket\n"); + return 2; + } + + if(o.scriptTrace()) { + l_nsock_trace(udata->nsiod, "CLOSE", TO); + } + +#ifdef HAVE_OPENSSL + if (udata->ssl_session) + SSL_SESSION_free((SSL_SESSION*)udata->ssl_session); + udata->ssl_session=NULL; +#endif + + nsi_delete(udata->nsiod, NSOCK_PENDING_NOTIFY); + + udata->nsiod = NULL; + + lua_pushboolean(l, true); + return 1; +} + +static int l_nsock_set_timeout(lua_State* l) { + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + int timeout = (unsigned short) luaL_checkint(l, 2); + + udata->timeout = timeout; + + return 0; +} + diff -NraupbwB soc07-5184/nse_nsock.h soc07-nse-pcap/nse_nsock.h --- soc07-5184/nse_nsock.h 2007-07-10 18:15:14.000000000 +0200 +++ soc07-nse-pcap/nse_nsock.h 2007-07-11 23:06:41.000000000 +0200 @@ -11,5 +11,9 @@ int l_nsock_open(lua_State* l); int l_nsock_new(lua_State* l); int l_nsock_loop(int tout); +int l_dnet_new(lua_State* l); +int l_dnet_open(lua_State* l); +int l_dnet_get_interface_link(lua_State* l); + #endif diff -NraupbwB soc07-5184/nsock/examples/Makefile soc07-nse-pcap/nsock/examples/Makefile --- soc07-5184/nsock/examples/Makefile 2007-07-10 18:15:24.000000000 +0200 +++ soc07-nse-pcap/nsock/examples/Makefile 2007-07-11 23:06:41.000000000 +0200 @@ -8,6 +8,8 @@ OPENSSLLIB=-lssl -lcrypt INCLS = -I../include -I$(NBASEDIR) CFLAGS = -I/usr/local/include -Wall -g $(CCOPT) $(DEFS) $(INCLS) LDFLAGS = +PCAPBASEDIR=../../libpcap +PCAPLIB=$(PCAPBASEDIR)/libpcap.a RM = rm -f TARGETS = nsock_telnet nsock_test_timers @@ -20,5 +22,8 @@ nsock_telnet: nsock_telnet.o $(NSOCKLIB) nsock_test_timers: nsock_test_timers.o $(NSOCKLIB) $(CC) -o $@ $(CFLAGS) nsock_test_timers.o $(NSOCKLIB) $(NBASELIB) $(OPENSSLLIB) +nsock_pcap: nsock_pcap.o $(NSOCKLIB) $(PCAPLIB) + $(CC) -o $@ $(CFLAGS) nsock_pcap.o $(NSOCKLIB) $(NBASELIB) $(OPENSSLLIB) $(PCAPLIB) + clean: $(RM) *.o $(TARGETS) diff -NraupbwB soc07-5184/nsock/examples/nsock_pcap.c soc07-nse-pcap/nsock/examples/nsock_pcap.c --- soc07-5184/nsock/examples/nsock_pcap.c 1970-01-01 01:00:00.000000000 +0100 +++ soc07-nse-pcap/nsock/examples/nsock_pcap.c 2007-07-11 23:06:41.000000000 +0200 @@ -0,0 +1,94 @@ +#include "nsock.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *hex(char *str, unsigned int strsz){ + static char x[] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + static char buf[2048]; + unsigned int i; + unsigned char *s; + for(i=0, s=(unsigned char*)str; i + + @@ -239,6 +243,10 @@ > + + diff -NraupbwB soc07-5184/nsock/src/configure.ac soc07-nse-pcap/nsock/src/configure.ac --- soc07-5184/nsock/src/configure.ac 2007-07-10 18:15:24.000000000 +0200 +++ soc07-nse-pcap/nsock/src/configure.ac 2007-07-11 23:06:41.000000000 +0200 @@ -4,6 +4,88 @@ AC_PREREQ(2.13) dnl Process this file with autoconf to produce a configure script. AC_INIT(nsock_core.c) +dnl use nsock_config.h instad of -D macros +AC_CONFIG_HEADER(nsock_config.h) + +dnl Host specific hacks +AC_CANONICAL_HOST + +linux=no +macosx=no +needs_cpp_precomp=no + +case "$host" in + *alpha-dec-osf*) + AC_DEFINE(DEC) + ;; + *-netbsd* | *-knetbsd*-gnu) + AC_DEFINE(NETBSD) + ;; + *-openbsd*) + AC_DEFINE(OPENBSD) + ;; + *-sgi-irix5*) + AC_DEFINE(IRIX) + ;; + *-sgi-irix6*) + AC_DEFINE(IRIX) + ;; + *-hpux*) + AC_DEFINE(HPUX) + ;; + *-solaris2.0*) + AC_DEFINE(STUPID_SOLARIS_CHECKSUM_BUG) + AC_DEFINE(SOLARIS) + ;; + *-solaris2.[[1-9]][[0-9]]*) + AC_DEFINE(SOLARIS) + ;; + *-solaris2.1*) + AC_DEFINE(STUPID_SOLARIS_CHECKSUM_BUG) + AC_DEFINE(SOLARIS) + ;; + *-solaris2.2*) + AC_DEFINE(STUPID_SOLARIS_CHECKSUM_BUG) + AC_DEFINE(SOLARIS) + ;; + *-solaris2.3*) + AC_DEFINE(STUPID_SOLARIS_CHECKSUM_BUG) + AC_DEFINE(SOLARIS) + ;; + *-solaris2.4*) + AC_DEFINE(STUPID_SOLARIS_CHECKSUM_BUG) + AC_DEFINE(SOLARIS) + ;; + *-solaris2.5.1) + AC_DEFINE(STUPID_SOLARIS_CHECKSUM_BUG) + AC_DEFINE(SOLARIS) + ;; + *-solaris*) + AC_DEFINE(SOLARIS) + ;; + *-sunos4*) + AC_DEFINE(SUNOS) + AC_DEFINE(SPRINTF_RETURNS_STRING) + ;; + *-linux*) + linux=yes + AC_DEFINE(LINUX) + AC_DEFINE(PCAP_TIMEOUT_IGNORED) # libpcap doesn't even LOOK at + # the timeout you give it under Linux + ;; + *-freebsd* | *-kfreebsd*-gnu | *-dragonfly*) + AC_DEFINE(FREEBSD) + ;; + *-bsdi*) + AC_DEFINE(BSDI) + ;; + *-apple-darwin*) + macosx=yes + AC_DEFINE(MACOSX) + ;; +esac + + AC_ARG_WITH(localdirs, [ --with-localdirs Explicitly ask compiler to use /usr/local/{include,libs} if they exist ], [ case "$with_localdirs" in @@ -26,9 +108,6 @@ if test "$user_localdirs" = 1; then fi fi -dnl use nsock_config.h instad of -D macros -AC_CONFIG_HEADER(nsock_config.h) - dnl Checks for programs. AC_PROG_CC if test -n "$GCC"; then @@ -41,9 +120,6 @@ dnl AC_PATH_PROG(MAKEDEPEND, makedepend) AC_SUBST(COMPAT_OBJS) AC_SUBST(COMPAT_SRCS) -dnl Host specific hacks -AC_CANONICAL_HOST - dnl Checks for libraries. dnl AC_CHECK_LIB(m, pow) diff -NraupbwB soc07-5184/nsock/src/error.c soc07-nse-pcap/nsock/src/error.c --- soc07-5184/nsock/src/error.c 2007-07-10 18:15:24.000000000 +0200 +++ soc07-nse-pcap/nsock/src/error.c 2007-07-11 23:06:41.000000000 +0200 @@ -64,15 +64,6 @@ fflush(stdout);vfprintf(stderr, fmt, ap) fprintf(stderr, "\n");va_end(ap); exit(1);} -void myerror(char *fmt, ...) { -va_list ap;va_start(ap, fmt); -fflush(stdout); -vfprintf(stderr, fmt, ap); -fprintf(stderr, "\n"); -va_end(ap); -return; -} - void pfatal(char *fmt, ...) { va_list ap;va_start(ap, fmt); fflush(stdout); diff -NraupbwB soc07-5184/nsock/src/Makefile.in soc07-nse-pcap/nsock/src/Makefile.in --- soc07-5184/nsock/src/Makefile.in 2007-07-10 18:15:24.000000000 +0200 +++ soc07-nse-pcap/nsock/src/Makefile.in 2007-07-11 23:06:41.000000000 +0200 @@ -1,17 +1,18 @@ -NSOCK_VERSION = 0.01 +NSOCK_VERSION = 0.02 prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sbindir = @sbindir@ mandir = @mandir@ srcdir = @srcdir@ +datarootdir = @datarootdir@ CC = @CC@ AR = ar RANLIB = @RANLIB@ CCOPT = DEFS = @DEFS@ -DNSOCK_VERSION=\"$(NSOCK_VERSION)\" -INCLS = -I../include +INCLS = -I../include -I../../libpcap CFLAGS = @CFLAGS@ $(CCOPT) $(DEFS) $(INCLS) # CFLAGS = -g -Wall $(DEFS) $(INCLS) CPPFLAGS = @CPPFLAGS@ @@ -28,11 +29,11 @@ NBASEDIR=@NBASEDIR@ TARGET = libnsock.a -SRCS = error.c filespace.c gh_list.c nsock_connect.c nsock_core.c nsock_iod.c nsock_read.c nsock_timers.c nsock_write.c nsock_ssl.c nsock_utils.c nsock_event.c nsock_pool.c netutils.c @COMPAT_SRCS@ +SRCS = error.c filespace.c gh_list.c nsock_connect.c nsock_core.c nsock_iod.c nsock_read.c nsock_timers.c nsock_write.c nsock_ssl.c nsock_utils.c nsock_event.c nsock_pool.c netutils.c nsock_pcap.c @COMPAT_SRCS@ -OBJS = error.o filespace.o gh_list.o nsock_connect.o nsock_core.o nsock_iod.o nsock_read.o nsock_timers.o nsock_write.o nsock_ssl.o nsock_utils.o nsock_event.o nsock_pool.o netutils.o @COMPAT_OBJS@ +OBJS = error.o filespace.o gh_list.o nsock_connect.o nsock_core.o nsock_iod.o nsock_read.o nsock_timers.o nsock_write.o nsock_ssl.o nsock_utils.o nsock_event.o nsock_pool.o netutils.o nsock_pcap.o @COMPAT_OBJS@ -DEPS = error.h filespace.h gh_list.h nsock_internal.h nsock_utils.h netutils.h ../include/nsock.h $(NBASEDIR)/libnbase.a +DEPS = error.h filespace.h gh_list.h nsock_internal.h nsock_utils.h netutils.h nsock_pcap.h ../include/nsock.h $(NBASEDIR)/libnbase.a .c.o: $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@ diff -NraupbwB soc07-5184/nsock/src/nsock_config.h.in soc07-nse-pcap/nsock/src/nsock_config.h.in --- soc07-5184/nsock/src/nsock_config.h.in 2007-07-10 18:15:24.000000000 +0200 +++ soc07-nse-pcap/nsock/src/nsock_config.h.in 2007-07-11 23:06:41.000000000 +0200 @@ -63,3 +63,14 @@ #undef SPRINTF_RETURNS_STRING +#undef DEC +#undef LINUX +#undef FREEBSD +#undef OPENBSD +#undef SOLARIS +#undef SUNOS +#undef BSDI +#undef IRIX +#undef HPUX +#undef NETBSD +#undef MACOSX diff -NraupbwB soc07-5184/nsock/src/nsock_core.c soc07-nse-pcap/nsock/src/nsock_core.c --- soc07-5184/nsock/src/nsock_core.c 2007-07-10 18:15:24.000000000 +0200 +++ soc07-nse-pcap/nsock/src/nsock_core.c 2007-07-11 23:06:41.000000000 +0200 @@ -81,6 +81,11 @@ #include "netutils.h" +#if HAVE_PCAP +#include "nsock_pcap.h" +static int pcap_read_on_nonselect(mspool *nsp); +#endif + /* Msock time of day -- we update this at least once per nsock_loop round (and after most calls that are likely to block). Other nsock files should grab this @@ -105,6 +110,9 @@ static int wait_for_events(mspool *ms, i return 0; /* No need to wait on 0 events ... */ do { + if (ms->tracelevel > 3) + nsock_trace(ms, "wait_for_events"); + if (ms->evl.next_ev.tv_sec == 0) { event_msecs = -1; /* None of the events specified a timeout */ } else { @@ -131,6 +139,7 @@ static int wait_for_events(mspool *ms, i select_tv_p = NULL; } + /* Figure out whether there are any FDs in the sets, as @$@!$# Windows returns WSAINVAL (10022) if you call a select() with no FDs, even though the Linux man page says that doing so is a @@ -144,6 +153,13 @@ static int wait_for_events(mspool *ms, i else ms->mioi.max_sd--; } +#if HAVE_PCAP + /* do non-blocking read on pcap devices that doesn't support select() + * If there is anything read, don't do usleep() or select(), just leave this loop */ + if (pcap_read_on_nonselect(ms)) { + /* okay, something was read. */ + } else +#endif if (ms->mioi.max_sd < 0) { ms->mioi.results_left = 0; if (combined_msecs > 0) @@ -161,7 +177,7 @@ static int wait_for_events(mspool *ms, i } gettimeofday(&nsock_tod, NULL); /* Due to usleep or select delay */ - } while (ms->mioi.results_left == -1 && sock_err == EINTR); + } while (ms->mioi.results_left == -1 && sock_err == EINTR);// repeat only if signal occured if (ms->mioi.results_left == -1 && sock_err != EINTR) { ms->errnum = sock_err; @@ -650,6 +666,77 @@ void handle_read_result(mspool *ms, msev return; } +#if HAVE_PCAP +void handle_pcap_read_result(mspool *ms, msevent *nse, + enum nse_status status) +{ + msiod *iod = nse->iod; + mspcap *mp = (mspcap *) iod->pcap; + + + if (status == NSE_STATUS_TIMEOUT) { + nse->status = NSE_STATUS_TIMEOUT; + nse->event_done = 1; + } else if (status == NSE_STATUS_CANCELLED) { + nse->status = NSE_STATUS_CANCELLED; + nse->event_done = 1; + } else if (status == NSE_STATUS_SUCCESS) { + /* check if we already have something read */ + if(FILESPACE_LENGTH(&(nse->iobuf)) == 0){ + nse->status = NSE_STATUS_TIMEOUT; + nse->event_done = 0; + }else{ + nse->status = NSE_STATUS_SUCCESS;/* we have full buffer */ + nse->event_done = 1; + } + } else { + assert(0); /* Currently we only know about TIMEOUT, CANCELLED, and SUCCESS callbacks */ + } + + /* If we asked for an event dispatch, we are done reading on the socket so + we can take it off the descriptor list ... */ + if (nse->event_done && mp->pcap_desc >= 0) { + FD_CLR(mp->pcap_desc, &ms->mioi.fds_master_r); + FD_CLR(mp->pcap_desc, &ms->mioi.fds_results_r); + } + return; +} + +/* + * returns number of descriptors on which data was read + * */ +static int pcap_read_on_nonselect(mspool *nsp) +{ + gh_list *event_list = &nsp->evl.pcap_read_events; + gh_list_elem *current, *next; + msevent *nse; + int rc; + int ret = 0; + + if (nsp->tracelevel > 3) + nsock_trace(nsp, "PCAP read_on_nonselect"); + + for(current = GH_LIST_FIRST_ELEM(event_list); + current != NULL; current = next) { + + nse = (msevent *) GH_LIST_ELEM_DATA(current); + rc = do_actual_pcap_read(nse); + if(rc==1){ /* something received */ + ret++; + break; + } + next = GH_LIST_ELEM_NEXT(current); + } + if (nsp->tracelevel > 3) + nsock_trace(nsp, "PCAP END read_on_nonselect"); + + return(ret); +} +#endif // HAVE_PCAP + + + + /* Iterate through all the event lists (such as connect_events, read_events, timer_events, etc) and take action for those that have completed (due to timeout, i/o, etc) */ @@ -665,6 +752,9 @@ static void iterate_through_event_lists( &nsp->evl.read_events, &nsp->evl.write_events, &nsp->evl.timer_events, + #if HAVE_PCAP + &nsp->evl.pcap_read_events, + #endif 0 }; int current_list_idx; @@ -682,9 +772,20 @@ static void iterate_through_event_lists( */ /* foreach list */ + if (nsp->tracelevel > 7){ + for(current_list_idx = 0; event_lists[current_list_idx] != NULL; + current_list_idx++) { + nsock_trace(nsp, "before iterating, list %i", current_list_idx); + for(current = GH_LIST_FIRST_ELEM(event_lists[current_list_idx]); + current != NULL; current = GH_LIST_ELEM_NEXT(current)) { + nse = (msevent *) GH_LIST_ELEM_DATA(current); + nsock_trace(nsp, "before iterating %lu",nse->id); + } + } + } + /* foreach list */ for(current_list_idx = 0; event_lists[current_list_idx] != NULL; current_list_idx++) { - /* foreach element in the list */ for(current = GH_LIST_FIRST_ELEM(event_lists[current_list_idx]); current != NULL; current = next) { @@ -688,8 +789,10 @@ static void iterate_through_event_lists( /* foreach element in the list */ for(current = GH_LIST_FIRST_ELEM(event_lists[current_list_idx]); current != NULL; current = next) { - nse = (msevent *) GH_LIST_ELEM_DATA(current); + if (nsp->tracelevel > 7) + nsock_trace(nsp, "list %i, iterating %lu",current_list_idx, nse->id); + if ( ! nse->event_done) { switch(nse->type) { case NSE_TYPE_CONNECT: @@ -751,6 +854,49 @@ static void iterate_through_event_lists( } break; +#if HAVE_PCAP + case NSE_TYPE_PCAP_READ:{ + if (nsp->tracelevel > 5) + nsock_trace(nsp, "PCAP iterating %lu",nse->id); + + /* buffer empty? check it! */ + if ( FILESPACE_LENGTH(&(nse->iobuf))==0 ) + do_actual_pcap_read(nse); + + /* if already received smth */ + if ( FILESPACE_LENGTH(&(nse->iobuf))>0 ) + handle_pcap_read_result(nsp, nse, NSE_STATUS_SUCCESS); + + if (!nse->event_done && nse->timeout.tv_sec && + TIMEVAL_MSEC_SUBTRACT(nse->timeout, nsock_tod) <= 0) + handle_pcap_read_result(nsp, nse, NSE_STATUS_TIMEOUT); + + #if PCAP_BSD_SELECT_HACK + /* If event occured, and we're in BSD_HACK mode, than this event was added + * to two queues. evl.read_event and evl.pcap_read_event + * Of coure we should destroy it only once. + * I assume we're now in evl.read_event, co just unlink this event from + * evl.pcap_read_event */ + if(((mspcap *) nse->iod->pcap)->pcap_desc >= 0 && + nse->event_done && + event_lists[current_list_idx] == &nsp->evl.read_events){ + /* event is done, list is read_events and we're in BSD_HACK mode. + * So unlink event from pcap_read_events */ + gh_list_remove(&nsp->evl.pcap_read_events, nse); + if (nsp->tracelevel > 8) + nsock_trace(nsp, "PCAP NSE #%lu: Removing event from PCAP_READ_EVENTS", nse->id); + } + if(((mspcap *) nse->iod->pcap)->pcap_desc >= 0 && + nse->event_done && + event_lists[current_list_idx] == &nsp->evl.pcap_read_events){ + gh_list_remove(&nsp->evl.read_events, nse); + if (nsp->tracelevel > 8) + nsock_trace(nsp, "PCAP NSE #%lu: Removing event from READ_EVENTS", nse->id); + } + #endif + break; + } +#endif default: fatal("Event has unknown type (%d)", nse->type); break; /* unreached */ @@ -758,6 +904,9 @@ static void iterate_through_event_lists( } if (nse->event_done) { + if (nsp->tracelevel > 8) + nsock_trace(nsp, "NSE #%lu: Removing event from event_lists[%i]", nse->id, current_list_idx); + /* WooHoo! The event is ready to be sent */ msevent_dispatch_and_delete(nsp, nse, 1); next = GH_LIST_ELEM_NEXT(current); @@ -862,6 +1011,8 @@ const struct timeval *nsock_gettimeofday such as adjusting the descriptor select/poll lists, registering the timeout value, etc. */ void nsp_add_event(mspool *nsp, msevent *nse) { + if (nsp->tracelevel > 5) + nsock_trace(nsp, "NSE #%lu: Adding event", nse->id); /* First lets do the event-type independant stuff -- starting with timeouts */ @@ -918,6 +1069,36 @@ void nsp_add_event(mspool *nsp, msevent gh_list_prepend(&nsp->evl.timer_events, nse); break; +#if HAVE_PCAP + case NSE_TYPE_PCAP_READ:{ + mspcap *mp = (mspcap *) nse->iod->pcap; + assert(mp); + if(mp->pcap_desc >= 0){ /* pcap descriptor present */ + if(!nse->event_done){ + FD_SET(mp->pcap_desc, &nsp->mioi.fds_master_r); + nsp->mioi.max_sd = MAX(nsp->mioi.max_sd, mp->pcap_desc); + } + if (nsp->tracelevel > 8) + nsock_trace(nsp, "PCAP NSE #%lu: Adding event to READ_EVENTS", nse->id); + gh_list_prepend(&nsp->evl.read_events, nse); + + #if PCAP_BSD_SELECT_HACK + /* when using BSD hack we must do pcap_next() after select(). + * Let's insert this pcap to bot queues, to selectable and nonselectable. + * This will result in doing pcap_next_ex() just before select() */ + if (nsp->tracelevel > 8) + nsock_trace(nsp, "PCAP NSE #%lu: Adding event to PCAP_READ_EVENTS", nse->id); + gh_list_prepend(&nsp->evl.pcap_read_events, nse); + #endif + }else{ /* pcap isn't selectable. Add it to pcap-specific queue. */ + if (nsp->tracelevel > 8) + nsock_trace(nsp, "PCAP NSE #%lu: Adding event to PCAP_READ_EVENTS", nse->id); + gh_list_prepend(&nsp->evl.pcap_read_events, nse); + } + break; + } +#endif + default: assert(0); break; /* unreached */ @@ -1010,6 +1191,17 @@ void nsock_trace_handler_callback(mspool nsock_trace(ms, "Callback: %s %s %sfor EID %li", nse_type2str(nse->type), nse_status2str(nse->status), errstr, nse->id); + + break; +#if HAVE_PCAP + case NSE_TYPE_PCAP_READ: + nsock_trace(ms, "Callback: %s %s %sfor EID %li ", + nse_type2str(nse->type), nse_status2str(nse->status), + errstr, nse->id); + break; +#endif + default: + assert(0); break; } diff -NraupbwB soc07-5184/nsock/src/nsock_event.c soc07-nse-pcap/nsock/src/nsock_event.c --- soc07-5184/nsock/src/nsock_event.c 2007-07-10 18:15:24.000000000 +0200 +++ soc07-nse-pcap/nsock/src/nsock_event.c 2007-07-11 23:06:41.000000000 +0200 @@ -61,6 +61,10 @@ #include "nsock_internal.h" #include "gh_list.h" +#if HAVE_PCAP +#include "nsock_pcap.h" +#endif + #include extern struct timeval nsock_tod; @@ -125,6 +129,7 @@ int nsock_event_cancel(nsock_pool ms_poo mspool *nsp = (mspool *) ms_pool; enum nse_type type = get_event_id_type(id); gh_list *event_list = NULL; + gh_list *event_list2 = NULL; gh_list_elem *current, *next; msevent *nse = NULL; @@ -148,6 +153,12 @@ int nsock_event_cancel(nsock_pool ms_poo case NSE_TYPE_TIMER: event_list = &nsp->evl.timer_events; break; +#if HAVE_PCAP + case NSE_TYPE_PCAP_READ: + event_list = &nsp->evl.read_events; + event_list2 = &nsp->evl.pcap_read_events; + break; +#endif default: fatal("Bogus event type in nsock_event_cancel"); break; } @@ -161,6 +172,16 @@ int nsock_event_cancel(nsock_pool ms_poo break; } + if (current == NULL && event_list2){ + event_list = event_list2; + for(current = GH_LIST_FIRST_ELEM(event_list); current != NULL; + current = next) { + next = GH_LIST_ELEM_NEXT(current); + nse = (msevent *) GH_LIST_ELEM_DATA(current); + if (nse->id == id) + break; + } + } if (current == NULL) return 0; @@ -201,10 +222,51 @@ int msevent_cancel(mspool *nsp, msevent case NSE_TYPE_TIMER: handle_timer_result(nsp, nse, NSE_STATUS_CANCELLED); break; +#if HAVE_PCAP + case NSE_TYPE_PCAP_READ: + handle_pcap_read_result(nsp, nse, NSE_STATUS_CANCELLED); + break; +#endif + default: + assert(0); } assert(nse->event_done); gh_list_remove_elem(event_list, elem); + if (nsp->tracelevel > 8) + nsock_trace(nsp, "NSE #%lu: Removing event from some event_list", nse->id); + +#if HAVE_PCAP +#if PCAP_BSD_SELECT_HACK + if(nse->type==NSE_TYPE_PCAP_READ){ + if (nsp->tracelevel > 8) + nsock_trace(nsp, "PCAP NSE #%lu: CANCELL TEST el.pcap=%x el.read=%x el.curr=%x sd=%i", + nse->id, &nsp->evl.pcap_read_events, &nsp->evl.read_events, event_list,((mspcap *) nse->iod->pcap)->pcap_desc ); + /* If event occured, and we're in BSD_HACK mode, than this event was added + * to two queues. evl.read_event and evl.pcap_read_event + * Of coure we should destroy it only once. + * I assume we're now in evl.read_event, co just unlink this event from + * evl.pcap_read_event */ + + if(((mspcap *) nse->iod->pcap)->pcap_desc >= 0 && + event_list == &nsp->evl.read_events){ + /* event is done, list is read_events and we're in BSD_HACK mode. + * So unlink event from pcap_read_events */ + gh_list_remove(&nsp->evl.pcap_read_events, nse); + if (nsp->tracelevel > 8) + nsock_trace(nsp, "PCAP NSE #%lu: Removing event from PCAP_READ_EVENTS", nse->id); + } + if(((mspcap *) nse->iod->pcap)->pcap_desc >= 0 && + event_list == &nsp->evl.pcap_read_events){ + /* event is done, list is read_events and we're in BSD_HACK mode. + * So unlink event from pcap_read_events */ + gh_list_remove(&nsp->evl.read_events, nse); + if (nsp->tracelevel > 8) + nsock_trace(nsp, "PCAP NSE #%lu: Removing event from READ_EVENTS", nse->id); + } + } +#endif +#endif msevent_dispatch_and_delete(nsp, nse, notify); @@ -252,7 +314,7 @@ nsock_event_id get_new_event_id(mspool * unsigned long serial = ms->next_event_serial++; unsigned long max_serial_allowed; int shiftbits; - assert(type <= 4); + assert(type < NSE_TYPE_MAX); shiftbits = sizeof(nsock_event_id) * 8 - TYPE_CODE_NUM_BITS; max_serial_allowed = ( 1 << shiftbits ) - 1; @@ -299,6 +361,15 @@ msevent *msevent_new(mspool *nsp, enum n if (type == NSE_TYPE_READ || type == NSE_TYPE_WRITE) { filespace_init(&(nse->iobuf), 1024); } +#if HAVE_SSL + if (type == NSE_TYPE_PCAP_READ) { + mspcap *mp = (mspcap *) nsi->pcap; + assert(mp); + int sz = mp->snaplen+1 + sizeof(nsock_pcap); + filespace_init(&(nse->iobuf), sz); + } +#endif + if (timeout_msecs != -1) { assert(timeout_msecs >= 0); TIMEVAL_MSEC_ADD(nse->timeout, nsock_tod, timeout_msecs); @@ -307,6 +378,12 @@ msevent *msevent_new(mspool *nsp, enum n nse->handler = handler; nse->userdata = userdata; nse->time_created = nsock_tod; + + if (nsp->tracelevel > 3) + nsock_trace(nsp, "msevent_new (IOD #%li) (EID #%li)", + nse->iod->id, + nse->id); + return nse; } @@ -317,10 +394,22 @@ msevent *msevent_new(mspool *nsp, enum n remember to do this if you call msevent_delete() directly */ void msevent_delete(mspool *nsp, msevent *nse) { + if (nsp->tracelevel > 3) + nsock_trace(nsp, "msevent_delete (IOD #%li) (EID #%li)", + nse->iod->id, + nse->id); + /* First free the IOBuf inside it if neccessary */ if (nse->type == NSE_TYPE_READ || nse->type == NSE_TYPE_WRITE) { fs_free(&nse->iobuf); } + #if HAVE_PCAP + if (nse->type == NSE_TYPE_PCAP_READ) { + fs_free(&nse->iobuf); + if (nsp->tracelevel > 5) + nsock_trace(nsp, "PCAP removed %lu\n",nse->id); + } + #endif /* Now we add the event back into the free pool */ gh_list_prepend(&nsp->evl.free_events, nse); @@ -337,6 +426,7 @@ const char *nse_type2str(enum nse_type t case NSE_TYPE_READ: return "READ"; case NSE_TYPE_WRITE: return "WRITE"; case NSE_TYPE_TIMER: return "TIMER"; + case NSE_TYPE_PCAP_READ: return "READ-PCAP"; default: return "UNKNOWN!"; } diff -NraupbwB soc07-5184/nsock/src/nsock_internal.h soc07-nse-pcap/nsock/src/nsock_internal.h --- soc07-5184/nsock/src/nsock_internal.h 2007-07-10 18:15:24.000000000 +0200 +++ soc07-nse-pcap/nsock/src/nsock_internal.h 2007-07-11 23:06:41.000000000 +0200 @@ -136,6 +136,9 @@ struct event_lists { gh_list read_events; gh_list write_events; gh_list timer_events; + #if HAVE_PCAP + gh_list pcap_read_events; + #endif gh_list free_events; /* When an event is deleted, we stick it here for later reuse */ struct timeval next_ev; /* The soonest time that either a timer event goes @@ -226,6 +229,8 @@ struct msiod { unsigned long id; /* Every iod has an id which is always unique for the same nspool (unless you create billions of them) */ void *userdata; + + void *pcap; /* Pointer to mspcap struct (used only if pcap support is included) */ }; @@ -321,6 +326,11 @@ void handle_write_result(mspool *ms, mse void handle_timer_result(mspool *ms, msevent *nse, enum nse_status status); +#if HAVE_PCAP +void handle_pcap_read_result(mspool *ms, msevent *nse, + enum nse_status status); +#endif + void nsock_trace(mspool *ms, char *fmt, ...) __attribute__ ((format (printf, 2, 3))); diff -NraupbwB soc07-5184/nsock/src/nsock_iod.c soc07-nse-pcap/nsock/src/nsock_iod.c --- soc07-5184/nsock/src/nsock_iod.c 2007-07-10 18:15:24.000000000 +0200 +++ soc07-nse-pcap/nsock/src/nsock_iod.c 2007-07-11 23:06:41.000000000 +0200 @@ -63,6 +63,10 @@ #include "gh_list.h" #include "netutils.h" +#if HAVE_PCAP +#include "nsock_pcap.h" +#endif + #include /* nsock_iod is like a "file descriptor" for the nsock library. You @@ -207,6 +211,26 @@ void nsi_delete(nsock_iod nsockiod, int nsi->state = NSIOD_STATE_DELETED; nsi->userdata = NULL; +#if HAVE_PCAP + if(nsi->pcap){ + mspcap *mp = (mspcap *) nsi->pcap; + if(mp->pt){ + pcap_close(mp->pt); + mp->pt=NULL; + } + if(mp->pcap_desc){ + // Should I close pcap_desc or pcap_close does this for me? + mp->pcap_desc = -1; + } + if(mp->pcap_device){ + free(mp->pcap_device); + mp->pcap_device = NULL; + } + free(mp); + nsi->pcap = NULL; + } +#endif + gh_list_remove_elem(&nsi->nsp->active_iods, nsi->entry_in_nsp_active_iods); gh_list_prepend(&nsi->nsp->free_iods, nsi); diff -NraupbwB soc07-5184/nsock/src/nsock_pcap.c soc07-nse-pcap/nsock/src/nsock_pcap.c --- soc07-5184/nsock/src/nsock_pcap.c 1970-01-01 01:00:00.000000000 +0100 +++ soc07-nse-pcap/nsock/src/nsock_pcap.c 2007-07-11 23:06:41.000000000 +0200 @@ -0,0 +1,370 @@ +#include "nsock.h" +#include "nsock_internal.h" +#include "nsock_pcap.h" +#include + +#if HAVE_PCAP +static int nsock_pcap_get_l3_offset(pcap_t *pt, int *dl); +char * nsock_pcap_set_filter(pcap_t *pt, const char *device, const char *bpf); + +/* + * Convert new nsiod to pcap descriptor. Other parameters have the + * same meaning as for pcap_open_live in pcap(3). + * device : pcap-style device name + * snaplen : size of packet to be copied to hanler + * promisc : whether to open device in promiscous mode + * bpf_fmt : berkeley filter + * return value: NULL if everything was okay, or error string if error occurred + * */ +char* nsock_pcap_open(nsock_pool nsp, nsock_iod nsiod, + const char *pcap_device, int snaplen, int promisc, + const char *bpf_fmt, ...) + +{ + msiod *nsi = (msiod *) nsiod; + mspool *ms = (mspool *) nsp; + mspcap *mp = (mspcap *) nsi->pcap; + static char errorbuf[128]; + + if(mp) return "nsock-pcap: this nsi already has pcap device opened"; + + mp = (mspcap *)safe_malloc(sizeof(mspcap)); + nsi->pcap = (void*)mp; + + char err0r[PCAP_ERRBUF_SIZE]; + + #ifdef PCAP_CAN_DO_SELECT + #if PCAP_BSD_SELECT_HACK + /* MacOsX reports error if to_ms is too big (like INT_MAX) with error + * FAILED. Reported error: BIOCSRTIMEOUT: Invalid argument + * INT_MAX/6 (=357913941) seems to be working...*/ + int to_ms = 357913941; + #else + int to_ms = 200; + #endif + #else + int to_ms = 1; + #endif + + /* packet filter string */ + char bpf[4096]; + va_list ap; + + va_start(ap, bpf_fmt); + if(vsnprintf(bpf, sizeof(bpf), bpf_fmt, ap) >= (int) sizeof(bpf)){ + va_end(ap); + return "nsock-pcap: nsock_pcap_open called with too-large bpf filter arg"; + } + va_end(ap); + + if (ms->tracelevel > 0) + nsock_trace(ms, "PCAP requested on device '%s' with berkeley filter '%s' (promisc=%i snaplen=%i to_ms=%i) (IOD #%li)", + pcap_device,bpf, promisc, snaplen, to_ms, nsi->id); + + int failed = 0; + do { + mp->pt = pcap_open_live((char*)pcap_device, snaplen, promisc, to_ms, err0r); + if (mp->pt) /* okay, opened!*/ + break; + /* sorry, something failed*/ + if (++failed >= 3) { + fprintf(stderr, + "Call to pcap_open_live(%s, %d, %d, %d) failed three times. Reported error: %s\n" + "There are several possible reasons for this, depending on your operating system:\n" + "LINUX: If you are getting Socket type not supported, try modprobe af_packet or recompile your kernel with SOCK_PACKET enabled.\n" + "*BSD: If you are getting device not configured, you need to recompile your kernel with Berkeley Packet Filter support. If you are getting No such file or directory, try creating the device (eg cd /dev; MAKEDEV ; or use mknod).\n" + "*WINDOWS: Nmap only supports ethernet interfaces on Windows for most operations because Microsoft disabled raw sockets as of Windows XP SP2. Depending on the reason for this error, it is possible that the --unprivileged command-line argument will help.\n" + "SOLARIS: If you are trying to scan localhost and getting '/dev/lo0: No such file or directory', complain to Sun. I don't think Solaris can support advanced localhost scans. You can probably use \"-P0 -sT localhost\" though.\n\n", + pcap_device, snaplen, promisc, to_ms, err0r); + return "nsock-pcap: can't open pcap! are you root?"; + } + + fprintf(stderr, "pcap_open_live(%s, %d, %d, %d) FAILED. Reported error: %s. Will wait %d seconds then retry.\n", + pcap_device, snaplen, promisc, to_ms, err0r, 4*failed); + sleep(4* failed); + }while(1); + + char *e = nsock_pcap_set_filter(mp->pt, pcap_device, bpf); + if(e) return e; + + + #ifdef WIN32 + /* We want any responses back ASAP */ + pcap_setmintocopy(mp->pt, 1); + + /* If there are no packets left to read exit after to_ms miliseconds*/ + PacketSetReadTimeout(mp->pt->adapter, to_ms); + #endif + + int datalink; + mp->l3_offset = nsock_pcap_get_l3_offset(mp->pt, &datalink); + mp->snaplen = snaplen; + mp->datalink = datalink; + mp->pcap_device = strdup(pcap_device); + #ifdef PCAP_CAN_DO_SELECT + mp->pcap_desc = pcap_get_selectable_fd(mp->pt); + #else + mp->pcap_desc = -1; + #endif + + /* Set device non-blocking */ + if(pcap_setnonblock(mp->pt, 1, err0r) < 0){ + /* I can't do select() on pcap! blockig + no_select is fatal */ + if(mp->pcap_desc < 0){ + snprintf(errorbuf, sizeof(errorbuf),"nsock-pcap: Failed to set pcap descriptor on device %s to nonblocking state: %s", pcap_device, err0r); + return errorbuf; + } + + /* When we use bsd hack we also need to set non-blocking */ + #ifdef PCAP_BSD_SELECT_HACK + snprintf(errorbuf, sizeof(errorbuf),"nsock-pcap: Failed to set pcap descriptor on device %s to nonblocking state: %s", pcap_device, err0r); + return errorbuf; + #endif + + /* in other case, we can accept blocking pcap */ + fprintf(stderr, "Failed to set pcap descriptor on device %s to nonblocking state: %s", pcap_device, err0r); + } + + if (ms->tracelevel > 0) + nsock_trace(ms, "PCAP created successfully on device '%s' (pcap_desc=%i bsd_hack=%i to_valid=%i l3_offset=%i) (IOD #%li)", + pcap_device, + mp->pcap_desc, + #if PCAP_BSD_SELECT_HACK + 1, + #else + 0, + #endif + #if PCAP_RECV_TIMEVAL_VALID + 1, + #else + 0, + #endif + mp->l3_offset, + nsi->id); + return NULL; +} + +char *nsock_pcap_set_filter(pcap_t *pt, const char *device, const char *bpf) +{ + struct bpf_program fcode; + #ifndef __amigaos__ + unsigned int localnet, netmask; + #else + bpf_u_int32 localnet, netmask; + #endif + char err0r[PCAP_ERRBUF_SIZE]; + static char errorbuf[128]; + + // Cast below is becaue OpenBSD apparently has a version that takes a + // non-const device (hopefully they don't actually write to it). + if (pcap_lookupnet( (char *) device, &localnet, &netmask, err0r) < 0){ + snprintf(errorbuf, sizeof(errorbuf), "Failed to lookup subnet/netmask for device (%s): %s", device, err0r); + return errorbuf; + } + + // log_write(LOG_STDOUT, "Packet capture filter (device %s): %s\n", device, buf); + + if (pcap_compile(pt, &fcode, (char*)bpf, 1, netmask) < 0){ + snprintf(errorbuf, sizeof(errorbuf), "Error compiling our pcap filter: %s\n", pcap_geterr(pt)); + return errorbuf; + } + + if (pcap_setfilter(pt, &fcode) < 0 ){ + snprintf(errorbuf, sizeof(errorbuf),"Failed to set the pcap filter: %s\n", pcap_geterr(pt)); + return errorbuf; + } + + pcap_freecode(&fcode); + return NULL; +} + +static int nsock_pcap_get_l3_offset(pcap_t *pt, int *dl){ + int datalink; + unsigned int offset = 0; + + /* New packet capture device, need to recompute offset */ + if ( (datalink = pcap_datalink(pt)) < 0) + fatal("Cannot obtain datalink information: %s", pcap_geterr(pt)); + + /* NOTE: IF A NEW OFFSET EVER EXCEEDS THE CURRENT MAX (24), ADJUST + MAX_LINK_HEADERSZ in tcpip.h */ + switch(datalink) { + case DLT_EN10MB: offset = 14; break; + case DLT_IEEE802: offset = 22; break; + #ifdef __amigaos__ + case DLT_MIAMI: offset = 16; break; + #endif + #ifdef DLT_LOOP + case DLT_LOOP: + #endif + case DLT_NULL: offset = 4; break; + + case DLT_SLIP: + #ifdef DLT_SLIP_BSDOS + case DLT_SLIP_BSDOS: + #endif + #if (FREEBSD || OPENBSD || NETBSD || BSDI || MACOSX) + offset = 16;break; + #else + offset = 24;break; /* Anyone use this??? */ + #endif + + case DLT_PPP: + #ifdef DLT_PPP_BSDOS + case DLT_PPP_BSDOS: + #endif + #ifdef DLT_PPP_SERIAL + case DLT_PPP_SERIAL: + #endif + #ifdef DLT_PPP_ETHER + case DLT_PPP_ETHER: + #endif + #if (FREEBSD || OPENBSD || NETBSD || BSDI || MACOSX) + offset = 4;break; + #else + #ifdef SOLARIS + offset = 8;break; + #else + offset = 24;break; /* Anyone use this? */ + #endif /* ifdef solaris */ + #endif /* if freebsd || openbsd || netbsd || bsdi */ + case DLT_RAW: offset = 0; break; + case DLT_FDDI: offset = 21; break; + #ifdef DLT_ENC + case DLT_ENC: offset = 12; break; + #endif /* DLT_ENC */ + #ifdef DLT_LINUX_SLL + case DLT_LINUX_SLL: offset = 16; break; + #endif + + default: /* Sorry, link type is unknown. */ + fatal("Unknown datalink type %d.\n", datalink); + } + if(dl) + *dl = datalink; + return(offset); +} + + +/* + * Requests exacly one packet to be captured. + * */ +nsock_event_id nsock_pcap_read_packet(nsock_pool nsp, nsock_iod nsiod, + nsock_ev_handler handler, int timeout_msecs, + void *userdata) +{ + msiod *nsi = (msiod *) nsiod; + mspool *ms = (mspool *) nsp; + msevent *nse; + + nse = msevent_new(ms, NSE_TYPE_PCAP_READ, nsi, timeout_msecs, handler, userdata); + assert(nse); + + if (ms->tracelevel > 0) { + nsock_trace(ms, "Pcap read request from IOD #%li EID %li", + nsi->id, nse->id); + } + + nsp_add_event(ms, nse); + + return nse->id; +} + +/* + * Remember that pcap descriptor is in nonblocking state. */ +int do_actual_pcap_read(msevent *nse) +{ + msiod *iod = nse->iod; + mspcap *mp = (mspcap *) iod->pcap; + + nsock_pcap npp; + nsock_pcap *n; + memset(&npp, 0, sizeof(nsock_pcap)); + + if (nse->iod->nsp->tracelevel > 2) + nsock_trace(nse->iod->nsp, "PCAP do_actual_pcap_read TEST (IOD #%li) (EID #%li)", + nse->iod->id, nse->id); + + assert( FILESPACE_LENGTH(&(nse->iobuf)) == 0 ); + + struct pcap_pkthdr *pkt_header; + const unsigned char *pkt_data = NULL; + int rc = pcap_next_ex(mp->pt, &pkt_header, &pkt_data); + switch(rc){ + case 1: /* read good packet */ + #ifdef PCAP_RECV_TIMEVAL_VALID + npp.ts = pkt_header->ts; + #else + /* on these platforms time received from pcap is invalid. It's better to set current time */ + memcpy(&npp.ts, nsock_gettimeofday(), sizeof(struct timeval)); + #endif + npp.len = pkt_header->len; + npp.caplen = pkt_header->caplen; + npp.packet = pkt_data; + fscat(&(nse->iobuf), (char*)&npp, sizeof(npp)); + fscat(&(nse->iobuf), (char*)pkt_data, npp.caplen); + n = (nsock_pcap *) FILESPACE_STR(&(nse->iobuf)); + n->packet = (unsigned char*)FILESPACE_STR(&(nse->iobuf))+sizeof(npp); + if (nse->iod->nsp->tracelevel > 2) + nsock_trace(nse->iod->nsp, "PCAP do_actual_pcap_read READ (IOD #%li) (EID #%li) size=%i", + nse->iod->id, nse->id, pkt_header->caplen); + return(1); + case 0: /* timeout */ + return(0); + case -1: /* error */ + fatal("pcap_next_ex() fatal error while reading from pcap: %s\n", pcap_geterr(mp->pt)); + break; + case -2: /* no more packets in savefile (if reading from one) */ + default: + assert(0); + } + return 0; +} + +void nse_readpcap(nsock_event nsee, + const unsigned char **l2_data, size_t *l2_len, + const unsigned char **l3_data, size_t *l3_len, + size_t *packet_len, struct timeval *ts) +{ + msevent *nse = (msevent *)nsee; + msiod *iod = nse->iod; + mspcap *mp = (mspcap *) iod->pcap; + + nsock_pcap *n = (nsock_pcap *) FILESPACE_STR(&(nse->iobuf)); + if(FILESPACE_LENGTH(&(nse->iobuf)) < sizeof(nsock_pcap)){ + if(l2_data) *l2_data = NULL; + if(l2_len ) *l2_len = 0; + if(l3_data) *l3_data = NULL; + if(l3_len ) *l3_len = 0; + if(packet_len) *packet_len = 0; + return; + } + + size_t l2l = MIN(mp->l3_offset, n->caplen); + size_t l3l = MAX(0, n->caplen-mp->l3_offset); + + if(l2_data) *l2_data = n->packet; + if(l2_len ) *l2_len = l2l; + if(l3_data) *l3_data = l3l>0? n->packet+l2l : NULL; + if(l3_len ) *l3_len = l3l; + if(packet_len) *packet_len = n->len; + if(ts) *ts = n->ts; + return; +} + +int nsi_pcap_linktype(nsock_iod nsiod){ + msiod *nsi = (msiod *) nsiod; + mspcap *mp = (mspcap *) nsi->pcap; + assert(mp); + return(mp->datalink); +} + +int nsi_is_pcap(nsock_iod nsiod){ + msiod *nsi = (msiod *) nsiod; + mspcap *mp = (mspcap *) nsi->pcap; + return(mp!=NULL); +} + + +#endif // HAVE_PCAP + diff -NraupbwB soc07-5184/nsock/src/nsock_pcap.h soc07-nse-pcap/nsock/src/nsock_pcap.h --- soc07-5184/nsock/src/nsock_pcap.h 1970-01-01 01:00:00.000000000 +0100 +++ soc07-nse-pcap/nsock/src/nsock_pcap.h 2007-07-11 23:06:41.000000000 +0200 @@ -0,0 +1,85 @@ +#ifndef NSOCK_PCAP_H +#define NSOCK_PCAP_H + +#include "nsock_internal.h" +#ifdef HAVE_PCAP + +#ifdef WIN32 +#include "mswin32\winclude.h" +#include "pcap-int.h" +#endif + +#include "pcap.h" + +#include +#include + + +/* + * There are three possible ways of reading packets from pcap descriptor: + * do select() on descriptor -> this one is of course the best, but + * there are systems that don't support this like WIN32 + * This works perfectly for Linux. + * do select() but whith some hacks -> this one is hack for older bsd + * systems, Descriptor *must* be set in nonblocking mode. + * never do select() -> this one is for WIN32 and other systems that + * return descriptor -1 from pcap_get_selectable_fd() + * In this case descriptor *must* be set in nonblocking mode. + * If that fails than we can't do any sniffing from that box. + * + * In all cases we try to set descriptor to non-blocking mode. + * */ + +// Returns whether the system supports pcap_get_selectable_fd() properly +#if !defined(WIN32) +#define PCAP_CAN_DO_SELECT 1 +#endif + +/* + * Note that on most versions of most BSDs (including Mac OS X) select() and poll() do not work + * correctly on BPF devices; pcap_get_selectable_fd() will return a file descriptor on most of those + * versions (the exceptions being FreeBSD 4.3 and 4.4), a simple select() or poll() will + * not return even after a timeout specified in pcap_open_live() expires. To work around + * this, an application that uses select() or poll() to wait for packets to arrive must put + * the pcap_t in non-blocking mode, and must arrange that the select() or poll() have a timeout + * less than or equal to the timeout specified in pcap_open_live(), and must try to read packets + * after that timeout expires, regardless of whether select() or poll() indicated that the file + * descriptor for the pcap_t is ready to be read or not. (That workaround will not work in + * FreeBSD 4.3 and later; however, in FreeBSD 4.6 and later, select() and poll() work correctly + * on BPF devices, so the workaround isn't necessary, although it does no harm.) + */ +#if defined(MACOSX) || defined(FREEBSD) || defined(OPENBSD) +// Well, now select() is not receiving any pcap events on MACOSX, but maybe it will someday :) +// in both cases. It never hurts to enable this feature. It just has performance penalty. +#define PCAP_BSD_SELECT_HACK 1 +#endif + +// Returns whether the packet receive time value obtained from libpcap +// (and thus by readip_pcap()) should be considered valid. When +// invalid (Windows and Amiga), readip_pcap returns the time you called it. +#if !defined(WIN32) && !defined(__amigaos__) +#define PCAP_RECV_TIMEVAL_VALID 1 +#endif + + +typedef struct{ + pcap_t *pt; + int pcap_desc; + int datalink; + int l3_offset; + int snaplen; + char *pcap_device; +} mspcap; + + +typedef struct{ + struct timeval ts; + int caplen; + int len; + const unsigned char *packet; // caplen bytes +} nsock_pcap; + +int do_actual_pcap_read(msevent *nse); + +#endif /* HAVE_PCAP */ +#endif /* NSOCK_PCAP_H */ diff -NraupbwB soc07-5184/nsock/src/nsock_pool.c soc07-nse-pcap/nsock/src/nsock_pool.c --- soc07-5184/nsock/src/nsock_pool.c 2007-07-10 18:15:24.000000000 +0200 +++ soc07-nse-pcap/nsock/src/nsock_pool.c 2007-07-11 23:06:41.000000000 +0200 @@ -168,6 +168,9 @@ nsock_pool nsp_new(void *userdata) { gh_list_init(&nsp->evl.read_events); gh_list_init(&nsp->evl.write_events); gh_list_init(&nsp->evl.timer_events); + #if HAVE_PCAP + gh_list_init(&nsp->evl.pcap_read_events); + #endif gh_list_init(&nsp->evl.free_events); nsp->evl.next_ev.tv_sec = 0; nsp->evl.events_pending = 0; @@ -192,6 +195,9 @@ void nsp_delete(nsock_pool ms_pool) { &nsp->evl.read_events, &nsp->evl.write_events, &nsp->evl.timer_events, + #if HAVE_PCAP + &nsp->evl.pcap_read_events, + #endif 0 }; int current_list_idx; diff -NraupbwB soc07-5184/scripts/p0fa.fp soc07-nse-pcap/scripts/p0fa.fp --- soc07-5184/scripts/p0fa.fp 1970-01-01 01:00:00.000000000 +0100 +++ soc07-nse-pcap/scripts/p0fa.fp 2007-07-11 23:06:41.000000000 +0200 @@ -0,0 +1,208 @@ +# +# p0f - SYN+ACK fingerprints +# -------------------------- +# +# .-------------------------------------------------------------------------. +# | The purpose of this file is to cover signatures for outgoing TCP/IP | +# | connections (SYN+ACK packets). This mode of operation can be enabled | +# | with -A option. Please refer to p0f.fp for information on the metrics | +# | used to create a signature, and for a guide on adding new entries to | +# | those files. This database is somewhat neglected, and is looking for a | +# | caring maintainer. | +# `-------------------------------------------------------------------------' +# +# (C) Copyright 2000-2006 by Michal Zalewski +# +# Plenty of signatures contributed in bulk by rain forest puppy, Paul Woo and +# Michael Bauer. +# +# Submit all additions to the authors. Read p0f.fp before adding any +# signatures. Run p0f -A -C after making any modifications. This file is +# NOT compatible with SYN, RST+, or stray ACK modes. Use only with -A option. +# +# Feel like contributing? You can run p0f -A -K, then test/tryid -iR nnn... +# +# IMPORTANT INFORMATION ABOUT THE INTERDEPENDENCY OF SYNs AND SYN+ACKs +# -------------------------------------------------------------------- +# +# Some systems would have different SYN+ACK fingerprints depending on +# the system that sent SYN. More specifically, RFC1323, RFC2018 and +# RFC1644 extensions sometimes show up only if SYN had them enabled. +# +# Also, some silly systems may copy WSS from the SYN packet you've sent, +# in which case, you need to wildcard the value. Use test/sendsyn.c, which +# uses a distinct WSS of 12345, to test for this condition if unsure. +# +# IMPORTANT INFORMATION ABOUT DIFFERENCES IN COMPARISON TO p0f.fp: +# ---------------------------------------------------------------- +# +# - 'A' quirk would be present on almost every signature here. ACK number +# is unusual for SYN packets, but is a commonplace in SYN+ACK packets, +# of course. It is still possible to have a signature without 'A', when +# the ACK flag is present but the value is zero - this, however, is +# very uncommon. +# +# - 'T' quirk would show up on almost all signatures for systems implementing +# RFC1323. The second timestamp is only unusual for SYN packets. SYN+ACK +# are expected to have it set. +# + +########################## +# Standard OS signatures # +########################## + +# ---------------- Linux ------------------- + +32736:64:0:44:M*:A:Linux:2.0 +S22:64:1:60:M*,S,T,N,W0:AT:Linux:2.2 +S22:64:1:52:M*,N,N,S,N,W0:A:Linux:2.2 w/o timestamps + +5792:64:1:60:M*,S,T,N,W0:AT:Linux:older 2.4 +5792:64:1:60:M*,S,T,N,W0:ZAT:Linux:recent 2.4 (1) +S4:64:1:44:M*:ZA:Linux:recent 2.4 (2) +5792:64:1:44:M*:ZA:Linux:recent 2.4 (3) + +S4:64:1:52:M*,N,N,S,N,W0:ZA:Linux:2.4 w/o timestamps + +# --------------- Windows ------------------ + +65535:128:1:64:M*,N,W0,N,N,T0,N,N,S:A:Windows:2000 SP4 +S44:128:1:64:M*,N,W0,N,N,T0,N,N,S:A:Windows:XP SP1 +S12:128:1:64:M*,N,W0,N,N,T0,N,N,S:A:Windows:2000 (SP1+) +S6:128:1:44:M*:A:Windows:NT 4.0 SP1+ +65535:128:1:48:M*,N,N,S:A:Windows:98 (SE) +65535:128:1:44:M*:A:Windows:2000 (1) +16616:128:1:44:M*:A:Windows:2003 +16384:128:1:44:M*:A:Windows:2000 (2) +S16:128:1:44:M*:A:Windows:2000 (3) + +# ------------------- OpenBSD -------------- + +17376:64:1:64:M*,N,N,S,N,W0,N,N,T:AT:OpenBSD:3.3 + +# ------------------- NetBSD ---------------- + +16384:64:0:60:M*,N,W0,N,N,T0:AT:NetBSD:1.6 + +# ----------------- HP/UX ------------------ + +32768:64:1:44:M*:A:HPUX:10.20 + +# ----------------- Tru64 ------------------ + +S23:60:0:48:M*,N,W0:A:Tru64:5.0 (1) +65535:64:0:44:M*:A:Tru64:5.0 (2) + +# ----------------- Novell ----------------- + +6144:128:1:52:M*,W0,N,S,N,N:A:Novell:Netware 6.0 (SP3) +32768:128:1:44:M*:A:Novell:Netware 5.1 + +# ------------------ IRIX ------------------ + +60816:60:1:60:M*,N,W0,N,N,T:AT:IRIX:6.5.0 + +# ----------------- Solaris ---------------- + +49232:64:1:64:N,N,T,M*,N,W0,N,N,S:AT:Solaris:9 (1) +S1:255:1:60:N,N,T,N,W0,M*:AT:Solaris:7 +24656:64:1:44:M*:A:Solaris:8 +33304:64:1:60:N,N,T,M*,N,W1:AT:Solaris:9 (2) + +# ----------------- FreeBSD ---------------- + +65535:64:1:60:M*,N,W1,N,N,T:AT:FreeBSD:5.0 +57344:64:1:44:M*:A:FreeBSD:4.6-4.8 +65535:64:1:44:M*:A:FreeBSD:4.4 + +57344:64:1:48:M1460,N,W0:A:FreeBSD:4.6-4.8 (wscale) +57344:64:1:60:M1460,N,W0,N,N,T:AT:FreeBSD:4.6-4.8 (RFC1323) + +# ------------------- AIX ------------------ + +S17:255:1:44:M536:A:AIX:4.2 + +S12:64:0:44:M1460:A:AIX:5.2 ML04 (1) +S42:64:0:44:M1460:A:AIX:5.2 ML04 (2) + +# ------------------ BSD/OS ---------------- + +S6:64:1:60:M1460,N,W0,N,N,T:AT:BSD/OS:4.0.x + +# ------------------ OS/390 ---------------- + +2048:64:0:44:M1460:A:OS/390:? + +# ------------------ Novell ---------------- + +6144:128:1:44:M1400:A:Novell:iChain 2.2 + +# ------------------ MacOS ----------------- + +33304:64:1:60:M*,N,W0,N,N,T:AT:MacOS:X 10.2.6 + +################################################################# +# Contributed by Ryan Kruse - trial run # +################################################################# + +# S4:255:0:44:M1024:A:Cisco:LocalDirector +# 1024:255:0:44:M536:A:Cisco,3COM,Nortel:CatIOS,SuperStack,BayStack +# S16:64:0:44:M512:A:Nortel:Contivity +# 8192:64:0:44:M1460:A:Cisco,Nortel,SonicWall,Tasman:Aironet,BayStack Switch,Soho,1200 +# 4096:255:0:44:M1460:A:Cisco:PIX,CatOS +# 8192:128:0:44:M1460:A:Cisco:VPN Concentrator +# 8192:128:0:60:M1460,N,W0,N,N,T:AT:Cisco:VPN Concentrator +# 4096:32:0:44:M1460:A:Cisco,3COM,Extreme,Nortel:Catalyst Switch CatOS,CoreBuilder,Summit,Passport +# S4:255:0:44:M536:ZA:Cisco:IOS +# 1024:32:0:44:M1480:UA:Nortel:BayStack Switch +# 4096:60:0:44:M1460:A:Adtran:NetVanta +# 4096:64:0:44:M1008:A:Adtran:TSU +# S4:32:0:44:M1024:A:Alcatel:Switch +# S8:255:0:44:M536:ZA:Cisco:IOS +# 50:255:0:44:M536:ZA:Cisco:CatIOS +# 512:64:0:40:.:A:Dell:Switch +# 4096:64:0:40:.:A:Enterasys:Vertical Horizon Switch +# 17640:64:1:44:M1460:A:F5,Juniper,RiverStone:BigIP,Juniper OS,Router 7.0+ +# 16384:64:0:44:M1460:A:Foundry,SonicWall:BigIron,TZ +# 4096:64:0:44:M1452:A:HP:ProCurve Switch +# 1024:64:0:44:M1260:A:Marconi:ES +# 10240:30:0:44:M1460:A:Milan:Switch +# 4096:64:0:44:M1380:A:NetScreen:Firewall +# S32:64:0:44:M512:A:Nokia:CheckPoint +# 1024:64:0:44:M536:A:Nortel:BayStack Switch +# 4128:255:0:44:M*:ZA:Cisco:IOS +# 1024:16:0:44:M536:A:Nortel:BayStack Switch +# 1024:30:0:44:M1480:A:Nortel:BayStack Switch +# S4:64:0:44:M1460:A:Symbol:Spectrum Access Point +# S2:255:0:44:M512:A:ZyXEL:Prestige +# S16:255:0:44:M1024:A:ZyXEL:ZyAI + +########################################### +# Appliance / embedded / other signatures # +########################################### + +16384:64:1:44:M1460:A:F5:BigIP LB 4.1.x (sometimes FreeBSD) +4128:255:0:44:M*:ZA:Cisco:Catalyst 2900 12.0(5) +4096:60:0:44:M*:A:Brother:HL-1270N +S1:30:0:44:M1730:A:Cyclades:PR3000 +8192:64:1:44:M1460:A:NetApp:Data OnTap 6.x +5792:64:1:60:W0,N,N,N,T,M1460:ZAT:FortiNet:FortiGate 50 +S1:64:1:44:M1460:A:NetCache:5.3.1 +S1:64:0:44:M512:A:Printer:controller (?) +4096:128:0:40:.:A:Sequent:DYNIX 4.2.x +S16:64:0:44:M512:A:3Com:NBX PBX (BSD/OS 2.1) +16000:64:0:44:M1442:A:CastleNet:DSL router +S2:64:0:44:M32728:A:D-Link:DSL-500 +S4:60:0:44:M1460:A:HP:JetDirect A.05.32 +8576:64:1:44:M*:A:Raptor:firewall +S12:64:1:44:M1400:A:Cequrux Firewall:4.x +2048:255:0:44:M1400:A:Netgear:MR814 +16384:128:0:64:M1460,N,W0,N,N,T0,N,N,S:A:Akamai:??? (1) +16384:128:0:60:M1460,N,W0,N,N,T0:A:Akamai:??? (2) + +8190:255:0:44:M1452:A:Citrix:Netscaler 6.1 + +# Whatever they run. EOL boys... +S6:128:1:48:M1460,E:PA:@Slashdot:or BusinessWeek (???) + + diff -NraupbwB soc07-5184/scripts/p0f.nse soc07-nse-pcap/scripts/p0f.nse --- soc07-5184/scripts/p0f.nse 1970-01-01 01:00:00.000000000 +0100 +++ soc07-nse-pcap/scripts/p0f.nse 2007-07-11 23:06:49.000000000 +0200 @@ -0,0 +1,614 @@ +id = "p0f signature" +description = "Guesses target os per each opened port, instead of standard per host tests." +author = "Marek Majkowski " +license = "See nmaps COPYING for licence" + +require "bit" + +--[[ + it's just an implementation of lcamtuf's p0f SYN+ACK scan +]]-- + +categories = {"safe"} -- intrusive? + +---------------------------------------------------------------------------------------------------------------- +Tcp = {} + +function Tcp:new(packet, packet_len) + local o = setmetatable({}, {__index = Tcp}) + o.buf = packet + o.packet_len = packet_len + if not o:parse_ip() then + return nil + end + if not o:parse_tcp() then + return nil + end + return o +end +function u8(b, i) + return string.byte(b, i+1) +end +function u16(b, i) + local b1,b2 + b1, b2 = string.byte(b, i+1), string.byte(b, i+2) + -- 2^8 2^0 + return b1*256 + b2 +end +function u32(b,i) + local b1,b2,b3,b4 + b1, b2 = string.byte(b, i+1), string.byte(b, i+2) + b3, b4 = string.byte(b, i+3), string.byte(b, i+4) + -- 2^24 2^16 2^8 2^0 + return b1*16777216 + b2*65536 + b3*256 + b4 +end + +function Tcp:u8(index) + return u8(self.buf, index) +end +function Tcp:u16(index) + return u16(self.buf, index) +end +function Tcp:u32(index) + return u32(self.buf, index) +end +function Tcp:raw(index, length) + return string.char(string.byte(self.buf, index+1, index+1+length-1)) + +end +function Tcp:parse_options(offset, length) -- parse ip/tcp options to dict structure + local options = {} + local op = 1 + local opt_ptr = 0 + while opt_ptr < length do + local t, l, d + options[op] = {} + + t = self:u8(offset + opt_ptr) + options[op].type = t + if t==0 or t==1 then + l = 1 + d = nil + else + l = self:u8(offset + opt_ptr + 1) + if l > 2 then + d = self:raw(offset + opt_ptr + 2, l-2) + end + end + options[op].len = l + options[op].data = d + opt_ptr = opt_ptr + l + op = op + 1 + end + return options +end +function Tcp:toip(raw_ip_addr) + return string.format("%i.%i.%i.%i", string.byte(raw_ip_addr,1,4)) +end + +function Tcp:parse_ip() + self.ip_offset = 0 + if string.len(self.buf) < 20 then -- too short + return false + end + self.ip_v = bit.rshift(bit.band(self:u8(self.ip_offset + 0), 0xF0), 4) + self.ip_hl = bit.band(self:u8(self.ip_offset + 0), 0x0F) -- header_length or data_offset + if self.ip_v ~= 4 then -- not ip + return false + end + self.ip_tos = self:u8(self.ip_offset + 1) + self.ip_len = self:u16(self.ip_offset + 2) + self.ip_id = self:u16(self.ip_offset + 4) + self.ip_off = self:u16(self.ip_offset + 6) + self.ip_rf = bit.band(self.ip_off, 0x8000)~=0 -- true/false + self.ip_df = bit.band(self.ip_off, 0x4000)~=0 + self.ip_mf = bit.band(self.ip_off, 0x2000)~=0 + self.ip_off = bit.band(self.ip_off, 0x1FFF) -- fragment offset + self.ip_ttl = self:u8(self.ip_offset + 8) + self.ip_p = self:u8(self.ip_offset + 9) + self.ip_sum = self:u16(self.ip_offset + 10) + self.ip_bin_src = self:raw(self.ip_offset + 12,4) -- raw 4-bytes string + self.ip_bin_dst = self:raw(self.ip_offset + 16,4) + self.ip_src = self:toip(self.ip_bin_src) -- formatted string + self.ip_dst = self:toip(self.ip_bin_dst) + self.ip_opt_offset = self.ip_offset + 20 + self.ip_options = self:parse_options(self.ip_opt_offset, ((self.ip_hl*4)-20)) + self.ip_data_offset = self.ip_offset + self.ip_hl*4 + return true +end +function Tcp:tostring() + return string.format( + "%s:%i -> %s:%i", + self.ip_src, self.tcp_sport, + self.ip_dst, self.tcp_dport + ) +end +function Tcp:to_p0f() + return string.format( + "%s:%i:%i:%i:%s:%s:?:?|link: %s, %s%s%s", + self:to_p0f_window_size(), + self.ip_ttl, + (self.ip_df and "1" or "0"), + self:to_p0f_packet_size(), + self:to_p0f_tcp_options(), + self:to_p0f_quirks(), + self:lookup_link(), + self:to_p0f_uptime(), + self:to_fill(), + self:to_ipid() + ) +end +function tohex(str) + local b = "" + for c in string.gmatch(str, ".") do + b = string.format('%s%02x',b, string.byte(c)) + end + return b +end +function Tcp:to_fill() -- ethernet fill, if packet is smaller than 46 bytes + local l = self.tcp_data_offset + self.tcp_data_length + local s = self.packet_len + if l < s then + return string.format(", fill:%s", tohex( string.sub(self.buf, l+1) )) + end + return "" +end +function Tcp:to_ipid() -- ipid number + if self.ip_id > 0 then + return string.format(", ipid:%i", self.ip_id) + end + return "" +end + +function Tcp:to_p0f_uptime() + if self.tcp_opt_t1 == nil then + return "up: disabled" + end + return string.format("up: %i hrs", self.tcp_opt_t1/360000) +end +function Tcp:to_p0f_window_size() + local win = self.tcp_win + if self.tcp_opt_mss ~= nil and + win % self.tcp_opt_mss == 0 then + return string.format("S%i", win/self.tcp_opt_mss) -- not floating -> no reminder + end + if self.tcp_opt_mtu ~= nil and + win % self.tcp_opt_mtu == 0 then + return string.format("T%i", win/self.tcp_opt_mtu) -- not floating -> no reminder + end + return string.format("%i", win) +end +function Tcp:to_p0f_packet_size() + if self.ip_len < 100 then + return string.format("%i", self.ip_len) + end + -- bigger than 99 + return "*" +end +function Tcp:to_p0f_tcp_options() + local opt_chr = { + [0] = "E", + [1] = "N", + [2] = "M", + [3] = "W", + [4] = "S", + [8] = "T" + } + local str = "" + for k,opt in ipairs(self.tcp_options) do + if opt_chr[opt.type] ~= nil then + str = str .. opt_chr[opt.type] + else + str = str .. string.format("?%i", opt.type) + end + + if opt.type == 2 then + str = str .. string.format("%i", u16(opt.data, 0)) + elseif opt.type == 3 then + str = str .. string.format("%i", u8(opt.data, 0)) + elseif opt.type == 8 then + local t = u32(opt.data, 0) + if t == 0 then + str = str .. "0" + end + end + str = str .. "," + end + if string.len(str) == 0 then + return "." + end + return string.sub(str, 0, string.len(str)-1) +end +function Tcp:tcp_parse_options() + local eoo = false + for _,opt in ipairs(self.tcp_options) do + if eoo then + self.tcp_opt_after_eol = true + end + + if opt.type == 0 then -- end of options + eoo = true + elseif opt.type == 2 then -- MSS + self.tcp_opt_mss = u16(opt.data, 0) + self.tcp_opt_mtu = self.tcp_opt_mss + 40 + elseif opt.type == 3 then -- widow scaling + self.tcp_opt_ws = u8(opt.data, 0) + elseif opt.type == 8 then -- timestamp + self.tcp_opt_t1 = u32(opt.data, 0) + self.tcp_opt_t2 = u32(opt.data, 4) + end + end +end +function Tcp:to_p0f_quirks() + local str = "" + + -- P + if self.tcp_opt_after_eol then + str = str .. "P" + end + + -- Z + if self.ip_id == 0 then + str = str .. "Z" + end + + -- I + if self.ip_options[1] ~= nil then + str = str .. "I" + end + + -- U + if self.tcp_urp ~= 0 then + str = str .. "U" + end + + -- X + if self.tcp_x2 ~= 0 then + str = str .. "X" + end + + -- A + if self.tcp_ack ~= 0 then + str = str .. "A" + end + + -- T + if self.tcp_opt_t2 ~= nil and self.tcp_opt_t2 > 0 then +--[[ io.write(string.format("%s:%i->%i t1=%i t2=%i\n", + self.ip_src, + self.tcp_sport, + self.tcp_dport, + self.tcp_opt_t1, + self.tcp_opt_t2)) +]]-- + str = str .. "T" + end + + -- D + if self.tcp_data_length > 0 then + str = str .. "D" + end + + -- F + if self.tcp_th_urg or self.tcp_th_fin then -- no push? + str = str .. "F" + end + + -- not implemented option -> "!" + + if string.len(str) == 0 then + return "." + end + return str +end + +function Tcp:parse_tcp() + self.tcp_offset = self.ip_data_offset + if string.len(self.buf) < self.tcp_offset + 20 then + return false + end + self.tcp_sport = self:u16(self.tcp_offset + 0) + self.tcp_dport = self:u16(self.tcp_offset + 2) + self.tcp_seq = self:u32(self.tcp_offset + 4) + self.tcp_ack = self:u32(self.tcp_offset + 8) + self.tcp_hl = bit.rshift(bit.band(self:u8(self.tcp_offset+12), 0xF0), 4) -- header_length or data_offset + self.tcp_x2 = bit.band(self:u8(self.tcp_offset+12), 0x0F) + self.tcp_flags = self:u8(self.tcp_offset + 13) + self.tcp_th_fin = bit.band(self.tcp_flags, 0x01)~=0 -- true/false + self.tcp_th_syn = bit.band(self.tcp_flags, 0x02)~=0 + self.tcp_th_rst = bit.band(self.tcp_flags, 0x04)~=0 + self.tcp_th_push = bit.band(self.tcp_flags, 0x08)~=0 + self.tcp_th_ack = bit.band(self.tcp_flags, 0x10)~=0 + self.tcp_th_urg = bit.band(self.tcp_flags, 0x20)~=0 + self.tcp_th_ece = bit.band(self.tcp_flags, 0x40)~=0 + self.tcp_th_cwr = bit.band(self.tcp_flags, 0x80)~=0 + self.tcp_win = self:u16(self.tcp_offset + 14) + self.tcp_sum = self:u16(self.tcp_offset + 16) + self.tcp_urp = self:u16(self.tcp_offset + 18) + self.tcp_opt_offset = self.tcp_offset + 20 + self.tcp_options = self:parse_options(self.tcp_opt_offset, ((self.tcp_hl*4)-20)) + self.tcp_data_offset = self.tcp_offset + self.tcp_hl*4 + self.tcp_data_length = self.ip_len - self.tcp_offset - self.tcp_hl*4 + self:tcp_parse_options() + return true +end + +function Tcp:lookup_link() + local mtu_def = { + {["mtu"]=256, ["txt"]= "radio modem"}, + {["mtu"]=386, ["txt"]= "ethernut"}, + {["mtu"]=552, ["txt"]= "SLIP line / encap ppp"}, + {["mtu"]=576, ["txt"]= "sometimes modem"}, + {["mtu"]=1280, ["txt"]= "gif tunnel"}, + {["mtu"]=1300, ["txt"]= "PIX, SMC, sometimes wireless"}, + {["mtu"]=1362, ["txt"]= "sometimes DSL (1)"}, + {["mtu"]=1372, ["txt"]= "cable modem"}, + {["mtu"]=1400, ["txt"]= "(Google/AOL)"}, + {["mtu"]=1415, ["txt"]= "sometimes wireless"}, + {["mtu"]=1420, ["txt"]= "GPRS, T1, FreeS/WAN"}, + {["mtu"]=1423, ["txt"]= "sometimes cable"}, + {["mtu"]=1440, ["txt"]= "sometimes DSL (2)"}, + {["mtu"]=1442, ["txt"]= "IPIP tunnel"}, + {["mtu"]=1450, ["txt"]= "vtun"}, + {["mtu"]=1452, ["txt"]= "sometimes DSL (3)"}, + {["mtu"]=1454, ["txt"]= "sometimes DSL (4)"}, + {["mtu"]=1456, ["txt"]= "ISDN ppp"}, + {["mtu"]=1458, ["txt"]= "BT DSL (?)"}, + {["mtu"]=1462, ["txt"]= "sometimes DSL (5)"}, + {["mtu"]=1470, ["txt"]= "(Google 2)"}, + {["mtu"]=1476, ["txt"]= "IPSec/GRE"}, + {["mtu"]=1480, ["txt"]= "IPv6/IPIP"}, + {["mtu"]=1492, ["txt"]= "pppoe (DSL)"}, + {["mtu"]=1496, ["txt"]= "vLAN"}, + {["mtu"]=1500, ["txt"]= "ethernet/modem"}, + {["mtu"]=1656, ["txt"]= "Ericsson HIS"}, + {["mtu"]=2024, ["txt"]= "wireless/IrDA"}, + {["mtu"]=2048, ["txt"]= "Cyclom X.25 WAN"}, + {["mtu"]=2250, ["txt"]= "AiroNet wireless"}, + {["mtu"]=3924, ["txt"]= "loopback"}, + {["mtu"]=4056, ["txt"]= "token ring (1)"}, + {["mtu"]=4096, ["txt"]= "Sangoma X.25 WAN"}, + {["mtu"]=4352, ["txt"]= "FDDI"}, + {["mtu"]=4500, ["txt"]= "token ring (2)"}, + {["mtu"]=9180, ["txt"]= "FORE ATM"}, + {["mtu"]=16384, ["txt"]= "sometimes loopback (1)"}, + {["mtu"]=16436, ["txt"]= "sometimes loopback (2)"}, + {["mtu"]=18000, ["txt"]= "token ring x4"}, + } + if not self.tcp_opt_mss or self.tcp_opt_mss==0 then + return "unspecified" + end + for _,x in ipairs(mtu_def) do + local mtu = x["mtu"] + local txt = x["txt"] + if self.tcp_opt_mtu == mtu then + return txt + end + if self.tcp_opt_mtu < mtu then + return string.format("unknown-%i", self.tcp_opt_mtu) + end + end + return string.format("unknown-%i", self.tcp_opt_mtu) +end + +---------------------------------------------------------------------------------------------------------------- +function split(str, regexp) + local tab = {} + local i = 0 + for x in string.gfind(str, regexp) do + i = i+1 + tab[i] = x + end + return tab +end +function join(delim, arr, idx) + local i = idx + local o = "" + while arr[i] ~= nil do + if o ~= "" then + o = o .. delim + end + o = o .. arr[i] + i = i + 1 + end + return o +end +function sig_unpack(signature) + local s = {} + local tab = split(signature, "[^:]+") + s.wwww = tab[1] + s.ttt = tonumber(tab[2]) + s.D = tab[3] + s.ss = tab[4] + s.OOO = split(tab[5], "[^,]+") + s.QQ = tab[6] -- split(tab[6], ".") + s.OS = tab[7] + s.details = join(":" ,tab, 8) + s.full = signature + return s +end +function sig_getindex_OOO(t) + local s = "" + for k,v in ipairs(t) do + local t = string.find(v, "^[NEST?]") -- no W and M + if t ~= nil then + s = s .. v .. "," + end + end + return s +end +function sig_getindex(s) + return string.format("%s|%s|%s|%s", s.D, s.ss, sig_getindex_OOO(s.OOO), s.QQ ) +end + +function sigs_load(filename) + local sigs = {} + local f = io.open("scripts/" .. filename, "r") + if f == nil then + io.write("P0F file NOT FOUND " .. filename .. "\n") + end +-- io.write("Loading p0f signature file\n") + while true do + local line = f:read("*line") + if line == nil then break end + if string.find(line, "^#") == nil and + string.find(line, ":") ~= nil then + -- io.write(line .. "!\n") + local sig = sig_unpack(line) + local idx = sig_getindex(sig) + if sigs[idx] == nil then + sigs[idx] = {} + end + table.insert(sigs[idx], sig) + -- io.write(string.format("%s\t%s\n", idx , line)) + end + + end + f:close() + return sigs +end +function test_opt(a, b) -- a=['Xnnn', 'X%nnn', 'X*'] b=[Xnnn] + if a == b then + return true + end + if string.len(a)<2 then + return false + end + local a1 = string.sub(a, 1, 1) + local b1 = string.sub(b, 1, 1) + local a2 = string.sub(a, 2, 2) + local b2 = string.sub(b, 2, 2) + local a2s = string.sub(a, 2) + local b2s = string.sub(b, 2) -- can't be % + local a3s = string.sub(a, 3) + local b3s = string.sub(b, 3) + if a1 ~= b1 then + return false + end + if a2s == "*" then + return true + end + if a2 == "%" then + if tonumber(b2s) % tonumber(a3s) == 0 then + return true + else + return false + end + end + return false +end + +function sig_compare(t, s) + if test_opt(t.wwww, s.wwww)~= true then + return false, string.format("wwww %s!=%s", t.wwww, s.wwww) + end + if t.ttt < s.ttt or + s.ttt < t.ttt-40 then + return false, string.format("ttl %i>=%i>=%i", t.ttt, s.ttt, t.ttt-40) + end + if t.D ~= s.D then + return false, string.format("D %s!=%s", t.D, s.D) + end + if t.ss ~= s.ss then + return false, string.format("ss %s!=%s", t.ss, s.ss) + end + if t.QQ ~= s.QQ then + return false, string.format("QQ %s!=%s", t.QQ, s.QQ) + end + + return sig_compare_OOO(t.OOO, s.OOO) +end + +function sig_compare_OOO(to, so) + for k,v in ipairs(to) do + local a = v + local b = so[k] + if test_opt(a,b) ~= true then + return false, string.format("Opt %s!=%s", a, b) + end + end + return true +end + +function sig_test(sigs, tsignature) + local ts = sig_unpack(tsignature) + local idx = sig_getindex(ts) + local t = split(tsignature, "[^|]+") + if sigs[idx] == nil then + return string.format("UNKNOWN [%s] (%s)", t[1], t[2]) + end + local out = {} + for k,sig in ipairs(sigs[idx]) do + local t, err = sig_compare(sig, ts) + if t == true then + table.insert(out, sig) +-- else +-- io.write(string.format("%s\t\t%s\n", sig.full, err)) -- match debugging + end + end + + if out[1] == nil then + return string.format("UNKNOWN [%s] (%s)", t[1], t[2]) + end + + local s = "" + local dist + for k, sig in ipairs(out) do + dist = tonumber(sig.ttt) - tonumber(ts.ttt) + s = s .. string.format("%s %s|", sig.OS, sig.details) + end + s = string.sub(s, 1, string.len(s)-1) + return string.format("%s (distance: %i, %s)", s, dist, t[2]) +end + +---------------------------------------------------------------------------------------------------------------- +portrule = function(host, port) + return true +end + + +-- index is on target_ip, target_port +-- packet is from target (so target is source) +callback = function(packetsz, layer2, layer3) + local tcp = Tcp:new(layer3, packetsz-string.len(layer2)) + local a = tcp.ip_bin_src .. string.format("%i", tcp.tcp_sport) + return a +end + +function make_index(target_ip, target_port) + local a = target_ip .. string.format("%i", target_port) + return a +end + + +action = function(host, port) + local pcap = nmap.new_socket() + local conn = nmap.new_socket() + local status, packetsz, layer2, layer3 + local sigs = nmap.registry["p0f_sigs"] + local _, o, tcp, localport + if sigs == nil then + sigs = sigs_load("p0fa.fp") + nmap.registry["p0f_sigs"] = sigs + end + + -- index is on source_ip, target_ip, target_port + pcap:pcap_open(host.interface, 256, 0, callback, + "tcp and (tcp[tcpflags] & tcp-ack) != 0 and (tcp[tcpflags] & tcp-syn) != 0") + pcap:set_timeout(5000) -- 5 seconds? enough? + + + pcap:pcap_register( make_index(host.bin_ip, port.number) ) + + conn:connect(host.ip, port.number) + status , packetsz, layer2, layer3 = pcap:pcap_receive() + if status then + tcp = Tcp:new(layer3, packetsz-string.len(layer2)) + o = sig_test(sigs, tcp:to_p0f()) + else -- timeouted + o = "Failed to capture SYN+ACK" + end + + conn:close() + return o +end diff -NraupbwB soc07-5184/scripts/promiscuous.nse soc07-nse-pcap/scripts/promiscuous.nse --- soc07-5184/scripts/promiscuous.nse 1970-01-01 01:00:00.000000000 +0100 +++ soc07-nse-pcap/scripts/promiscuous.nse 2007-07-11 23:06:41.000000000 +0200 @@ -0,0 +1,134 @@ +id = "Promiscuous detection" +description = "Checks if hosts on local ethernet have network card in promiscuous mode." +author = "Marek Majkowski " +license = "See nmaps COPYING for licence" + +--[[ +This script tries to guess if node in local ethernet is in promisucous mode. + +The technique is described here: + http://www.securityfriday.com/promiscuous_detection_01.pdf + +]]-- + +categories = {"safe"} -- intrusive? + +-- okay, we're interested only in hosts that are on our ethernet lan +hostrule = function(host, port) + if host.directly_connected == true and + host.mac_addr ~= nil and + host.mac_addr_src ~= nil and + host.interface ~= nil and + nmap.get_interface_link(host.interface) == 'ethernet' then + return true + end + return false +end + +--[[ +function tohex(str) + local b = "" + for c in string.gmatch(str, ".") do + b = string.format('%s%02x',b, string.byte(c)) + end + return b +end +]]-- + +callback = function(packetsz, layer2, layer3) + return string.sub(layer2, 0, 12) +end + + +do_test = function(dnet, pcap, host, test) + local _ + local status + local i = 0 + + -- ARP requests are send with timeouts: 10ms, 40ms, 90ms + -- before each try, we wait at least 100ms + -- in summary, this test takes at least 100ms and at most 440ms + for i=1,3 do + -- flush buffers :), wait quite long. + repeat + pcap:set_timeout(100) + pcap:pcap_register(host.mac_addr_src .. host.mac_addr) + status ,_,_,_ = pcap:pcap_receive() + until status ~= true + pcap:set_timeout(10 * i*i) + pcap:pcap_register(host.mac_addr_src .. host.mac_addr) + + dnet:ethernet_send(test) + + status ,_,_,_ = pcap:pcap_receive() + if status == true then + -- the basic idea, was to inform user about time, when we got packet + -- so that 1 would mean (0-10ms), 2=(10-40ms) and 3=(40ms-90ms) + -- but when we're running this tests on macs, first test is always 2. + -- which means that the first answer is dropped. + -- for now, just return 1 if test was successfull, it's easier + -- return(i) + return(1) + end + end + return('_') +end + +action = function(host, port) + local dnet = nmap.new_dnet() + local pcap = nmap.new_socket() + local _ + local status + local results = { + ['1_____1_'] = false, -- MacOSX(Tiger.Panther)/Linux/ ?Win98/ WinXP sp2(no pcap) + ['1_______'] = false, -- Old Apple/SunOS/3Com + ['1___1_1_'] = false, -- MacOSX(Tiger) + ['11111111'] = true, -- BSD/Linux/OSX/ (or not promiscous openwrt ) + ['1_1___1_'] = false, -- WinXP sp2 + pcap|| win98 sniff || win2k sniff (see below) + ['111___1_'] = true, -- WinXP sp2 promisc +-- ['1111__1_'] = true, -- ?Win98 promisc + ??win98 no promisc *not confirmed* + } + dnet:ethernet_open(host.interface) + + pcap:pcap_open(host.interface, 64, 0, callback, "arp") + + local test_static = host.mac_addr_src .. + string.char(0x08,0x06, 0x00,0x01, 0x08,0x00, 0x06,0x04, 0x00,0x01) .. + host.mac_addr_src .. + host.bin_ip_src .. + string.char(0x00,0x00, 0x00,0x00, 0x00,0x00) .. + host.bin_ip + local t = { + string.char(0xff,0xff, 0xff,0xff, 0xff,0xff), -- B32 no meaning? + string.char(0xff,0xff, 0xff,0xff, 0xff,0xfe), -- B31 + string.char(0xff,0xff, 0x00,0x00, 0x00,0x00), -- B16 + string.char(0xff,0x00, 0x00,0x00, 0x00,0x00), -- B8 + string.char(0x01,0x00, 0x00,0x00, 0x00,0x00), -- G + string.char(0x01,0x00, 0x5e,0x00, 0x00,0x00), -- M0 + string.char(0x01,0x00, 0x5e,0x00, 0x00,0x01), -- M1 no meaning? + string.char(0x01,0x00, 0x5e,0x00, 0x00,0x03), -- M3 + } + local v + local out = "" + for _, v in ipairs(t) do + out = out .. do_test(dnet, pcap, host, v .. test_static) + end + + dnet:ethernet_close() + pcap:pcap_close() + + if out == '1_1___1_' then + return 'Win98/Win2K/WinXP with pcap installed. I\'m unsure if they\'re sniffing. (tests: "' .. out .. '")' + end + if results[out] == false then + -- probably not sniffing + return + end + if results[out] == true then + -- rather sniffer. + return 'PROMISCUOUS (tests: "' .. out .. '")' + end + + -- results[out] == nil + return 'Unknown (tests: "' .. out .. '")' +end diff -NraupbwB soc07-5184/Target.cc soc07-nse-pcap/Target.cc --- soc07-5184/Target.cc 2007-07-10 18:15:14.000000000 +0200 +++ soc07-nse-pcap/Target.cc 2007-07-11 23:06:41.000000000 +0200 @@ -349,6 +349,10 @@ void Target::setDirectlyConnected(bool c directly_connected = connected? 1 : 0; } +int Target::directlyConnectedOrUnset(){ + return directly_connected; +} + bool Target::directlyConnected() { assert(directly_connected == 0 || directly_connected == 1); return directly_connected; diff -NraupbwB soc07-5184/Target.cc.orig soc07-nse-pcap/Target.cc.orig --- soc07-5184/Target.cc.orig 1970-01-01 01:00:00.000000000 +0100 +++ soc07-nse-pcap/Target.cc.orig 2007-07-10 18:15:14.000000000 +0200 @@ -0,0 +1,458 @@ + +/*************************************************************************** + * Target.cc -- The Target class encapsulates much of the information Nmap * + * has about a host. Results (such as ping, OS scan, etc) are stored in * + * this class as they are determined. * + * * + ***********************IMPORTANT NMAP LICENSE TERMS************************ + * * + * The Nmap Security Scanner is (C) 1996-2006 Insecure.Com LLC. Nmap is * + * also a registered trademark of Insecure.Com LLC. This program is free * + * software; you may redistribute and/or modify it under the terms of the * + * GNU General Public License as published by the Free Software * + * Foundation; Version 2 with the clarifications and exceptions described * + * below. This guarantees your right to use, modify, and redistribute * + * this software under certain conditions. If you wish to embed Nmap * + * technology into proprietary software, we sell alternative licenses * + * (contact sales@insecure.com). Dozens of software vendors already * + * license Nmap technology such as host discovery, port scanning, OS * + * detection, and version detection. * + * * + * Note that the GPL places important restrictions on "derived works", yet * + * it does not provide a detailed definition of that term. To avoid * + * misunderstandings, we consider an application to constitute a * + * "derivative work" for the purpose of this license if it does any of the * + * following: * + * o Integrates source code from Nmap * + * o Reads or includes Nmap copyrighted data files, such as * + * nmap-os-fingerprints or nmap-service-probes. * + * o Executes Nmap and parses the results (as opposed to typical shell or * + * execution-menu apps, which simply display raw Nmap output and so are * + * not derivative works.) * + * o Integrates/includes/aggregates Nmap into a proprietary executable * + * installer, such as those produced by InstallShield. * + * o Links to a library or executes a program that does any of the above * + * * + * The term "Nmap" should be taken to also include any portions or derived * + * works of Nmap. This list is not exclusive, but is just meant to * + * clarify our interpretation of derived works with some common examples. * + * These restrictions only apply when you actually redistribute Nmap. For * + * example, nothing stops you from writing and selling a proprietary * + * front-end to Nmap. Just distribute it by itself, and point people to * + * http://insecure.org/nmap/ to download Nmap. * + * * + * We don't consider these to be added restrictions on top of the GPL, but * + * just a clarification of how we interpret "derived works" as it applies * + * to our GPL-licensed Nmap product. This is similar to the way Linus * + * Torvalds has announced his interpretation of how "derived works" * + * applies to Linux kernel modules. Our interpretation refers only to * + * Nmap - we don't speak for any other GPL products. * + * * + * If you have any questions about the GPL licensing restrictions on using * + * Nmap in non-GPL works, we would be happy to help. As mentioned above, * + * we also offer alternative license to integrate Nmap into proprietary * + * applications and appliances. These contracts have been sold to dozens * + * of software vendors, and generally include a perpetual license as well * + * as providing for priority support and updates as well as helping to * + * fund the continued development of Nmap technology. Please email * + * sales@insecure.com for further information. * + * * + * As a special exception to the GPL terms, Insecure.Com LLC grants * + * permission to link the code of this program with any version of the * + * OpenSSL library which is distributed under a license identical to that * + * listed in the included Copying.OpenSSL file, and distribute linked * + * combinations including the two. You must obey the GNU GPL in all * + * respects for all of the code used other than OpenSSL. If you modify * + * this file, you may extend this exception to your version of the file, * + * but you are not obligated to do so. * + * * + * If you received these files with a written license agreement or * + * contract stating terms other than the terms above, then that * + * alternative license agreement takes precedence over these comments. * + * * + * Source is provided to this software because we believe users have a * + * right to know exactly what a program is going to do before they run it. * + * This also allows you to audit the software for security holes (none * + * have been found so far). * + * * + * Source code also allows you to port Nmap to new platforms, fix bugs, * + * and add new features. You are highly encouraged to send your changes * + * to fyodor@insecure.org for possible incorporation into the main * + * distribution. By sending these changes to Fyodor or one the * + * Insecure.Org development mailing lists, it is assumed that you are * + * offering Fyodor and Insecure.Com LLC the unlimited, non-exclusive right * + * to reuse, modify, and relicense the code. Nmap will always be * + * available Open Source, but this is important because the inability to * + * relicense code has caused devastating problems for other Free Software * + * projects (such as KDE and NASM). We also occasionally relicense the * + * code to third parties as discussed above. If you wish to specify * + * special license conditions of your contributions, just say so when you * + * send them. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details at * + * http://www.gnu.org/copyleft/gpl.html , or in the COPYING file included * + * with Nmap. * + * * + ***************************************************************************/ + +/* $Id: Target.cc 4822 2007-06-02 02:16:34Z david $ */ + +#ifdef WIN32 +#include "nmap_winconfig.h" +#endif + +#include "Target.h" +#include +#include "nbase.h" +#include "NmapOps.h" +#include "utils.h" +#include "nmap_error.h" + +extern NmapOps o; + +Target::Target() { + Initialize(); +} + +void Target::Initialize() { + hostname = NULL; + memset(&seq, 0, sizeof(seq)); + distance = -1; + FPR1 = NULL; + FPR = NULL; + osscan_flag = OS_NOTPERF; + wierd_responses = flags = 0; + memset(&to, 0, sizeof(to)); + memset(&targetsock, 0, sizeof(targetsock)); + memset(&sourcesock, 0, sizeof(sourcesock)); + memset(&nexthopsock, 0, sizeof(nexthopsock)); + targetsocklen = sourcesocklen = nexthopsocklen = 0; + directly_connected = -1; + targetipstring[0] = '\0'; + nameIPBuf = NULL; + memset(&MACaddress, 0, sizeof(MACaddress)); + memset(&SrcMACaddress, 0, sizeof(SrcMACaddress)); + memset(&NextHopMACaddress, 0, sizeof(NextHopMACaddress)); + MACaddress_set = SrcMACaddress_set = NextHopMACaddress_set = false; + htn.msecs_used = 0; + htn.toclock_running = false; + interface_type = devt_other; + devname[0] = '\0'; + devfullname[0] = '\0'; + state_reason_init(&reason); +} + + +const char * Target::deviceName() { + return (devname[0] != '\0')? devname : NULL; +} + +const char * Target::deviceFullName() { + return (devfullname[0] != '\0')? devfullname : NULL; +} + +void Target::Recycle() { + FreeInternal(); + Initialize(); +} + +Target::~Target() { + FreeInternal(); +} + +void Target::FreeInternal() { + + /* Free the DNS name if we resolved one */ + if (hostname) + free(hostname); + + if (nameIPBuf) { + free(nameIPBuf); + nameIPBuf = NULL; + } + + if (FPR1) delete FPR1; + if (FPR) delete FPR; +} + +/* Creates a "presentation" formatted string out of the IPv4/IPv6 address. + Called when the IP changes */ +void Target::GenerateIPString() { + struct sockaddr_in *sin = (struct sockaddr_in *) &targetsock; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &targetsock; + + if (inet_ntop(sin->sin_family, (sin->sin_family == AF_INET)? + (char *) &sin->sin_addr : +#if HAVE_IPV6 + (char *) &sin6->sin6_addr, +#else + (char *) NULL, +#endif + targetipstring, sizeof(targetipstring)) == NULL) { + fatal("Failed to convert target address to presentation format!?! Error: %s", strerror(socket_errno())); + } +} + +/* Fills a sockaddr_storage with the AF_INET or AF_INET6 address + information of the target. This is a preferred way to get the + address since it is portable for IPv6 hosts. Returns 0 for + success. ss_len must be provided. It is not examined, but is set + to the size of the sockaddr copied in. */ +int Target::TargetSockAddr(struct sockaddr_storage *ss, size_t *ss_len) { + assert(ss); + assert(ss_len); + if (targetsocklen <= 0) + return 1; + assert(targetsocklen <= sizeof(*ss)); + memcpy(ss, &targetsock, targetsocklen); + *ss_len = targetsocklen; + return 0; +} + +/* Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted + to sockaddr_storage */ +void Target::setTargetSockAddr(struct sockaddr_storage *ss, size_t ss_len) { + + assert(ss_len > 0 && ss_len <= sizeof(*ss)); + if (targetsocklen > 0) { + /* We had an old target sock, so we better blow away the hostname as + this one may be new. */ + setHostName(NULL); + } + memcpy(&targetsock, ss, ss_len); + targetsocklen = ss_len; + GenerateIPString(); + /* The ports array needs to know a name too */ + ports.setIdStr(targetipstr()); +} + +// Returns IPv4 host address or {0} if unavailable. +struct in_addr Target::v4host() { + const struct in_addr *addy = v4hostip(); + struct in_addr in; + if (addy) return *addy; + in.s_addr = 0; + return in; +} + +// Returns IPv4 host address or NULL if unavailable. +const struct in_addr *Target::v4hostip() { + struct sockaddr_in *sin = (struct sockaddr_in *) &targetsock; + if (sin->sin_family == AF_INET) { + return &(sin->sin_addr); + } + return NULL; +} + + /* The source address used to reach the target */ +int Target::SourceSockAddr(struct sockaddr_storage *ss, size_t *ss_len) { + if (sourcesocklen <= 0) + return 1; + assert(sourcesocklen <= sizeof(*ss)); + if (ss) + memcpy(ss, &sourcesock, sourcesocklen); + if (ss_len) + *ss_len = sourcesocklen; + return 0; +} + +/* Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted + to sockaddr_storage */ +void Target::setSourceSockAddr(struct sockaddr_storage *ss, size_t ss_len) { + assert(ss_len > 0 && ss_len <= sizeof(*ss)); + memcpy(&sourcesock, ss, ss_len); + sourcesocklen = ss_len; +} + +// Returns IPv4 host address or {0} if unavailable. +struct in_addr Target::v4source() { + const struct in_addr *addy = v4sourceip(); + struct in_addr in; + if (addy) return *addy; + in.s_addr = 0; + return in; +} + +// Returns IPv4 host address or NULL if unavailable. +const struct in_addr *Target::v4sourceip() { + struct sockaddr_in *sin = (struct sockaddr_in *) &sourcesock; + if (sin->sin_family == AF_INET) { + return &(sin->sin_addr); + } + return NULL; +} + + + /* You can set to NULL to erase a name or if it failed to resolve -- or + just don't call this if it fails to resolve */ +void Target::setHostName(char *name) { + char *p; + if (hostname) { + free(hostname); + hostname = NULL; + } + if (name) { + p = hostname = strdup(name); + while (*p) { + // I think only a-z A-Z 0-9 . and - are allowed, but I'll be a little more + // generous. + if (!isalnum(*p) && !strchr(".-+=:_~*", *p)) { + log_write(LOG_STDOUT, "Illegal character(s) in hostname -- replacing with '*'\n"); + *p = '*'; + } + p++; + } + } +} + + /* Generates a printable string consisting of the host's IP + address and hostname (if available). Eg "www.insecure.org + (64.71.184.53)" or "fe80::202:e3ff:fe14:1102". The name is + written into the buffer provided, which is also returned. Results + that do not fit in buflen will be truncated. */ +const char *Target::NameIP(char *buf, size_t buflen) { + assert(buf); + assert(buflen > 8); + if (hostname) { + snprintf(buf, buflen, "%s (%s)", hostname, targetipstring); + } else Strncpy(buf, targetipstring, buflen); + return buf; +} + +/* This next version returns a static buffer -- so no concurrency */ +const char *Target::NameIP() { + if (!nameIPBuf) nameIPBuf = (char *) safe_malloc(MAXHOSTNAMELEN + INET6_ADDRSTRLEN); + return NameIP(nameIPBuf, MAXHOSTNAMELEN + INET6_ADDRSTRLEN); +} + + /* Returns the next hop for sending packets to this host. Returns true if + next_hop was filled in. It might be false, for example, if + next_hop has never been set */ +bool Target::nextHop(struct sockaddr_storage *next_hop, size_t *next_hop_len) { + if (nexthopsocklen <= 0) + return false; + assert(nexthopsocklen <= sizeof(*next_hop)); + if (next_hop) + memcpy(next_hop, &nexthopsock, nexthopsocklen); + if (next_hop_len) + *next_hop_len = nexthopsocklen; + return true; +} + + /* If the host is directly connected on a network, set and retrieve + that information here. directlyConnected() will abort if it hasn't + been set yet. */ +void Target::setDirectlyConnected(bool connected) { + directly_connected = connected? 1 : 0; +} + +bool Target::directlyConnected() { + assert(directly_connected == 0 || directly_connected == 1); + return directly_connected; +} + +/* Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted + to sockaddr_storage */ +void Target::setNextHop(struct sockaddr_storage *next_hop, size_t next_hop_len) { + assert(next_hop_len > 0 && next_hop_len <= sizeof(nexthopsock)); + memcpy(&nexthopsock, next_hop, next_hop_len); + nexthopsocklen = next_hop_len; +} + + + /* Starts the timeout clock for the host running (e.g. you are + beginning a scan). If you do not have the current time handy, + you can pass in NULL. When done, call stopTimeOutClock (it will + also automatically be stopped of timedOut() returns true) */ +void Target::startTimeOutClock(const struct timeval *now) { + assert(htn.toclock_running == false); + htn.toclock_running = true; + if (now) htn.toclock_start = *now; + else gettimeofday(&htn.toclock_start, NULL); +} + /* The complement to startTimeOutClock. */ +void Target::stopTimeOutClock(const struct timeval *now) { + struct timeval tv; + assert(htn.toclock_running == true); + htn.toclock_running = false; + if (now) tv = *now; + else gettimeofday(&tv, NULL); + htn.msecs_used += TIMEVAL_MSEC_SUBTRACT(tv, htn.toclock_start); +} + /* Returns whether the host is timedout. If the timeoutclock is + running, counts elapsed time for that. Pass NULL if you don't have the + current time handy. You might as well also pass NULL if the + clock is not running, as the func won't need the time. */ +bool Target::timedOut(const struct timeval *now) { + unsigned long used = htn.msecs_used; + struct timeval tv; + + if (!o.host_timeout) return false; + if (htn.toclock_running) { + if (now) tv = *now; + else gettimeofday(&tv, NULL); + used += TIMEVAL_MSEC_SUBTRACT(tv, htn.toclock_start); + } + + return (used > o.host_timeout)? true : false; +} + + +/* Returns zero if MAC address set successfully */ +int Target::setMACAddress(const u8 *addy) { + if (!addy) return 1; + memcpy(MACaddress, addy, 6); + MACaddress_set = 1; + return 0; +} + +int Target::setSrcMACAddress(const u8 *addy) { + if (!addy) return 1; + memcpy(SrcMACaddress, addy, 6); + SrcMACaddress_set = 1; + return 0; +} + +int Target::setNextHopMACAddress(const u8 *addy) { + if (!addy) return 1; + memcpy(NextHopMACaddress, addy, 6); + NextHopMACaddress_set = 1; + return 0; +} + +/* Set the device names so that they can be returned by deviceName() + and deviceFullName(). The normal name may not include alias + qualifier, while the full name may include it (e.g. "eth1:1"). If + these are non-null, they will overwrite the stored version */ +void Target::setDeviceNames(const char *name, const char *fullname) { + if (name) Strncpy(devname, name, sizeof(devname)); + if (fullname) Strncpy(devfullname, fullname, sizeof(devfullname)); +} + +/* Returns the 6-byte long MAC address, or NULL if none has been set */ +const u8 *Target::MACAddress() { + return (MACaddress_set)? MACaddress : NULL; +} + +const u8 *Target::SrcMACAddress() { + return (SrcMACaddress_set)? SrcMACaddress : NULL; +} + +const u8 *Target::NextHopMACAddress() { + return (NextHopMACaddress_set)? NextHopMACaddress : NULL; +} + +int Target::osscanPerformed(void) { + return osscan_flag; +} + +void Target::osscanSetFlag(int flag) { + if(osscan_flag == OS_PERF_UNREL) + return; + else + osscan_flag = flag; +} + diff -NraupbwB soc07-5184/Target.h soc07-nse-pcap/Target.h --- soc07-5184/Target.h 2007-07-10 18:15:14.000000000 +0200 +++ soc07-nse-pcap/Target.h 2007-07-11 23:06:41.000000000 +0200 @@ -181,6 +181,7 @@ class Target { been set yet. */ void setDirectlyConnected(bool connected); bool directlyConnected(); + int directlyConnectedOrUnset(); /* 1-directly connected, 0-no, -1-we don't know*/ /* If the host is NOT directly connected, you can set the next hop value here. It is OK to pass in a sockaddr_in or sockaddr_in6 diff -NraupbwB soc07-5184/Target.h.orig soc07-nse-pcap/Target.h.orig --- soc07-5184/Target.h.orig 1970-01-01 01:00:00.000000000 +0100 +++ soc07-nse-pcap/Target.h.orig 2007-07-10 18:15:14.000000000 +0200 @@ -0,0 +1,277 @@ + +/*************************************************************************** + * Target.h -- The Target class encapsulates much of the information Nmap * + * has about a host. Results (such as ping, OS scan, etc) are stored in * + * this class as they are determined. * + * * + ***********************IMPORTANT NMAP LICENSE TERMS************************ + * * + * The Nmap Security Scanner is (C) 1996-2006 Insecure.Com LLC. Nmap is * + * also a registered trademark of Insecure.Com LLC. This program is free * + * software; you may redistribute and/or modify it under the terms of the * + * GNU General Public License as published by the Free Software * + * Foundation; Version 2 with the clarifications and exceptions described * + * below. This guarantees your right to use, modify, and redistribute * + * this software under certain conditions. If you wish to embed Nmap * + * technology into proprietary software, we sell alternative licenses * + * (contact sales@insecure.com). Dozens of software vendors already * + * license Nmap technology such as host discovery, port scanning, OS * + * detection, and version detection. * + * * + * Note that the GPL places important restrictions on "derived works", yet * + * it does not provide a detailed definition of that term. To avoid * + * misunderstandings, we consider an application to constitute a * + * "derivative work" for the purpose of this license if it does any of the * + * following: * + * o Integrates source code from Nmap * + * o Reads or includes Nmap copyrighted data files, such as * + * nmap-os-fingerprints or nmap-service-probes. * + * o Executes Nmap and parses the results (as opposed to typical shell or * + * execution-menu apps, which simply display raw Nmap output and so are * + * not derivative works.) * + * o Integrates/includes/aggregates Nmap into a proprietary executable * + * installer, such as those produced by InstallShield. * + * o Links to a library or executes a program that does any of the above * + * * + * The term "Nmap" should be taken to also include any portions or derived * + * works of Nmap. This list is not exclusive, but is just meant to * + * clarify our interpretation of derived works with some common examples. * + * These restrictions only apply when you actually redistribute Nmap. For * + * example, nothing stops you from writing and selling a proprietary * + * front-end to Nmap. Just distribute it by itself, and point people to * + * http://insecure.org/nmap/ to download Nmap. * + * * + * We don't consider these to be added restrictions on top of the GPL, but * + * just a clarification of how we interpret "derived works" as it applies * + * to our GPL-licensed Nmap product. This is similar to the way Linus * + * Torvalds has announced his interpretation of how "derived works" * + * applies to Linux kernel modules. Our interpretation refers only to * + * Nmap - we don't speak for any other GPL products. * + * * + * If you have any questions about the GPL licensing restrictions on using * + * Nmap in non-GPL works, we would be happy to help. As mentioned above, * + * we also offer alternative license to integrate Nmap into proprietary * + * applications and appliances. These contracts have been sold to dozens * + * of software vendors, and generally include a perpetual license as well * + * as providing for priority support and updates as well as helping to * + * fund the continued development of Nmap technology. Please email * + * sales@insecure.com for further information. * + * * + * As a special exception to the GPL terms, Insecure.Com LLC grants * + * permission to link the code of this program with any version of the * + * OpenSSL library which is distributed under a license identical to that * + * listed in the included Copying.OpenSSL file, and distribute linked * + * combinations including the two. You must obey the GNU GPL in all * + * respects for all of the code used other than OpenSSL. If you modify * + * this file, you may extend this exception to your version of the file, * + * but you are not obligated to do so. * + * * + * If you received these files with a written license agreement or * + * contract stating terms other than the terms above, then that * + * alternative license agreement takes precedence over these comments. * + * * + * Source is provided to this software because we believe users have a * + * right to know exactly what a program is going to do before they run it. * + * This also allows you to audit the software for security holes (none * + * have been found so far). * + * * + * Source code also allows you to port Nmap to new platforms, fix bugs, * + * and add new features. You are highly encouraged to send your changes * + * to fyodor@insecure.org for possible incorporation into the main * + * distribution. By sending these changes to Fyodor or one the * + * Insecure.Org development mailing lists, it is assumed that you are * + * offering Fyodor and Insecure.Com LLC the unlimited, non-exclusive right * + * to reuse, modify, and relicense the code. Nmap will always be * + * available Open Source, but this is important because the inability to * + * relicense code has caused devastating problems for other Free Software * + * projects (such as KDE and NASM). We also occasionally relicense the * + * code to third parties as discussed above. If you wish to specify * + * special license conditions of your contributions, just say so when you * + * send them. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details at * + * http://www.gnu.org/copyleft/gpl.html , or in the COPYING file included * + * with Nmap. * + * * + ***************************************************************************/ + +/* $Id: Target.h 4822 2007-06-02 02:16:34Z david $ */ + +#ifndef TARGET_H +#define TARGET_H + +#include "nmap.h" +#include "FingerPrintResults.h" + +#ifndef NOLUA +#include "nse_main.h" +#endif + +#include "reason.h" +#include "portlist.h" +#include "tcpip.h" + +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif + +enum osscan_flags { + OS_NOTPERF=0, OS_PERF, OS_PERF_UNREL +}; + +struct host_timeout_nfo { + unsigned long msecs_used; /* How many msecs has this Target used? */ + bool toclock_running; /* Is the clock running right now? */ + struct timeval toclock_start; /* When did the clock start? */ +}; + +class Target { + public: /* For now ... a lot of the data members should be made private */ + Target(); + ~Target(); + /* Recycles the object by freeing internal objects and reinitializing + to default state */ + void Recycle(); + /* Fills a sockaddr_storage with the AF_INET or AF_INET6 address + information of the target. This is a preferred way to get the + address since it is portable for IPv6 hosts. Returns 0 for + success. ss_len must be provided. It is not examined, but is set + to the size of the sockaddr copied in. */ + int TargetSockAddr(struct sockaddr_storage *ss, size_t *ss_len); + /* Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted + to sockaddr_storage */ + void setTargetSockAddr(struct sockaddr_storage *ss, size_t ss_len); + // Returns IPv4 target host address or {0} if unavailable. + struct in_addr v4host(); + const struct in_addr *v4hostip(); + /* The source address used to reach the target */ + int SourceSockAddr(struct sockaddr_storage *ss, size_t *ss_len); + /* Note that it is OK to pass in a sockaddr_in or sockaddr_in6 casted + to sockaddr_storage */ + void setSourceSockAddr(struct sockaddr_storage *ss, size_t ss_len); + struct in_addr v4source(); + const struct in_addr *v4sourceip(); + /* The IPv4 or IPv6 literal string for the target host */ + const char *targetipstr() { return targetipstring; } + /* Give the name from the last setHostName() call, which should be + the name obtained from reverse-resolution (PTR query) of the IP (v4 + or v6). If the name has not been set, or was set to NULL, an empty + string ("") is returned to make printing easier. */ + const char *HostName() { return hostname? hostname : ""; } + /* You can set to NULL to erase a name or if it failed to resolve -- or + just don't call this if it fails to resolve. The hostname is blown + away when you setTargetSockAddr(), so make sure you do these in proper + order + */ + void setHostName(char *name); + /* Generates a printable string consisting of the host's IP + address and hostname (if available). Eg "www.insecure.org + (64.71.184.53)" or "fe80::202:e3ff:fe14:1102". The name is + written into the buffer provided, which is also returned. Results + that do not fit in buflen will be truncated. */ + const char *NameIP(char *buf, size_t buflen); + /* This next version returns a STATIC buffer -- so no concurrency */ + const char *NameIP(); + + /* If the host is directly connected on a network, set and retrieve + that information here. directlyConnected() will abort if it hasn't + been set yet. */ + void setDirectlyConnected(bool connected); + bool directlyConnected(); + + /* If the host is NOT directly connected, you can set the next hop + value here. It is OK to pass in a sockaddr_in or sockaddr_in6 + casted to sockaddr_storage*/ + void setNextHop(struct sockaddr_storage *next_hop, size_t next_hop_len); + /* Returns the next hop for sending packets to this host. Returns true if + next_hop was filled in. It might be false, for example, if + next_hop has never been set */ + bool nextHop(struct sockaddr_storage *next_hop, size_t *next_hop_len); + + /* Sets the interface type to one of: + devt_ethernet, devt_loopback, devt_p2p, devt_other + */ + void setIfType(devtype iftype) { interface_type = iftype; } + /* Returns -1 if it has not yet been set with setIfType() */ + devtype ifType() { return interface_type; } + /* Starts the timeout clock for the host running (e.g. you are + beginning a scan). If you do not have the current time handy, + you can pass in NULL. When done, call stopTimeOutClock (it will + also automatically be stopped of timedOut() returns true) */ + void startTimeOutClock(const struct timeval *now); + /* The complement to startTimeOutClock. */ + void stopTimeOutClock(const struct timeval *now); + /* Is the timeout clock currently running? */ + bool timeOutClockRunning() { return htn.toclock_running; } + /* Returns whether the host is timedout. If the timeoutclock is + running, counts elapsed time for that. Pass NULL if you don't have the + current time handy. You might as well also pass NULL if the + clock is not running, as the func won't need the time. */ + bool timedOut(const struct timeval *now); + + /* Takes a 6-byte MAC address */ + int setMACAddress(const u8 *addy); + int setSrcMACAddress(const u8 *addy); + int setNextHopMACAddress(const u8 *addy); // this should be the target's own MAC if directlyConnected() + + /* Returns a pointer to 6-byte MAC address, or NULL if none is set */ + const u8 *MACAddress(); + const u8 *SrcMACAddress(); + const u8 *NextHopMACAddress(); + +/* Set the device names so that they can be returned by deviceName() + and deviceFullName(). The normal name may not include alias + qualifier, while the full name may include it (e.g. "eth1:1"). If + these are non-null, they will overwrite the stored version */ + void setDeviceNames(const char *name, const char *fullname); + const char *deviceName(); + const char *deviceFullName(); + + int osscanPerformed(void); + void osscanSetFlag(int flag); + + struct seq_info seq; + int distance; + FingerPrintResults *FPR1; /* FP results get by the old OS scan system. */ + FingerPrintResults *FPR; /* FP results get by the new OS scan system. */ + PortList ports; + + // unsigned int up; + // unsigned int down; + int wierd_responses; /* echo responses from other addresses, Ie a network broadcast address */ + unsigned int flags; /* HOST_UP, HOST_DOWN, HOST_FIREWALLED, HOST_BROADCAST (instead of HOST_BROADCAST use wierd_responses */ + struct timeout_info to; + char *hostname; // Null if unable to resolve or unset + +#ifndef NOLUA + ScriptResults scriptResults; +#endif + + state_reason_t reason; + + private: + void Initialize(); + void FreeInternal(); // Free memory allocated inside this object + // Creates a "presentation" formatted string out of the IPv4/IPv6 address + void GenerateIPString(); + struct sockaddr_storage targetsock, sourcesock, nexthopsock; + size_t targetsocklen, sourcesocklen, nexthopsocklen; + int directly_connected; // -1 = unset; 0 = no; 1 = yes + char targetipstring[INET6_ADDRSTRLEN]; + char *nameIPBuf; /* for the NameIP(void) function to return */ + u8 MACaddress[6], SrcMACaddress[6], NextHopMACaddress[6]; + bool MACaddress_set, SrcMACaddress_set, NextHopMACaddress_set; + struct host_timeout_nfo htn; + devtype interface_type; + char devname[32]; + char devfullname[32]; + /* 0 (OS_NOTPERF) if os detection not performed + * 1 (OS_PERF) if os detection performed + * 2 (OS_PERF_UNREL) if an unreliable os detection has been performed */ + int osscan_flag; +}; + +#endif /* TARGET_H */