strrchr与strchr函数 find_first_of与find_last_of

本文深入探讨了C语言中的字符串查找函数及其使用,并与C++中的string类进行对比,详细展示了如何利用这些函数进行字符串处理,包括反向查找、正向查找、替换字符等操作。

这两个是字符串的查找函数:
char buf[1024] = “Hello world\t12”;
char sep1 = ‘\t’, sep2 = ’ ‘;
char *p;
p = strrchr(buf, sep1);
表示在字符串buf中反向查找sep1字符,若找到,则返回指向该字符的指针;若没有找到,则返回NULL
p = strrchr(buf, sep2);
表示在字符串buf中正向查找sep2字符,若找到,则返回指向该字符的指针;若没有找到,则返回NULL
直接上代码吧,

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
//#define N 10240
#define LEN 10240

int main()
{
    char buf[LEN] = "test string\t12"; // the problem of char '\t'
    char sep = '\t';

    char *p;
    char buf1[LEN], buf2[LEN];
    p = strrchr(buf, sep);
    *p = '\0';
    int i = 0;
    p ++;

    int num = atoi(p);
    while(*p)
    {
        buf1[i ++] = *p;
        p ++;
    }
    p = buf;
    i = 0;
    while(*p)
    {
        buf2[i++] = *p;
        p ++;
    }   
    printf("The string is %s\n", buf2);
    printf("The number is %d\n", num);

    sep = ' ';
    char buf3[LEN], buf4[LEN];
    p = strchr(buf2, sep);
    if(p == NULL)
    {
        printf("The return is NULL\n");
        exit(-1);
    }
    *p = '\0';
    p ++;
    i = 0;
    while(*p)
    {
        buf3[i ++] = *p;
        p ++;
    }
    p = buf2;
    i = 0;
    while(*p)
    {
        buf4[i ++] = *p;
        p ++;
    }
    printf("The first string is %s,\tThe second string is %s\n", buf4, buf3);

    return 0;
}

值得一说的事情是,代码中的注释,’\t’,在用vim编辑时,tab键不代表’\t’,需要用\t来表示。


相应的在C++中string对应的函数是find_first_of与find_last_of
直接贴代码,

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <iostream>
//#define N 10240
#define LEN 10240

int main()
{
    char buf[LEN] = "teststring\t12";
    char sep = '\t';
    std::string str = (std::string)buf;
    std::string str1, str2;
    std::string::size_type ind = str.find_last_of(sep);
    if(ind != std::string::npos)
    {   
        std::cout << "The test" << std::endl;
        str1 = str.substr(0, ind);
        str2 = str.substr(ind + 1, str.size());
    }   
    std::cout << "The ORG is " << (std::string)buf << std::endl;
    std::cout << "str1 : " << str1 << std::endl;
    std::cout << "str2 : " << str2 << std::endl;
    return 0;
}
#define MAX_PATH_LEN 1024 #define MAX_MATCHES 1024 #define DEBUG_PRINT(fmt, ...) \ do { fprintf(stderr, "[DEBUG] %s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__); } while (0) typedef enum { MODE_NEW, MODE_CYCLE, MODE_DIR_APPEND } completion_mode; typedef struct { char current_path[MAX_PATH_LEN]; char prefix[MAX_PATH_LEN]; char *matches[MAX_MATCHES]; int match_count; int current_index; int is_virtual_path; char *last_returned; char last_attempt[MAX_PATH_LEN]; } completion_state; static completion_state comp_state; /****************** 核心函数实现 ******************/ void reset_completion_state() { DEBUG_PRINT("Resetting completion state (had %d matches)\n", comp_state.match_count); for (int i = 0; i < comp_state.match_count; i++) { DEBUG_PRINT("Freeing match[%d]: %s\n", i, comp_state.matches[i]); XFREE(MTYPE_TMP, comp_state.matches[i]); } if (comp_state.last_returned) { DEBUG_PRINT("Freeing last_returned: %s\n", comp_state.last_returned); XFREE(MTYPE_TMP, comp_state.last_returned); } memset(&comp_state, 0, sizeof(comp_state)); } void parse_input_text(const char *text) { DEBUG_PRINT("Parsing input text: '%s'\n", text); strncpy(comp_state.last_attempt, text, MAX_PATH_LEN-1); comp_state.last_attempt[MAX_PATH_LEN-1] = '\0'; strncpy(comp_state.current_path, text, MAX_PATH_LEN-1); char *last_slash = strrchr(comp_state.current_path, '/'); if (last_slash) { strncpy(comp_state.prefix, last_slash+1, MAX_PATH_LEN-1); *(last_slash+1) = '\0'; DEBUG_PRINT("Split path: dir='%s' prefix='%s'\n", comp_state.current_path, comp_state.prefix); } else { strncpy(comp_state.prefix, comp_state.current_path, MAX_PATH_LEN-1); comp_state.current_path[0] = '\0'; DEBUG_PRINT("No slash, prefix='%s'\n", comp_state.prefix); } } /* 虚拟路径映射 */ static const char* map_virtual_path(const char *path) { if (strncmp(path, "flash:/", 7) == 0) { DEBUG_PRINT("Mapping virtual path '%s' to '/mnt/switch/'\n", path); return "/mnt/switch/"; } DEBUG_PRINT("No virtual mapping for '%s'\n", path); return path; } /* 核心目录扫描函数 */ void generate_current_dir_paths(const char *base_path, const char *prefix, int is_virtual, char **matches, int *match_count) { const char *search_path = is_virtual ? map_virtual_path(base_path) : base_path; DEBUG_PRINT("Scanning dir: base='%s' prefix='%s' (virtual=%d, mapped='%s')\n", base_path, prefix, is_virtual, search_path); DIR *dir = opendir(*search_path ? search_path : "."); if (!dir) { DEBUG_PRINT("Failed to open dir '%s'\n", search_path); return; } struct dirent *entry; while ((entry = readdir(dir)) != NULL && *match_count < MAX_MATCHES) { /* 跳过隐藏文件和当前目录 */ if (entry->d_name[0] == '.') { DEBUG_PRINT("Skipping hidden file: '%s'\n", entry->d_name); continue; } /* 前缀匹配检查 */ if (prefix[0] && strncmp(entry->d_name, prefix, strlen(prefix)) != 0) { DEBUG_PRINT("Skipping non-matching: '%s' (prefix='%s')\n", entry->d_name, prefix); continue; } /* 构建完整路径 */ char full_path[MAX_PATH_LEN]; snprintf(full_path, sizeof(full_path), "%s%s", base_path, entry->d_name); /* 添加目录分隔符 */ struct stat st; int is_dir = (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)); if (is_dir) { snprintf(full_path, sizeof(full_path), "%s%s/", base_path, entry->d_name); DEBUG_PRINT("Found directory: %s\n", full_path); } else { DEBUG_PRINT("Found file: %s\n", full_path); } /* 存储匹配项 */ matches[*match_count] = XSTRDUP(MTYPE_TMP, full_path); DEBUG_PRINT("Added match[%d]: %s\n", *match_count, matches[*match_count]); (*match_count)++; } closedir(dir); DEBUG_PRINT("Total matches found: %d\n", *match_count); } char *filename_completion_function(const char *text, int state) { (void)state; // 明确标记未使用参数 DEBUG_PRINT("--- NEW COMPLETION CALL (text='%s') ---\n", text); /* 自主状态决策 */ completion_mode mode; if (comp_state.last_returned == NULL) { mode = MODE_NEW; DEBUG_PRINT("Mode set to NEW (first completion)\n"); } else if (strchr(comp_state.last_returned, '/') && strncmp(text, comp_state.last_returned, strlen(comp_state.last_returned)) == 0) { mode = MODE_DIR_APPEND; DEBUG_PRINT("Mode set to DIR_APPEND (text='%s', last='%s')\n", text, comp_state.last_returned); } else if (strcmp(text, comp_state.last_attempt) == 0) { mode = MODE_CYCLE; DEBUG_PRINT("Mode set to CYCLE (same input)\n"); } else { mode = MODE_NEW; DEBUG_PRINT("Mode set to NEW (different input)\n"); } /* 处理模式 */ switch (mode) { case MODE_NEW: reset_completion_state(); parse_input_text(text); comp_state.is_virtual_path = (strncmp(text, "flash:/", 7) == 0); DEBUG_PRINT("Virtual path detected: %d\n", comp_state.is_virtual_path); generate_current_dir_paths(comp_state.current_path, comp_state.prefix, comp_state.is_virtual_path, comp_state.matches, &comp_state.match_count); comp_state.current_index = 0; break; case MODE_DIR_APPEND: { strncpy(comp_state.current_path, comp_state.last_returned, MAX_PATH_LEN); strncpy(comp_state.prefix, text + strlen(comp_state.last_returned), MAX_PATH_LEN); DEBUG_PRINT("DIR_APPEND: path='%s' new_prefix='%s'\n", comp_state.current_path, comp_state.prefix); generate_current_dir_paths(comp_state.current_path, comp_state.prefix, comp_state.is_virtual_path, comp_state.matches, &comp_state.match_count); comp_state.current_index = 0; break; } case MODE_CYCLE: comp_state.current_index = (comp_state.current_index + 1) % comp_state.match_count; DEBUG_PRINT("CYCLE: new index=%d/%d\n", comp_state.current_index, comp_state.match_count); break; } /* 返回结果 */ char *ret = NULL; if (comp_state.current_index < comp_state.match_count) { ret = XSTRDUP(MTYPE_TMP, comp_state.matches[comp_state.current_index]); DEBUG_PRINT("Returning match[%d]: %s\n", comp_state.current_index, ret); /* 更新状态 */ XFREE(MTYPE_TMP, comp_state.last_returned); comp_state.last_returned = XSTRDUP(MTYPE_TMP, ret); strncpy(comp_state.last_attempt, text, MAX_PATH_LEN-1); } else { DEBUG_PRINT("No valid matches to return\n"); } return ret; } 打印如下: <dahua>dir e 【cmlsh_completion】text = e [MATCHES] Called with text='e' [DEBUG] filename_completion_function:1866: --- NEW COMPLETION CALL (text='e') --- [DEBUG] filename_completion_function:1872: Mode set to NEW (first completion) [DEBUG] reset_completion_state:1772: Resetting completion state (had 0 matches) [DEBUG] parse_input_text:1785: Parsing input text: 'e' [DEBUG] parse_input_text:1799: No slash, prefix='e' [DEBUG] filename_completion_function:1895: Virtual path detected: 0 [DEBUG] generate_current_dir_paths:1817: Scanning dir: base='' prefix='e' (virtual=0, mapped='') [DEBUG] generate_current_dir_paths:1830: Skipping hidden file: '.' [DEBUG] generate_current_dir_paths:1830: Skipping hidden file: '..' [DEBUG] generate_current_dir_paths:1836: Skipping non-matching: 'fa' (prefix='e') [DEBUG] generate_current_dir_paths:1836: Skipping non-matching: 'cfg' (prefix='e') [DEBUG] generate_current_dir_paths:1850: Found directory: etc/ [DEBUG] generate_current_dir_paths:1857: Added match[0]: etc/ [DEBUG] generate_current_dir_paths:1830: Skipping hidden file: '.trash' [DEBUG] generate_current_dir_paths:1836: Skipping non-matching: 'home' (prefix='e') [DEBUG] generate_current_dir_paths:1836: Skipping non-matching: 'logfile' (prefix='e') [DEBUG] generate_current_dir_paths:1836: Skipping non-matching: 'installers' (prefix='e') [DEBUG] generate_current_dir_paths:1836: Skipping non-matching: 'main_uImage' (prefix='e') [DEBUG] generate_current_dir_paths:1852: Found file: ea.txt [DEBUG] generate_current_dir_paths:1857: Added match[1]: ea.txt [DEBUG] generate_current_dir_paths:1861: Total matches found: 2 [DEBUG] filename_completion_function:1925: Returning match[0]: etc/ [MATCHES] Returning single match: 'etc/' tc/ 【cmlsh_completion】text = etc/ [MATCHES] Called with text='etc/' [DEBUG] filename_completion_function:1866: --- NEW COMPLETION CALL (text='etc/') --- [DEBUG] filename_completion_function:1877: Mode set to DIR_APPEND (text='etc/', last='etc/') [DEBUG] filename_completion_function:1905: DIR_APPEND: path='etc/' new_prefix='' [DEBUG 会有异常退出,分析上边的打印代码逻辑,修复问题
08-13
现在这个代码编译有问题,帮我修正错误 /**************************************************************************************************/ /* INCLUDE_FILES */ /**************************************************************************************************/ #include "wm.h" #include "wmb.h" #include "rcc.h" #include "cli_common.h" #include "gml/data.h" #include "dlist.h" #include "midware/tpConfig.h" #include "midware/tpState.h" #include "fepPfm.h" #include "common/applError.h" #include <time.h> #include <stdbool.h> #include "cliLanDnsHandlers.h" #include "dmlib/dmDnsProxyShell.h" #include "uilib/uilibTpconfig.h" #include "uilib/uilibTpconfigCpn.h" #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> /**************************************************************************************************/ /* DEFINES */ /**************************************************************************************************/ #define DNS_SERVER_MAX_RULES 120 #define DNS_DOMAIN_MAX_LENGTH 256 #define DNS_SERVER_MAX_LENGTH 256 #define DNS_NAME_MAX_LENGTH 64 #define IP_ADDRESS_MAX_LENGTH 46 // IPv6 max length #define MAX_CLI_INPUT_LEN 256 #define MAX_ALIAS_COUNT 10 #define CLI_PRINT(pCliEnv, fmt, ...) cliPrintf((cli_env *)(pCliEnv), fmt, ##__VA_ARGS__) #define DEFAULT_DNS_CONFIG_PATH "/etc/dnsproxy/dns_rules.conf" STATUS create_default_rules(const char *path); STATUS reset_to_default_rules(const char *path); STATUS load_dns_rules(const char *path); int mkdir_p(const char *path, mode_t mode); const char *get_current_time_str(void); // 改进 DEBUG_LOG 宏,添加 do-while 结构 #define DEBUG_LOG(fmt, ...) \ do { \ printf("[DEBUG] %s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); \ } while(0) #define PRINT_FIELD(label, value) \ CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, label, value ? value : "") #define PRINT_FIELD_U(label, value) \ CLI_PRINT(pCliEnv, "%-*s: %u\n", FIELD_LABEL_WIDTH, label, value) /**************************************************************************************************/ /* TYPES */ /**************************************************************************************************/ typedef enum { DNS_RULE_TYPE_IP = 0, DNS_RULE_TYPE_CNAME, DNS_RULE_TYPE_FORWARD } DNS_RULE_TYPE_E; // 链表节点定义 typedef struct lanDns_node { CFG_DNSSERVER_RULE_T rule; bool is_new; bool modified; struct lanDns_node *next; } lanDns_node_t; /**************************************************************************************************/ /* GLOBAL VARIABLES */ /**************************************************************************************************/ static lanDns_node_t *g_landns_list = NULL; static lanDns_node_t *g_current_edit = NULL; static bool g_dns_loaded = false; // 添加加载状态标志 /**************************************************************************************************/ /* UTILITY FUNCTIONS */ /**************************************************************************************************/ void safe_strncpy(char *dest, const char *src, size_t dest_size) { if (dest_size == 0) return; strncpy(dest, src, dest_size - 1); dest[dest_size - 1] = '\0'; } // 链表节点计数 static int dlist_count(lanDns_node_t *head) { int count = 0; lanDns_node_t *current = head; while (current) { count++; current = current->next; } return count; } void safe_strncat(char *dest, const char *src, size_t dest_size) { if (dest_size == 0) return; size_t dest_len = strlen(dest); size_t src_len = strlen(src); if (dest_len >= dest_size - 1) return; // 目标已满 size_t copy_len = src_len; if (dest_len + src_len >= dest_size - 1) { copy_len = dest_size - dest_len - 1; } strncat(dest, src, copy_len); } static const char* _rule_type_to_str(DNS_RULE_TYPE_E type) { switch (type) { case DNS_RULE_TYPE_IP: return "ip"; case DNS_RULE_TYPE_CNAME: return "cname"; case DNS_RULE_TYPE_FORWARD: return "forward"; default: return "ip"; } } // IP验证函数 bool is_valid_ip(const char *ip) { struct sockaddr_in sa; struct sockaddr_in6 sa6; return (inet_pton(AF_INET, ip, &(sa.sin_addr)) != 0) || (inet_pton(AF_INET6, ip, &(sa6.sin6_addr)) != 0); } // CIDR格式验证 bool is_cidr_format(const char *cidr) { if (!cidr || strlen(cidr) > 64) return false; char copy[64]; strncpy(copy, cidr, sizeof(copy) - 1); copy[sizeof(copy) - 1] = '\0'; char *slash = strchr(copy, '/'); if (!slash) return false; *slash = '\0'; char *mask_str = slash + 1; // 验证IP部分 struct in_addr addr; if (inet_pton(AF_INET, copy, &addr) != 1) { return false; } // 验证子网掩码部分 errno = 0; char *end; long mask = strtol(mask_str, &end, 10); if (errno != 0 || *end != '\0' || mask < 0 || mask > 32) { return false; } return true; } // 验证网络列表 static bool is_valid_network_list(const char *list) { if (!list || strlen(list) == 0) return false; char copy[256]; strncpy(copy, list, sizeof(copy) - 1); copy[sizeof(copy) - 1] = '\0'; char *token = strtok(copy, ","); while (token != NULL) { // 跳过空格 while (*token == ' ') token++; // 检查是否为"all"或CIDR格式 if (strcmp(token, "all") != 0 && !is_cidr_format(token)) { return false; } token = strtok(NULL, ","); } return true; } STATUS reset_to_default_rules(const char *path) { // 实际项目中这里应写入默认规则 // 简化版只返回成功 return create_default_rules(path); } STATUS ensure_dns_rules_loaded() { // 1. 如果已加载则直接返回 if (g_dns_loaded) { return OK; } // 2. 获取配置文件路径(支持环境变量覆盖) const char *config_path = getenv("DNS_CONFIG_PATH"); if (!config_path) { config_path = DEFAULT_DNS_CONFIG_PATH; // "/etc/appname/dns_rules.conf" } // 3. 检查文件是否存在 if (access(config_path, F_OK) != 0) { // 3.1 文件不存在时自动创建 if (errno == ENOENT) { if (create_default_rules(config_path) != OK) { DEBUG_LOG("Failed to create config at %s: %s", config_path, strerror(errno)); return ERROR; } } else { // 3.2 权限或其他错误 DEBUG_LOG("Access error for %s: %s", config_path, strerror(errno)); return ERROR; } } // 4. 检查文件是否为空 struct stat st; if (stat(config_path, &st) == 0 && st.st_size == 0) { // 4.1 空文件时重置为默认内容 if (reset_to_default_rules(config_path) != OK) { DEBUG_LOG("Failed to reset empty config: %s", config_path); return ERROR; } } // 5. 加载规则 if (load_dns_rules(config_path) != OK) { DEBUG_LOG("Failed to parse config: %s", config_path); return ERROR; } g_dns_loaded = 1; return OK; } STATUS create_default_rules(const char *path) { // 1. 创建目录(如果不存在) char dir_path[256] = {0}; strncpy(dir_path, path, sizeof(dir_path)-1); char *last_slash = strrchr(dir_path, '/'); if (last_slash) { *last_slash = '\0'; // 提取目录路径 if (mkdir_p(dir_path, 0755) != 0) { // 递归创建目录 DEBUG_LOG("Failed to create dir %s: %s", dir_path, strerror(errno)); return ERROR; } } // 2. 创建并写入默认内容 FILE *fp = fopen(path, "w"); if (!fp) { DEBUG_LOG("fopen failed: %s", strerror(errno)); return ERROR; } fprintf(fp, "# DNS Rules Configuration\n"); fprintf(fp, "# Version: 1.0\n"); fprintf(fp, "# Created: %s\n", get_current_time_str()); fprintf(fp, "\n[rules]\n\n"); fclose(fp); return OK; } int mkdir_p(const char *path, mode_t mode) { char tmp[256]; char *p = NULL; size_t len; snprintf(tmp, sizeof(tmp), "%s", path); len = strlen(tmp); // 去除末尾斜杠 if (tmp[len - 1] == '/') { tmp[len - 1] = 0; } // 逐级创建目录 for (p = tmp + 1; *p; p++) { if (*p == '/') { *p = 0; if (mkdir(tmp, mode) != 0 && errno != EEXIST) { return -1; } *p = '/'; } } // 创建最终目录 if (mkdir(tmp, mode) != 0 && errno != EEXIST) { return -1; } return 0; } // 从链表删除节点 static void dlist_del(lanDns_node_t **head, lanDns_node_t *node) { if (*head == NULL || node == NULL) return; if (*head == node) { *head = node->next; } else { lanDns_node_t *prev = *head; while (prev->next != NULL && prev->next != node) { prev = prev->next; } if (prev->next == node) { prev->next = node->next; } } free(node); } // 链表添加尾部节点 static void dlist_add_tail(lanDns_node_t **head, lanDns_node_t *new_node) { if (!new_node) return; if (*head == NULL) { *head = new_node; } else { lanDns_node_t *current = *head; while (current->next != NULL) { current = current->next; } current->next = new_node; } new_node->next = NULL; } void destroy_landns_list() { lanDns_node_t *curr = g_landns_list, *next; while (curr) { next = curr->next; free(curr); curr = next; } g_landns_list = NULL; } // 链表遍历宏 #define dlist_for_each_entry(pos, head) \ for (pos = head; pos != NULL; pos = pos->next) // 安全遍历宏(支持删除) #define dlist_for_each_entry_safe(pos, n, head) \ for (pos = head, n = (pos ? pos->next : NULL); \ pos != NULL; \ pos = n, n = (n ? n->next : NULL)) /**************************************************************************************************/ /* CORE FUNCTIONS */ /**************************************************************************************************/ // 查找节点辅助函数(使用数据库直接查询) static lanDns_node_t* find_landns_node(const char *name) { CFG_DNSSERVER_RULE_T *db_rule = NULL; APPL_ERRCODE ret = dmDnsServerRuleGetByName(name, &db_rule); if (ret != ERR_NO_ERROR || !db_rule) { return NULL; } // 检查是否已在内存链表中 lanDns_node_t *node; dlist_for_each_entry(node, g_landns_list) { if (strcmp(node->rule.name, name) == 0) { // 更新内存中的规则 memcpy(&node->rule, db_rule, sizeof(CFG_DNSSERVER_RULE_T)); DNSPROXYSHELL_FREE(db_rule); return node; } } // 创建新节点 node = malloc(sizeof(lanDns_node_t)); if (!node) { DNSPROXYSHELL_FREE(db_rule); return NULL; } memset(node, 0, sizeof(lanDns_node_t)); memcpy(&node->rule, db_rule, sizeof(CFG_DNSSERVER_RULE_T)); DNSPROXYSHELL_FREE(db_rule); node->is_new = false; node->modified = false; node->next = NULL; // 添加到链表 dlist_add_tail(&g_landns_list, node); return node; } static lanDns_node_t* create_new_node(const char *name) { lanDns_node_t *node = malloc(sizeof(lanDns_node_t)); if (!node) return NULL; memset(node, 0, sizeof(lanDns_node_t)); safe_strncpy(node->rule.name, name, DNSPROXY_LEN_NAME32); safe_strncpy(node->rule.status, "on", DNSPROXY_LEN_STATUS); node->rule.ttl = 3600; // 默认TTL // 初始化数组字段 memset(node->rule.aliases, 0, DNSPROXY_LEN_ALIASES); memset(node->rule.ipv4_addrs, 0, DNSPROXY_LEN_IPV4_ADDRS); memset(node->rule.ipv6_addrs, 0, DNSPROXY_LEN_IPV6_ADDRS); memset(node->rule.dns_server, 0, DNSPROXY_LEN_DNS_SERVER); memset(node->rule.cname, 0, DNSPROXY_LEN_CNAME); safe_strncpy(node->rule.lan_networks, "all", DNSPROXY_LEN_LAN_NETWORKS); node->is_new = true; node->modified = true; return node; } /**************************************************************************************************/ /* COMMAND HANDLERS */ /**************************************************************************************************/ // 1. Profile处理函数 STATUS cli_lanDnsAddProfile(cli_env *pCliEnv, char *profileName) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { CLI_PRINT(pCliEnv, "%% Failed to load DNS rules\n"); return ERROR; } if (!profileName || strlen(profileName) == 0) { CLI_PRINT(pCliEnv, "%% Profile name cannot be empty\n"); return ERROR; } // 1. 先检查是否已在内存链表中(包括新创建的节点) lanDns_node_t *node; dlist_for_each_entry(node, g_landns_list) { if (strcmp(node->rule.name, profileName) == 0) { g_current_edit = node; CLI_PRINT(pCliEnv, "%% Editing existing profile: %s\n", profileName); return OK; } } // 2. 尝试从数据库加载 g_current_edit = find_landns_node(profileName); if (g_current_edit) { CLI_PRINT(pCliEnv, "%% Editing profile: %s\n", profileName); return OK; } // 3. 创建全新节点 g_current_edit = create_new_node(profileName); if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% Failed to create profile '%s'\n", profileName); return ERROR; } // 4. 添加新节点到内存链表(关键修复) g_current_edit->next = g_landns_list; g_landns_list = g_current_edit; CLI_PRINT(pCliEnv, "%% Created new profile: %s\n", profileName); return OK; } // 2. 处理类型设置 STATUS cli_lanDnsAddType(cli_env *pCliEnv, char *typeName) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (strcasecmp(typeName, "ip") == 0) { safe_strncpy(g_current_edit->rule.type, "ip", DNSPROXY_LEN_TYPE); } else if (strcasecmp(typeName, "cname") == 0) { safe_strncpy(g_current_edit->rule.type, "cname", DNSPROXY_LEN_TYPE); } else if (strcasecmp(typeName, "forward") == 0) { safe_strncpy(g_current_edit->rule.type, "forward", DNSPROXY_LEN_TYPE); } else { CLI_PRINT(pCliEnv, "%% Invalid type. Use ip/cname/forward\n"); return ERROR; } g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% Rule type set to: %s\n", g_current_edit->rule.type); return OK; } // 3. 域名处理函数 STATUS cli_lanDnsAddDomain(cli_env *pCliEnv, char *domain) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!domain || strlen(domain) == 0) { CLI_PRINT(pCliEnv, "%% Error: Domain cannot be empty\n"); return ERROR; } // 域名长度检查 if (strlen(domain) >= DNSPROXY_LEN_DOMAIN) { CLI_PRINT(pCliEnv, "%% Domain too long (max %d chars)\n", DNSPROXY_LEN_DOMAIN - 1); return ERROR; } safe_strncpy(g_current_edit->rule.domain, domain, DNSPROXY_LEN_DOMAIN); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% Domain set to: %s\n", g_current_edit->rule.domain); return OK; } // 4. 别名处理函数 STATUS cli_lanDnsAddAlias(cli_env *pCliEnv, char *alias) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!alias || strlen(alias) == 0) { CLI_PRINT(pCliEnv, "%% Error: Alias cannot be empty\n"); return ERROR; } // 别名长度检查 if (strlen(alias) >= DNSPROXY_LEN_DOMAIN) { CLI_PRINT(pCliEnv, "%% Alias too long (max %d chars)\n", DNSPROXY_LEN_DOMAIN - 1); return ERROR; } char current_aliases[DNSPROXY_LEN_ALIASES]; strncpy(current_aliases, g_current_edit->rule.aliases, sizeof(current_aliases)); if (strlen(current_aliases) > 0) { // 检查别名数量限制 int count = 1; for (char *p = current_aliases; *p; p++) { if (*p == ',') count++; } if (count >= MAX_ALIAS_COUNT) { CLI_PRINT(pCliEnv, "%% Maximum aliases reached (%d)\n", MAX_ALIAS_COUNT); return ERROR; } strncat(current_aliases, ",", sizeof(current_aliases) - strlen(current_aliases) - 1); } strncat(current_aliases, alias, sizeof(current_aliases) - strlen(current_aliases) - 1); safe_strncpy(g_current_edit->rule.aliases, current_aliases, DNSPROXY_LEN_ALIASES); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% Alias added: %s\n", alias); return OK; } // 5. 状态处理函数 STATUS cli_lanDnsAddStatus(cli_env *pCliEnv, char *status) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (strcasecmp(status, "on") == 0 || strcasecmp(status, "off") == 0) { safe_strncpy(g_current_edit->rule.status, status, DNSPROXY_LEN_STATUS); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% Status set to: %s\n", g_current_edit->rule.status); return OK; } CLI_PRINT(pCliEnv, "%% Invalid status. Use on/off\n"); return ERROR; } // 6. IPv4处理函数 STATUS cli_lanDnsAddIpv4(cli_env *pCliEnv, char *ip) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!ip || !is_valid_ip(ip)) { CLI_PRINT(pCliEnv, "%% Invalid IPv4 address\n"); return ERROR; } // 检查是否是有效的IPv4地址 (AF_INET) struct sockaddr_in sa; if (inet_pton(AF_INET, ip, &(sa.sin_addr)) == 0) { CLI_PRINT(pCliEnv, "%% Not a valid IPv4 address\n"); return ERROR; } safe_strncpy(g_current_edit->rule.ipv4_addrs, ip, DNSPROXY_LEN_IPV4_ADDRS); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% IPv4 set to: %s\n", g_current_edit->rule.ipv4_addrs); return OK; } // 7. IPv6处理函数 STATUS cli_lanDnsAddIpv6(cli_env *pCliEnv, char *ip) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!ip || !is_valid_ip(ip)) { CLI_PRINT(pCliEnv, "%% Invalid IPv6 address\n"); return ERROR; } // 检查是否是有效的IPv6地址 (AF_INET6) struct sockaddr_in6 sa6; if (inet_pton(AF_INET6, ip, &(sa6.sin6_addr)) == 0) { CLI_PRINT(pCliEnv, "%% Not a valid IPv6 address\n"); return ERROR; } safe_strncpy(g_current_edit->rule.ipv6_addrs, ip, DNSPROXY_LEN_IPV6_ADDRS); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% IPv6 set to: %s\n", g_current_edit->rule.ipv6_addrs); return OK; } // 8. CNAME处理函数 STATUS cli_lanDnsAddCname(cli_env *pCliEnv, char *cname) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!cname || strlen(cname) == 0) { CLI_PRINT(pCliEnv, "%% Error: CNAME cannot be empty\n"); return ERROR; } // CNAME长度检查 if (strlen(cname) >= DNSPROXY_LEN_CNAME) { CLI_PRINT(pCliEnv, "%% CNAME too long (max %d chars)\n", DNSPROXY_LEN_CNAME - 1); return ERROR; } safe_strncpy(g_current_edit->rule.cname, cname, DNSPROXY_LEN_CNAME); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% CNAME set to: %s\n", g_current_edit->rule.cname); return OK; } // 9. DNS服务器处理函数 STATUS cli_lanDnsAddDnsServer(cli_env *pCliEnv, char *dns_server) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!dns_server || !is_valid_ip(dns_server)) { CLI_PRINT(pCliEnv, "%% Invalid DNS server address\n"); return ERROR; } char current_servers[DNSPROXY_LEN_DNS_SERVER]; strncpy(current_servers, g_current_edit->rule.dns_server, sizeof(current_servers)); // 检查服务器数量限制 int count = strlen(current_servers) > 0 ? 1 : 0; for (char *p = current_servers; *p; p++) { if (*p == ',') count++; } if (count >= 2) { CLI_PRINT(pCliEnv, "%% Maximum DNS servers reached (2)\n"); return ERROR; } if (strlen(current_servers) > 0) { strncat(current_servers, ",", sizeof(current_servers) - strlen(current_servers) - 1); } strncat(current_servers, dns_server, sizeof(current_servers) - strlen(current_servers) - 1); safe_strncpy(g_current_edit->rule.dns_server, current_servers, DNSPROXY_LEN_DNS_SERVER); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% DNS Server added: %s\n", dns_server); return OK; } // 10. 处理属性删除 STATUS cli_lanDnsDeleteAttribute(cli_env *pCliEnv, char *attr) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile to modify\n"); return ERROR; } if (!attr) { CLI_PRINT(pCliEnv, "%% Error: Attribute name must be specified\n"); return ERROR; } if (strcmp(attr, "alias") == 0) { memset(g_current_edit->rule.aliases, 0, DNSPROXY_LEN_ALIASES); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% Aliases cleared\n"); } else if (strcmp(attr, "ipv4") == 0) { memset(g_current_edit->rule.ipv4_addrs, 0, DNSPROXY_LEN_IPV4_ADDRS); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% IPv4 addresses cleared\n"); } else if (strcmp(attr, "ipv6") == 0) { memset(g_current_edit->rule.ipv6_addrs, 0, DNSPROXY_LEN_IPV6_ADDRS); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% IPv6 addresses cleared\n"); } else if (strcmp(attr, "dns-server") == 0) { memset(g_current_edit->rule.dns_server, 0, DNSPROXY_LEN_DNS_SERVER); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% DNS servers cleared\n"); } else if (strcmp(attr, "cname") == 0) { memset(g_current_edit->rule.cname, 0, DNSPROXY_LEN_CNAME); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% CNAME target cleared\n"); } else { CLI_PRINT(pCliEnv, "%% Error: Unsupported attribute '%s'\n", attr); return ERROR; } return OK; } // 11. LAN网络设置函数 STATUS cli_lanDnsAddLanNetworks(cli_env *pCliEnv, char *networks) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!networks || strlen(networks) == 0) { CLI_PRINT(pCliEnv, "%% Error: LAN networks cannot be empty\n"); return ERROR; } // 验证网络格式 if (strcasecmp(networks, "all") != 0 && !is_valid_network_list(networks)) { CLI_PRINT(pCliEnv, "%% Invalid LAN networks format\n"); return ERROR; } safe_strncpy(g_current_edit->rule.lan_networks, networks, DNSPROXY_LEN_LAN_NETWORKS); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% LAN networks set to: %s\n", g_current_edit->rule.lan_networks); return OK; } // 12. TTL设置函数 STATUS cli_lanDnsAddTtl(cli_env *pCliEnv, char *ttl_str) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!ttl_str || strlen(ttl_str) == 0) { CLI_PRINT(pCliEnv, "%% Error: TTL cannot be empty\n"); return ERROR; } char *endptr; long ttl = strtol(ttl_str, &endptr, 10); if (*endptr != '\0' || ttl <= 0 || ttl > 86400) { // 限制在1秒到1天之间 CLI_PRINT(pCliEnv, "%% Invalid TTL. Must be integer between 1 and 86400\n"); return ERROR; } g_current_edit->rule.ttl = (uint32_t)ttl; g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% TTL set to: %u\n", g_current_edit->rule.ttl); return OK; } // 13. 提交更改到数据库 STATUS cli_lanDnsCommit(cli_env *pCliEnv) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile to commit\n"); return ERROR; } // 验证规则完整性 if (!g_current_edit->rule.name[0] || !g_current_edit->rule.domain[0] || !g_current_edit->rule.type[0]) { CLI_PRINT(pCliEnv, "%% Incomplete rule: name, domain and type are required\n"); return ERROR; } APPL_ERRCODE ret; CFG_DNSSERVER_RULE_T *rule = &g_current_edit->rule; // 如果是新规则,生成唯一ID if (g_current_edit->is_new) { time_t now = time(NULL); snprintf(rule->id, DNSPROXY_LEN_ID, "dns_%ld_%03d", (long)now, rand() % 1000); } // 保存到数据库 if (g_current_edit->is_new) { ret = dmDnsServerRuleAdd(rule); } else { ret = dmDnsServerRuleSet(rule); } if (ret == ERR_NO_ERROR) { g_current_edit->is_new = false; g_current_edit->modified = false; CLI_PRINT(pCliEnv, "%% Changes committed successfully\n"); // 更新内存链表状态 if (!find_landns_node(rule->name)) { // 确保规则在内存链表中 dlist_add_tail(&g_landns_list, g_current_edit); } return OK; } else { CLI_PRINT(pCliEnv, "%% Error committing changes (code: 0x%X)\n", ret); return ERROR; } } // 14. 显示当前配置 STATUS cli_lanDnsShow(cli_env *pCliEnv) { if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile to show\n"); return ERROR; } static const int FIELD_LABEL_WIDTH = 24; // 使用足够大的宽度 CFG_DNSSERVER_RULE_T *rule = &g_current_edit->rule; CLI_PRINT(pCliEnv, "LAN DNS Rule Configuration Summary:\n"); CLI_PRINT(pCliEnv, "----------------------------------------\n"); // 使用一致的格式化输出 CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "Profile Name", rule->name); CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "Rule ID", rule->id ? rule->id : "N/A"); CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "Status", rule->status); CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "Rule Type", rule->type); CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "Domain", rule->domain ? rule->domain : "N/A"); CLI_PRINT(pCliEnv, "%-*s: %u\n", FIELD_LABEL_WIDTH, "TTL", rule->ttl); // 条件显示字段 if (rule->aliases && strlen(rule->aliases) > 0) { CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "Aliases", rule->aliases); } if (rule->lan_networks && strlen(rule->lan_networks) > 0) { CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "LAN Networks", rule->lan_networks); } if (rule->ipv4_addrs && strlen(rule->ipv4_addrs) > 0) { CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "IPv4 Addresses", rule->ipv4_addrs); } if (rule->ipv6_addrs && strlen(rule->ipv6_addrs) > 0) { CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "IPv6 Addresses", rule->ipv6_addrs); } if (rule->cname && strlen(rule->cname) > 0) { CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "CNAME Target", rule->cname); } if (rule->dns_server && strlen(rule->dns_server) > 0) { CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "DNS Servers", rule->dns_server); } CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "Changes", g_current_edit->modified ? "Modified (not committed)" : "No changes"); CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, "Record Status", g_current_edit->is_new ? "New rule" : "Existing rule"); CLI_PRINT(pCliEnv, "----------------------------------------\n"); return OK; } /****************************************************************************************/ // 15. 退出配置模式函数 STATUS cli_lanDnsCancel(cli_env *pCliEnv) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile to cancel\n"); return ERROR; } char profile_name[DNSPROXY_LEN_NAME32]; strncpy(profile_name, g_current_edit->rule.name, sizeof(profile_name)); // 如果是新建但未提交的规则,直接删除 if (g_current_edit->is_new) { // 从内存链表删除 lanDns_node_t *prev = NULL; lanDns_node_t *curr = g_landns_list; while (curr && curr != g_current_edit) { prev = curr; curr = curr->next; } if (curr == g_current_edit) { if (prev) prev->next = g_current_edit->next; else g_landns_list = g_current_edit->next; } // 释放节点内存 free(g_current_edit); } else { // 对于已存在的规则,丢弃修改 g_current_edit->modified = false; // 恢复原始值(从数据库重新加载) CFG_DNSSERVER_RULE_T *db_rule = NULL; APPL_ERRCODE ret = dmDnsServerRuleGetByName(profile_name, &db_rule); if (ret == ERR_NO_ERROR && db_rule) { memcpy(&g_current_edit->rule, db_rule, sizeof(CFG_DNSSERVER_RULE_T)); DNSPROXYSHELL_FREE(db_rule); } } g_current_edit = NULL; CLI_PRINT(pCliEnv, "%% Cancelled editing for profile: %s\n", profile_name); return OK; } // 16. 退出配置模式函数(不带参数) STATUS cli_lanDnsExit(cli_env *pCliEnv) { return cli_lanDnsCancel(pCliEnv); } // 18. 显示所有profile的函数 STATUS cli_lanDnsShowAll(cli_env *pCliEnv) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_landns_list) { CLI_PRINT(pCliEnv, "%% No LAN DNS profiles configured\n"); return OK; } int count = dlist_count(g_landns_list); CLI_PRINT(pCliEnv, "┌───────────────────────────────────────────────────────┐\n"); CLI_PRINT(pCliEnv, "│ Configured LAN DNS Profiles (%d) │\n", count); CLI_PRINT(pCliEnv, "├──────────────┬──────────────┬──────────┬───────────────┤\n"); CLI_PRINT(pCliEnv, "│ Profile Name │ Status │ Type │ Domain │\n"); CLI_PRINT(pCliEnv, "├──────────────┼──────────────┼──────────┼───────────────┤\n"); lanDns_node_t *node; dlist_for_each_entry(node, g_landns_list) { CFG_DNSSERVER_RULE_T *rule = &node->rule; // 截断过长的字符串 char name_display[14] = {0}; strncpy(name_display, rule->name, 12); if (strlen(rule->name) > 12) strcat(name_display, ".."); char domain_display[15] = {0}; strncpy(domain_display, rule->domain, 13); if (strlen(rule->domain) > 13) strcat(domain_display, ".."); CLI_PRINT(pCliEnv, "│ %-12s │ %-12s │ %-8s │ %-13s │\n", name_display, rule->status, rule->type, domain_display); } CLI_PRINT(pCliEnv, "└──────────────┴──────────────┴──────────┴───────────────┘\n"); return OK; }
最新发布
10-21
src/cliLanDnsHandlers.c: In function ‘ensure_dns_rules_loaded’: src/cliLanDnsHandlers.c:187:9: error: ‘g_rules_loaded’ undeclared (first use in this function); did you mean ‘g_dns_loaded’? if (g_rules_loaded) { ^~~~~~~~~~~~~~ g_dns_loaded src/cliLanDnsHandlers.c:187:9: note: each undeclared identifier is reported only once for each function it appears in src/cliLanDnsHandlers.c:194:23: error: ‘DEFAULT_DNS_CONFIG_PATH’ undeclared (first use in this function); did you mean ‘DEFAULT_NETIF_MTU’? config_path = DEFAULT_DNS_CONFIG_PATH; // "/etc/appname/dns_rules.conf" ^~~~~~~~~~~~~~~~~~~~~~~ DEFAULT_NETIF_MTU src/cliLanDnsHandlers.c:201:17: error: implicit declaration of function ‘create_default_rules’ [-Werror=implicit-function-declaration] if (create_default_rules(config_path) != OK) { ^~~~~~~~~~~~~~~~~~~~ src/cliLanDnsHandlers.c:217:13: error: implicit declaration of function ‘reset_to_default_rules’ [-Werror=implicit-function-declaration] if (reset_to_default_rules(config_path) != OK) { ^~~~~~~~~~~~~~~~~~~~~~ src/cliLanDnsHandlers.c:224:9: error: implicit declaration of function ‘load_dns_rules’ [-Werror=implicit-function-declaration] if (load_dns_rules(config_path) != OK) { ^~~~~~~~~~~~~~ src/cliLanDnsHandlers.c: In function ‘create_default_rules’: src/cliLanDnsHandlers.c:240:13: error: implicit declaration of function ‘mkdir_p’; did you mean ‘mkdirat’? [-Werror=implicit-function-declaration] if (mkdir_p(dir_path, 0755) != 0) { // 递归创建目录 ^~~~~~~ mkdirat src/cliLanDnsHandlers.c:255:36: error: implicit declaration of function ‘get_current_time_str’; did you mean ‘getdirentries’? [-Werror=implicit-function-declaration] fprintf(fp, "# Created: %s\n", get_current_time_str()); 这是现在的代码 /**************************************************************************************************/ /* INCLUDE_FILES */ /**************************************************************************************************/ #include "wm.h" #include "wmb.h" #include "rcc.h" #include "cli_common.h" #include "gml/data.h" #include "dlist.h" #include "midware/tpConfig.h" #include "midware/tpState.h" #include "fepPfm.h" #include "common/applError.h" #include <time.h> #include <stdbool.h> #include "cliLanDnsHandlers.h" #include "dmlib/dmDnsProxyShell.h" #include "uilib/uilibTpconfig.h" #include "uilib/uilibTpconfigCpn.h" #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> /**************************************************************************************************/ /* DEFINES */ /**************************************************************************************************/ #define DNS_SERVER_MAX_RULES 120 #define DNS_DOMAIN_MAX_LENGTH 256 #define DNS_SERVER_MAX_LENGTH 256 #define DNS_NAME_MAX_LENGTH 64 #define IP_ADDRESS_MAX_LENGTH 46 // IPv6 max length #define MAX_CLI_INPUT_LEN 256 #define MAX_ALIAS_COUNT 10 #define CLI_PRINT(pCliEnv, fmt, ...) cliPrintf((cli_env *)(pCliEnv), fmt, ##__VA_ARGS__) // 改进 DEBUG_LOG 宏,添加 do-while 结构 #define DEBUG_LOG(fmt, ...) \ do { \ printf("[DEBUG] %s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); \ } while(0) #define PRINT_FIELD(label, value) \ CLI_PRINT(pCliEnv, "%-*s: %s\n", FIELD_LABEL_WIDTH, label, value ? value : "") #define PRINT_FIELD_U(label, value) \ CLI_PRINT(pCliEnv, "%-*s: %u\n", FIELD_LABEL_WIDTH, label, value) /**************************************************************************************************/ /* TYPES */ /**************************************************************************************************/ typedef enum { DNS_RULE_TYPE_IP = 0, DNS_RULE_TYPE_CNAME, DNS_RULE_TYPE_FORWARD } DNS_RULE_TYPE_E; // 链表节点定义 typedef struct lanDns_node { CFG_DNSSERVER_RULE_T rule; bool is_new; bool modified; struct lanDns_node *next; } lanDns_node_t; /**************************************************************************************************/ /* GLOBAL VARIABLES */ /**************************************************************************************************/ static lanDns_node_t *g_landns_list = NULL; static lanDns_node_t *g_current_edit = NULL; static bool g_dns_loaded = false; // 添加加载状态标志 /**************************************************************************************************/ /* UTILITY FUNCTIONS */ /**************************************************************************************************/ void safe_strncpy(char *dest, const char *src, size_t dest_size) { if (dest_size == 0) return; strncpy(dest, src, dest_size - 1); dest[dest_size - 1] = '\0'; } // 链表节点计数 static int dlist_count(lanDns_node_t *head) { int count = 0; lanDns_node_t *current = head; while (current) { count++; current = current->next; } return count; } void safe_strncat(char *dest, const char *src, size_t dest_size) { if (dest_size == 0) return; size_t dest_len = strlen(dest); size_t src_len = strlen(src); if (dest_len >= dest_size - 1) return; // 目标已满 size_t copy_len = src_len; if (dest_len + src_len >= dest_size - 1) { copy_len = dest_size - dest_len - 1; } strncat(dest, src, copy_len); } static const char* _rule_type_to_str(DNS_RULE_TYPE_E type) { switch (type) { case DNS_RULE_TYPE_IP: return "ip"; case DNS_RULE_TYPE_CNAME: return "cname"; case DNS_RULE_TYPE_FORWARD: return "forward"; default: return "ip"; } } // IP验证函数 bool is_valid_ip(const char *ip) { struct sockaddr_in sa; struct sockaddr_in6 sa6; return (inet_pton(AF_INET, ip, &(sa.sin_addr)) != 0) || (inet_pton(AF_INET6, ip, &(sa6.sin6_addr)) != 0); } // CIDR格式验证 bool is_cidr_format(const char *cidr) { if (!cidr || strlen(cidr) > 64) return false; char copy[64]; strncpy(copy, cidr, sizeof(copy) - 1); copy[sizeof(copy) - 1] = '\0'; char *slash = strchr(copy, '/'); if (!slash) return false; *slash = '\0'; char *mask_str = slash + 1; // 验证IP部分 struct in_addr addr; if (inet_pton(AF_INET, copy, &addr) != 1) { return false; } // 验证子网掩码部分 errno = 0; char *end; long mask = strtol(mask_str, &end, 10); if (errno != 0 || *end != '\0' || mask < 0 || mask > 32) { return false; } return true; } // 验证网络列表 static bool is_valid_network_list(const char *list) { if (!list || strlen(list) == 0) return false; char copy[256]; strncpy(copy, list, sizeof(copy) - 1); copy[sizeof(copy) - 1] = '\0'; char *token = strtok(copy, ","); while (token != NULL) { // 跳过空格 while (*token == ' ') token++; // 检查是否为"all"或CIDR格式 if (strcmp(token, "all") != 0 && !is_cidr_format(token)) { return false; } token = strtok(NULL, ","); } return true; } STATUS ensure_dns_rules_loaded() { // 1. 如果已加载则直接返回 if (g_rules_loaded) { return OK; } // 2. 获取配置文件路径(支持环境变量覆盖) const char *config_path = getenv("DNS_CONFIG_PATH"); if (!config_path) { config_path = DEFAULT_DNS_CONFIG_PATH; // "/etc/appname/dns_rules.conf" } // 3. 检查文件是否存在 if (access(config_path, F_OK) != 0) { // 3.1 文件不存在时自动创建 if (errno == ENOENT) { if (create_default_rules(config_path) != OK) { DEBUG_LOG("Failed to create config at %s: %s", config_path, strerror(errno)); return ERROR; } } else { // 3.2 权限或其他错误 DEBUG_LOG("Access error for %s: %s", config_path, strerror(errno)); return ERROR; } } // 4. 检查文件是否为空 struct stat st; if (stat(config_path, &st) == 0 && st.st_size == 0) { // 4.1 空文件时重置为默认内容 if (reset_to_default_rules(config_path) != OK) { DEBUG_LOG("Failed to reset empty config: %s", config_path); return ERROR; } } // 5. 加载规则 if (load_dns_rules(config_path) != OK) { DEBUG_LOG("Failed to parse config: %s", config_path); return ERROR; } g_rules_loaded = 1; return OK; } STATUS create_default_rules(const char *path) { // 1. 创建目录(如果不存在) char dir_path[256] = {0}; strncpy(dir_path, path, sizeof(dir_path)-1); char *last_slash = strrchr(dir_path, '/'); if (last_slash) { *last_slash = '\0'; // 提取目录路径 if (mkdir_p(dir_path, 0755) != 0) { // 递归创建目录 DEBUG_LOG("Failed to create dir %s: %s", dir_path, strerror(errno)); return ERROR; } } // 2. 创建并写入默认内容 FILE *fp = fopen(path, "w"); if (!fp) { DEBUG_LOG("fopen failed: %s", strerror(errno)); return ERROR; } fprintf(fp, "# DNS Rules Configuration\n"); fprintf(fp, "# Version: 1.0\n"); fprintf(fp, "# Created: %s\n", get_current_time_str()); fprintf(fp, "\n[rules]\n\n"); fclose(fp); return OK; } int mkdir_p(const char *path, mode_t mode) { char tmp[256]; char *p = NULL; size_t len; snprintf(tmp, sizeof(tmp), "%s", path); len = strlen(tmp); // 去除末尾斜杠 if (tmp[len - 1] == '/') { tmp[len - 1] = 0; } // 逐级创建目录 for (p = tmp + 1; *p; p++) { if (*p == '/') { *p = 0; if (mkdir(tmp, mode) != 0 && errno != EEXIST) { return -1; } *p = '/'; } } // 创建最终目录 if (mkdir(tmp, mode) != 0 && errno != EEXIST) { return -1; } return 0; } // 从链表删除节点 static void dlist_del(lanDns_node_t **head, lanDns_node_t *node) { if (*head == NULL || node == NULL) return; if (*head == node) { *head = node->next; } else { lanDns_node_t *prev = *head; while (prev->next != NULL && prev->next != node) { prev = prev->next; } if (prev->next == node) { prev->next = node->next; } } free(node); } // 链表添加尾部节点 static void dlist_add_tail(lanDns_node_t **head, lanDns_node_t *new_node) { if (!new_node) return; if (*head == NULL) { *head = new_node; } else { lanDns_node_t *current = *head; while (current->next != NULL) { current = current->next; } current->next = new_node; } new_node->next = NULL; } void destroy_landns_list() { lanDns_node_t *curr = g_landns_list, *next; while (curr) { next = curr->next; free(curr); curr = next; } g_landns_list = NULL; } // 链表遍历宏 #define dlist_for_each_entry(pos, head) \ for (pos = head; pos != NULL; pos = pos->next) // 安全遍历宏(支持删除) #define dlist_for_each_entry_safe(pos, n, head) \ for (pos = head, n = (pos ? pos->next : NULL); \ pos != NULL; \ pos = n, n = (n ? n->next : NULL)) /**************************************************************************************************/ /* CORE FUNCTIONS */ /**************************************************************************************************/ // 查找节点辅助函数(使用数据库直接查询) static lanDns_node_t* find_landns_node(const char *name) { CFG_DNSSERVER_RULE_T *db_rule = NULL; APPL_ERRCODE ret = dmDnsServerRuleGetByName(name, &db_rule); if (ret != ERR_NO_ERROR || !db_rule) { return NULL; } // 检查是否已在内存链表中 lanDns_node_t *node; dlist_for_each_entry(node, g_landns_list) { if (strcmp(node->rule.name, name) == 0) { // 更新内存中的规则 memcpy(&node->rule, db_rule, sizeof(CFG_DNSSERVER_RULE_T)); DNSPROXYSHELL_FREE(db_rule); return node; } } // 创建新节点 node = malloc(sizeof(lanDns_node_t)); if (!node) { DNSPROXYSHELL_FREE(db_rule); return NULL; } memset(node, 0, sizeof(lanDns_node_t)); memcpy(&node->rule, db_rule, sizeof(CFG_DNSSERVER_RULE_T)); DNSPROXYSHELL_FREE(db_rule); node->is_new = false; node->modified = false; node->next = NULL; // 添加到链表 dlist_add_tail(&g_landns_list, node); return node; } static lanDns_node_t* create_new_node(const char *name) { lanDns_node_t *node = malloc(sizeof(lanDns_node_t)); if (!node) return NULL; memset(node, 0, sizeof(lanDns_node_t)); safe_strncpy(node->rule.name, name, DNSPROXY_LEN_NAME32); safe_strncpy(node->rule.status, "on", DNSPROXY_LEN_STATUS); node->rule.ttl = 3600; // 默认TTL // 初始化数组字段 memset(node->rule.aliases, 0, DNSPROXY_LEN_ALIASES); memset(node->rule.ipv4_addrs, 0, DNSPROXY_LEN_IPV4_ADDRS); memset(node->rule.ipv6_addrs, 0, DNSPROXY_LEN_IPV6_ADDRS); memset(node->rule.dns_server, 0, DNSPROXY_LEN_DNS_SERVER); memset(node->rule.cname, 0, DNSPROXY_LEN_CNAME); safe_strncpy(node->rule.lan_networks, "all", DNSPROXY_LEN_LAN_NETWORKS); node->is_new = true; node->modified = true; return node; } /**************************************************************************************************/ /* COMMAND HANDLERS */ /**************************************************************************************************/ // 1. Profile处理函数 STATUS cli_lanDnsAddProfile(cli_env *pCliEnv, char *profileName) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { CLI_PRINT(pCliEnv, "%% Failed to load DNS rules\n"); return ERROR; } if (!profileName || strlen(profileName) == 0) { CLI_PRINT(pCliEnv, "%% Profile name cannot be empty\n"); return ERROR; } // 1. 先检查是否已在内存链表中(包括新创建的节点) lanDns_node_t *node; dlist_for_each_entry(node, g_landns_list) { if (strcmp(node->rule.name, profileName) == 0) { g_current_edit = node; CLI_PRINT(pCliEnv, "%% Editing existing profile: %s\n", profileName); return OK; } } // 2. 尝试从数据库加载 g_current_edit = find_landns_node(profileName); if (g_current_edit) { CLI_PRINT(pCliEnv, "%% Editing profile: %s\n", profileName); return OK; } // 3. 创建全新节点 g_current_edit = create_new_node(profileName); if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% Failed to create profile '%s'\n", profileName); return ERROR; } // 4. 添加新节点到内存链表(关键修复) g_current_edit->next = g_landns_list; g_landns_list = g_current_edit; CLI_PRINT(pCliEnv, "%% Created new profile: %s\n", profileName); return OK; } // 2. 处理类型设置 STATUS cli_lanDnsAddType(cli_env *pCliEnv, char *typeName) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (strcasecmp(typeName, "ip") == 0) { safe_strncpy(g_current_edit->rule.type, "ip", DNSPROXY_LEN_TYPE); } else if (strcasecmp(typeName, "cname") == 0) { safe_strncpy(g_current_edit->rule.type, "cname", DNSPROXY_LEN_TYPE); } else if (strcasecmp(typeName, "forward") == 0) { safe_strncpy(g_current_edit->rule.type, "forward", DNSPROXY_LEN_TYPE); } else { CLI_PRINT(pCliEnv, "%% Invalid type. Use ip/cname/forward\n"); return ERROR; } g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% Rule type set to: %s\n", g_current_edit->rule.type); return OK; } // 3. 域名处理函数 STATUS cli_lanDnsAddDomain(cli_env *pCliEnv, char *domain) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!domain || strlen(domain) == 0) { CLI_PRINT(pCliEnv, "%% Error: Domain cannot be empty\n"); return ERROR; } // 域名长度检查 if (strlen(domain) >= DNSPROXY_LEN_DOMAIN) { CLI_PRINT(pCliEnv, "%% Domain too long (max %d chars)\n", DNSPROXY_LEN_DOMAIN - 1); return ERROR; } safe_strncpy(g_current_edit->rule.domain, domain, DNSPROXY_LEN_DOMAIN); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% Domain set to: %s\n", g_current_edit->rule.domain); return OK; } // 4. 别名处理函数 STATUS cli_lanDnsAddAlias(cli_env *pCliEnv, char *alias) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!alias || strlen(alias) == 0) { CLI_PRINT(pCliEnv, "%% Error: Alias cannot be empty\n"); return ERROR; } // 别名长度检查 if (strlen(alias) >= DNSPROXY_LEN_DOMAIN) { CLI_PRINT(pCliEnv, "%% Alias too long (max %d chars)\n", DNSPROXY_LEN_DOMAIN - 1); return ERROR; } char current_aliases[DNSPROXY_LEN_ALIASES]; strncpy(current_aliases, g_current_edit->rule.aliases, sizeof(current_aliases)); if (strlen(current_aliases) > 0) { // 检查别名数量限制 int count = 1; for (char *p = current_aliases; *p; p++) { if (*p == ',') count++; } if (count >= MAX_ALIAS_COUNT) { CLI_PRINT(pCliEnv, "%% Maximum aliases reached (%d)\n", MAX_ALIAS_COUNT); return ERROR; } strncat(current_aliases, ",", sizeof(current_aliases) - strlen(current_aliases) - 1); } strncat(current_aliases, alias, sizeof(current_aliases) - strlen(current_aliases) - 1); safe_strncpy(g_current_edit->rule.aliases, current_aliases, DNSPROXY_LEN_ALIASES); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% Alias added: %s\n", alias); return OK; } // 5. 状态处理函数 STATUS cli_lanDnsAddStatus(cli_env *pCliEnv, char *status) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (strcasecmp(status, "on") == 0 || strcasecmp(status, "off") == 0) { safe_strncpy(g_current_edit->rule.status, status, DNSPROXY_LEN_STATUS); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% Status set to: %s\n", g_current_edit->rule.status); return OK; } CLI_PRINT(pCliEnv, "%% Invalid status. Use on/off\n"); return ERROR; } // 6. IPv4处理函数 STATUS cli_lanDnsAddIpv4(cli_env *pCliEnv, char *ip) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!ip || !is_valid_ip(ip)) { CLI_PRINT(pCliEnv, "%% Invalid IPv4 address\n"); return ERROR; } // 检查是否是有效的IPv4地址 (AF_INET) struct sockaddr_in sa; if (inet_pton(AF_INET, ip, &(sa.sin_addr)) == 0) { CLI_PRINT(pCliEnv, "%% Not a valid IPv4 address\n"); return ERROR; } safe_strncpy(g_current_edit->rule.ipv4_addrs, ip, DNSPROXY_LEN_IPV4_ADDRS); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% IPv4 set to: %s\n", g_current_edit->rule.ipv4_addrs); return OK; } // 7. IPv6处理函数 STATUS cli_lanDnsAddIpv6(cli_env *pCliEnv, char *ip) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!ip || !is_valid_ip(ip)) { CLI_PRINT(pCliEnv, "%% Invalid IPv6 address\n"); return ERROR; } // 检查是否是有效的IPv6地址 (AF_INET6) struct sockaddr_in6 sa6; if (inet_pton(AF_INET6, ip, &(sa6.sin6_addr)) == 0) { CLI_PRINT(pCliEnv, "%% Not a valid IPv6 address\n"); return ERROR; } safe_strncpy(g_current_edit->rule.ipv6_addrs, ip, DNSPROXY_LEN_IPV6_ADDRS); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% IPv6 set to: %s\n", g_current_edit->rule.ipv6_addrs); return OK; } // 8. CNAME处理函数 STATUS cli_lanDnsAddCname(cli_env *pCliEnv, char *cname) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!cname || strlen(cname) == 0) { CLI_PRINT(pCliEnv, "%% Error: CNAME cannot be empty\n"); return ERROR; } // CNAME长度检查 if (strlen(cname) >= DNSPROXY_LEN_CNAME) { CLI_PRINT(pCliEnv, "%% CNAME too long (max %d chars)\n", DNSPROXY_LEN_CNAME - 1); return ERROR; } safe_strncpy(g_current_edit->rule.cname, cname, DNSPROXY_LEN_CNAME); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% CNAME set to: %s\n", g_current_edit->rule.cname); return OK; } // 9. DNS服务器处理函数 STATUS cli_lanDnsAddDnsServer(cli_env *pCliEnv, char *dns_server) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!dns_server || !is_valid_ip(dns_server)) { CLI_PRINT(pCliEnv, "%% Invalid DNS server address\n"); return ERROR; } char current_servers[DNSPROXY_LEN_DNS_SERVER]; strncpy(current_servers, g_current_edit->rule.dns_server, sizeof(current_servers)); // 检查服务器数量限制 int count = strlen(current_servers) > 0 ? 1 : 0; for (char *p = current_servers; *p; p++) { if (*p == ',') count++; } if (count >= 2) { CLI_PRINT(pCliEnv, "%% Maximum DNS servers reached (2)\n"); return ERROR; } if (strlen(current_servers) > 0) { strncat(current_servers, ",", sizeof(current_servers) - strlen(current_servers) - 1); } strncat(current_servers, dns_server, sizeof(current_servers) - strlen(current_servers) - 1); safe_strncpy(g_current_edit->rule.dns_server, current_servers, DNSPROXY_LEN_DNS_SERVER); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% DNS Server added: %s\n", dns_server); return OK; } // 10. 处理属性删除 STATUS cli_lanDnsDeleteAttribute(cli_env *pCliEnv, char *attr) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile to modify\n"); return ERROR; } if (!attr) { CLI_PRINT(pCliEnv, "%% Error: Attribute name must be specified\n"); return ERROR; } if (strcmp(attr, "alias") == 0) { memset(g_current_edit->rule.aliases, 0, DNSPROXY_LEN_ALIASES); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% Aliases cleared\n"); } else if (strcmp(attr, "ipv4") == 0) { memset(g_current_edit->rule.ipv4_addrs, 0, DNSPROXY_LEN_IPV4_ADDRS); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% IPv4 addresses cleared\n"); } else if (strcmp(attr, "ipv6") == 0) { memset(g_current_edit->rule.ipv6_addrs, 0, DNSPROXY_LEN_IPV6_ADDRS); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% IPv6 addresses cleared\n"); } else if (strcmp(attr, "dns-server") == 0) { memset(g_current_edit->rule.dns_server, 0, DNSPROXY_LEN_DNS_SERVER); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% DNS servers cleared\n"); } else if (strcmp(attr, "cname") == 0) { memset(g_current_edit->rule.cname, 0, DNSPROXY_LEN_CNAME); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% CNAME target cleared\n"); } else { CLI_PRINT(pCliEnv, "%% Error: Unsupported attribute '%s'\n", attr); return ERROR; } return OK; } // 11. LAN网络设置函数 STATUS cli_lanDnsAddLanNetworks(cli_env *pCliEnv, char *networks) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!networks || strlen(networks) == 0) { CLI_PRINT(pCliEnv, "%% Error: LAN networks cannot be empty\n"); return ERROR; } // 验证网络格式 if (strcasecmp(networks, "all") != 0 && !is_valid_network_list(networks)) { CLI_PRINT(pCliEnv, "%% Invalid LAN networks format\n"); return ERROR; } safe_strncpy(g_current_edit->rule.lan_networks, networks, DNSPROXY_LEN_LAN_NETWORKS); g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% LAN networks set to: %s\n", g_current_edit->rule.lan_networks); return OK; } // 12. TTL设置函数 STATUS cli_lanDnsAddTtl(cli_env *pCliEnv, char *ttl_str) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile. Use 'profile <name>' first\n"); return ERROR; } if (!ttl_str || strlen(ttl_str) == 0) { CLI_PRINT(pCliEnv, "%% Error: TTL cannot be empty\n"); return ERROR; } char *endptr; long ttl = strtol(ttl_str, &endptr, 10); if (*endptr != '\0' || ttl <= 0 || ttl > 86400) { // 限制在1秒到1天之间 CLI_PRINT(pCliEnv, "%% Invalid TTL. Must be integer between 1 and 86400\n"); return ERROR; } g_current_edit->rule.ttl = (uint32_t)ttl; g_current_edit->modified = true; CLI_PRINT(pCliEnv, "%% TTL set to: %u\n", g_current_edit->rule.ttl); return OK; } // 13. 提交更改到数据库 STATUS cli_lanDnsCommit(cli_env *pCliEnv) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile to commit\n"); return ERROR; } // 验证规则完整性 if (!g_current_edit->rule.name[0] || !g_current_edit->rule.domain[0] || !g_current_edit->rule.type[0]) { CLI_PRINT(pCliEnv, "%% Incomplete rule: name, domain and type are required\n"); return ERROR; } APPL_ERRCODE ret; CFG_DNSSERVER_RULE_T *rule = &g_current_edit->rule; // 如果是新规则,生成唯一ID if (g_current_edit->is_new) { time_t now = time(NULL); snprintf(rule->id, DNSPROXY_LEN_ID, "dns_%ld_%03d", (long)now, rand() % 1000); } // 保存到数据库 if (g_current_edit->is_new) { ret = dmDnsServerRuleAdd(rule); } else { ret = dmDnsServerRuleSet(rule); } if (ret == ERR_NO_ERROR) { g_current_edit->is_new = false; g_current_edit->modified = false; CLI_PRINT(pCliEnv, "%% Changes committed successfully\n"); // 更新内存链表状态 if (!find_landns_node(rule->name)) { // 确保规则在内存链表中 dlist_add_tail(&g_landns_list, g_current_edit); } return OK; } else { CLI_PRINT(pCliEnv, "%% Error committing changes (code: 0x%X)\n", ret); return ERROR; } } // 14. 显示当前配置 STATUS cli_lanDnsShow(cli_env *pCliEnv) { if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile to show\n"); return ERROR; } CFG_DNSSERVER_RULE_T *rule = &g_current_edit->rule; // 使用固定宽度标签确保对齐 #define FIELD_LABEL_WIDTH 18 CLI_PRINT(pCliEnv, "LAN DNS Rule Configuration Summary:\n"); CLI_PRINT(pCliEnv, "----------------------------------------\n"); PRINT_FIELD("Profile Name", rule->name); PRINT_FIELD("Rule ID", rule->id ? rule->id : "N/A"); PRINT_FIELD("Status", rule->status); PRINT_FIELD("Rule Type", rule->type); PRINT_FIELD("Domain", rule->domain ? rule->domain : "N/A"); PRINT_FIELD_U("TTL", rule->ttl); // 条件显示字段 if (rule->aliases && strlen(rule->aliases) > 0) { PRINT_FIELD("Aliases", rule->aliases); } if (rule->lan_networks && strlen(rule->lan_networks) > 0) { PRINT_FIELD("LAN Networks", rule->lan_networks); } if (rule->ipv4_addrs && strlen(rule->ipv4_addrs) > 0) { PRINT_FIELD("IPv4 Addresses", rule->ipv4_addrs); } if (rule->ipv6_addrs && strlen(rule->ipv6_addrs) > 0) { PRINT_FIELD("IPv6 Addresses", rule->ipv6_addrs); } if (rule->cname && strlen(rule->cname) > 0) { PRINT_FIELD("CNAME Target", rule->cname); } if (rule->dns_server && strlen(rule->dns_server) > 0) { PRINT_FIELD("DNS Servers", rule->dns_server); } PRINT_FIELD("Changes", g_current_edit->modified ? "Modified (not committed)" : "No changes"); PRINT_FIELD("Record Status", g_current_edit->is_new ? "New rule" : "Existing rule"); CLI_PRINT(pCliEnv, "----------------------------------------\n"); return OK; } /****************************************************************************************/ // 15. 退出配置模式函数 STATUS cli_lanDnsCancel(cli_env *pCliEnv) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_current_edit) { CLI_PRINT(pCliEnv, "%% No active profile to cancel\n"); return ERROR; } char profile_name[DNSPROXY_LEN_NAME32]; strncpy(profile_name, g_current_edit->rule.name, sizeof(profile_name)); // 如果是新建但未提交的规则,直接删除 if (g_current_edit->is_new) { // 从内存链表删除 lanDns_node_t *prev = NULL; lanDns_node_t *curr = g_landns_list; while (curr && curr != g_current_edit) { prev = curr; curr = curr->next; } if (curr == g_current_edit) { if (prev) prev->next = g_current_edit->next; else g_landns_list = g_current_edit->next; } // 释放节点内存 free(g_current_edit); } else { // 对于已存在的规则,丢弃修改 g_current_edit->modified = false; // 恢复原始值(从数据库重新加载) CFG_DNSSERVER_RULE_T *db_rule = NULL; APPL_ERRCODE ret = dmDnsServerRuleGetByName(profile_name, &db_rule); if (ret == ERR_NO_ERROR && db_rule) { memcpy(&g_current_edit->rule, db_rule, sizeof(CFG_DNSSERVER_RULE_T)); DNSPROXYSHELL_FREE(db_rule); } } g_current_edit = NULL; CLI_PRINT(pCliEnv, "%% Cancelled editing for profile: %s\n", profile_name); return OK; } // 16. 退出配置模式函数(不带参数) STATUS cli_lanDnsExit(cli_env *pCliEnv) { return cli_lanDnsCancel(pCliEnv); } // 18. 显示所有profile的函数 STATUS cli_lanDnsShowAll(cli_env *pCliEnv) { // 确保规则已加载 if (ensure_dns_rules_loaded() != OK) { return ERROR; } if (!g_landns_list) { CLI_PRINT(pCliEnv, "%% No LAN DNS profiles configured\n"); return OK; } int count = dlist_count(g_landns_list); CLI_PRINT(pCliEnv, "┌───────────────────────────────────────────────────────┐\n"); CLI_PRINT(pCliEnv, "│ Configured LAN DNS Profiles (%d) │\n", count); CLI_PRINT(pCliEnv, "├──────────────┬──────────────┬──────────┬───────────────┤\n"); CLI_PRINT(pCliEnv, "│ Profile Name │ Status │ Type │ Domain │\n"); CLI_PRINT(pCliEnv, "├──────────────┼──────────────┼──────────┼───────────────┤\n"); lanDns_node_t *node; dlist_for_each_entry(node, g_landns_list) { CFG_DNSSERVER_RULE_T *rule = &node->rule; // 截断过长的字符串 char name_display[14] = {0}; strncpy(name_display, rule->name, 12); if (strlen(rule->name) > 12) strcat(name_display, ".."); char domain_display[15] = {0}; strncpy(domain_display, rule->domain, 13); if (strlen(rule->domain) > 13) strcat(domain_display, ".."); CLI_PRINT(pCliEnv, "│ %-12s │ %-12s │ %-8s │ %-13s │\n", name_display, rule->status, rule->type, domain_display); } CLI_PRINT(pCliEnv, "└──────────────┴──────────────┴──────────┴───────────────┘\n"); return OK; }
10-21
char* filename_completion_function(const char* text, int state) { static DIR* dir = NULL; static char * filename = NULL, dirname = NULL; static size_t filename_len = 0; static int is_virtual = 0; // 新增:虚拟路径标志 static char virtual_prefix[32] = “”; // 新增:虚拟路径前缀 struct dirent entry; char* temp; size_t len; if (state == 0 || dir == NULL) { if (dir != NULL) { closedir(dir); dir = NULL; } // 新增:虚拟路径处理 char resolved_path[PATH_MAX]; is_virtual = 0; resolve_virtual_path(text, resolved_path, sizeof(resolved_path), &is_virtual); // 如果是虚拟路径,保存原始前缀 if (is_virtual) { // 提取虚拟路径前缀(如"flash:/") const char* end = strchr(text, '/'); if (end) { size_t prefix_len = end - text + 1; strncpy(virtual_prefix, text, prefix_len); virtual_prefix[prefix_len] = '\0'; } else { strcpy(virtual_prefix, text); } } // 使用解析后的路径进行后续处理 const char* process_text = is_virtual ? resolved_path : text; temp = strrchr(process_text, '/'); if (temp) { temp++; filename = XREALLOC(MTYPE_TMP, filename, strlen(temp) + 1); (void)strcpy(filename, temp); len = temp - process_text; /* including last slash */ dirname = XREALLOC(MTYPE_TMP, dirname, len + 1); (void)strncpy(dirname, process_text, len); dirname[len] = '\0'; } else { filename = XSTRDUP(0, process_text); dirname = NULL; } /* support for ``~user'' syntax */ if (dirname && *dirname == '~') { temp = tilde_expand(dirname); dirname = XREALLOC(MTYPE_TMP, dirname, strlen(temp) + 1); (void)strcpy(dirname, temp); /* safe */ XFREE(0, temp); /* no longer needed */ } /* will be used in cycle */ filename_len = strlen(filename); if (filename_len == 0) return (NULL); /* no expansion possible */ dir = opendir(dirname ? dirname : "."); if (!dir) return (NULL); /* cannot open the directory */ } /* find the match */ while ((entry = readdir(dir)) != NULL) { /* otherwise, get first entry where first */ /* filename_len characters are equal */ if (entry->d_name[0] == filename[0] #if defined(__SVR4) || defined(linux) && strlen(entry->d_name) >= filename_len #else && entry->d_namlen >= filename_len #endif && strncmp(entry->d_name, filename, filename_len) == 0) break; } if (entry) { /* match found */ struct stat stbuf; #if defined(__SVR4) || defined(linux) len = strlen(entry->d_name) + #else len = entry->d_namlen + #endif ((dirname) ? strlen(dirname) : 0) + 1 + 1; temp = XMALLOC(0, len); // 构建完整路径 (void)sprintf(temp, "%s%s", dirname ? dirname : "", entry->d_name); /* safe */ /* test, if it's directory */ if (stat(temp, &stbuf) == 0 && S_ISDIR(stbuf.st_mode)) strcat(temp, "/"); /* safe */ // 新增:如果是虚拟路径,转换回原始格式 if (is_virtual) { // 计算真实路径前缀长度 size_t real_prefix_len = strlen(dirname ? dirname : ""); // 只保留文件名部分 const char* file_part = temp + real_prefix_len; // 重建虚拟路径 char* new_temp = XMALLOC(0, strlen(virtual_prefix) + strlen(file_part) + 1); sprintf(new_temp, "%s%s", virtual_prefix, file_part); XFREE(0, temp); temp = new_temp; } } else { temp = NULL; } return (temp); } int resolve_virtual_path(const char* text, char* out_path, size_t out_len, int* is_virtual) { *is_virtual = 0; if (strncmp(text, "flash:/", 7) == 0) { snprintf(out_path, out_len, "/mnt/switch/%s", text + 7); *is_virtual = 1; return 1; } // 更多虚拟路径? // if (strncmp(text, "tftp:/", 6) == 0) { ... } // 普通路径 snprintf(out_path, out_len, "%s", text); return 1; } char** list_directory_entries(const char* path, int* count, int* max_len) { DIR* dir = opendir(path); if (!dir) return NULL; struct dirent* entry; char** entries = NULL; int entry_count = 0; *max_len = 0; while ((entry = readdir(dir)) != NULL) { // 跳过 . 和 .. if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; if (entry->d_name[0] == '.') continue; if (entry->d_type == DT_LNK) continue; // 获取文件状态 char full_path[PATH_MAX]; snprintf(full_path, sizeof(full_path), "%s/%s", path, entry->d_name); struct stat st; if (stat(full_path, &st) != 0) continue; // 计算显示名称长度 int name_len = strlen(entry->d_name); if (S_ISDIR(st.st_mode)) { name_len++; // 为斜杠预留空间 } // 更新最大长度 if (name_len > *max_len) { *max_len = name_len; } // 分配条目内存 char** new_entries = XREALLOC(MTYPE_TMP, entries, (entry_count + 1) * sizeof(char*)); if (!new_entries) break; entries = new_entries; entries[entry_count] = XMALLOC(MTYPE_TMP, name_len + 2); // 构建显示名称 if (S_ISDIR(st.st_mode)) { snprintf(entries[entry_count], name_len + 1, "%s/", entry->d_name); } else { strcpy(entries[entry_count], entry->d_name); } entry_count++; } closedir(dir); // 添加结束标记 if (entries) { char** new_entries = XREALLOC(MTYPE_TMP, entries, (entry_count + 1) * sizeof(char*)); if (new_entries) { entries = new_entries; entries[entry_count] = NULL; } } *count = entry_count; return entries; } // 修改后的命令补全函数 char** cmlsh_completion(char* text, int start, int end) { char** matches; char** filename_matches = NULL; int is_fs_command = 0; int fs_cmd_len = 0; int i; // 获取当前行信息 const LineInfo* li; li = el_line(e); rl_line_buffer = (char*)li->buffer; rl_end = li->lastchar - li->buffer; rl_line_buffer[rl_end] = '\0'; // 初始补全尝试 matches = completion_matches(text, cmlsh_completion_matches); // 检查是否是文件系统命令 for (i = 0; cmlsh_fs_commands[i]; i++) { int len = strlen(cmlsh_fs_commands[i]); if (strncmp(rl_line_buffer, cmlsh_fs_commands[i], len) == 0 && (rl_line_buffer[len] == ' ' || rl_line_buffer[len] == '\0')) { is_fs_command = 1; fs_cmd_len = len; break; } } size_t text_len = strlen(text); // 如果是文件系统命令且输入以/结尾,尝试自动列出目录内容 if (is_fs_command && start > fs_cmd_len && text_len > 0 && text[text_len - 1] == '/') { char cwd[PATH_MAX]; if (!getcwd(cwd, sizeof(cwd))) strcpy(cwd, "."); char fullpath[PATH_MAX]; int is_virtual = 0; char resolved_text[PATH_MAX]; // 解析虚拟路径 resolve_virtual_path(text, resolved_text, sizeof(resolved_text), &is_virtual); if (is_virtual) { snprintf(fullpath, sizeof(fullpath), "%s", resolved_text); } else { snprintf(fullpath, sizeof(fullpath), "%s/%.*s", cwd, (int)(text_len - 1), text); } // 确保路径以/结尾 int fullpath_len = strlen(fullpath); if (fullpath_len > 0 && fullpath[fullpath_len - 1] != '/') { if (fullpath_len < sizeof(fullpath) - 1) { strcat(fullpath, "/"); } } struct stat st; if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode)) { int count = 0, max_len = 0; char** sub_matches = list_directory_entries(fullpath, &count, &max_len); if (sub_matches && count > 0) { rl_display_match_list_tmp(sub_matches, count, max_len); for (int i = 0; i < count; i++) XFREE(MTYPE_TMP, sub_matches[i]); XFREE(MTYPE_TMP, sub_matches); return NULL; } } } // 命令补全失败 → 尝试文件名补全 if (!matches || !matches[0]) { // 清理之前的匹配结果 if (matches) { for (i = 0; matches[i]; i++) XFREE(MTYPE_TMP, matches[i]); XFREE(MTYPE_TMP, matches); matches = NULL; } // 如果是文件系统命令,获取文件名补全 if (is_fs_command && start > fs_cmd_len) { filename_matches = completion_matches(text, filename_completion_function); } // ========== 虚拟路径前缀匹配处理 ========== const char* virtual_prefix = "flash:/"; size_t virtual_prefix_len = strlen(virtual_prefix); // 检查输入是否匹配虚拟路径前缀 int should_add_virtual = 0; if (strncmp(text, virtual_prefix, text_len) == 0) { // 完全匹配前缀 should_add_virtual = 1; } else if (text_len < virtual_prefix_len && strncmp(virtual_prefix, text, text_len) == 0) { // 部分匹配前缀(如"f"、"fl"等) should_add_virtual = 1; } // 检查是否已经包含虚拟路径项 int virtual_already_exists = 0; if (filename_matches) { for (i = 0; filename_matches[i]; i++) { if (strcmp(filename_matches[i], virtual_prefix) == 0) { virtual_already_exists = 1; break; } } } // 如果需要添加虚拟路径项且尚未存在 if (should_add_virtual && !virtual_already_exists) { int real_count = 0; if (filename_matches) { while (filename_matches[real_count]) real_count++; } // 创建新数组(真实+虚拟+NULL) int total_count = real_count + 1; char** merged = (char**)XCALLOC(MTYPE_TMP, (total_count + 1) * sizeof(char*)); // 复制真实路径补全项 for (i = 0; i < real_count; i++) { merged[i] = filename_matches[i]; } // 添加虚拟路径补全项 merged[real_count] = XSTRDUP(MTYPE_TMP, virtual_prefix); merged[real_count + 1] = NULL; // 释放原数组容器(不释放字符串内容) if (filename_matches) { XFREE(MTYPE_TMP, filename_matches); } filename_matches = merged; } // ========== 虚拟路径前缀匹配处理结束 ========== if (!filename_matches || !filename_matches[0]) { return NULL; } // 处理单个匹配项(可能是目录或虚拟路径) if (filename_matches[1] == NULL) { const char* match = filename_matches[0]; int match_len = strlen(match); // 如果是目录(以 / 结尾)或虚拟路径 if (match_len > 0 && (match[match_len - 1] == '/' || strncmp(match, virtual_prefix, virtual_prefix_len) == 0)) { size_t base_len = (strncmp(match, virtual_prefix, virtual_prefix_len) == 0) ? match_len : match_len - 1; // 检查是否应该自动展开 int should_expand = 0; if (text_len > 0 && text[text_len - 1] == '/') { should_expand = 1; } // 只在输入以斜杠结尾时展开目录 if (should_expand) { char cwd[PATH_MAX]; if (!getcwd(cwd, sizeof(cwd))) strcpy(cwd, "."); char fullpath[PATH_MAX]; int is_virtual = 0; // 处理虚拟路径 if (strncmp(match, virtual_prefix, virtual_prefix_len) == 0) { resolve_virtual_path(match, fullpath, sizeof(fullpath), &is_virtual); } else { snprintf(fullpath, sizeof(fullpath), "%s/%.*s", cwd, (int)base_len, match); } // 确保路径以/结尾 int fullpath_len = strlen(fullpath); if (fullpath_len > 0 && fullpath[fullpath_len - 1] != '/') { if (fullpath_len < sizeof(fullpath) - 1) { strcat(fullpath, "/"); } } struct stat st; if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode)) { int count = 0, max_len = 0; char** sub_matches = list_directory_entries(fullpath, &count, &max_len); if (sub_matches && count > 0) { rl_display_match_list_tmp(sub_matches, count, max_len); for (int i = 0; i < count; i++) XFREE(MTYPE_TMP, sub_matches[i]); XFREE(MTYPE_TMP, sub_matches); // 清理 filename_matches XFREE(MTYPE_TMP, filename_matches[0]); XFREE(MTYPE_TMP, filename_matches); return NULL; } } } } } return filename_matches; } return matches; } 现在这些代码的逻辑有点小问题: dir fla dir flash:/ x/ cfg/ ea.txt etc/ fa/ fb/ fc/ fd.txt fl/ home/ ins/ installers/ logfile/ xx/ 2.txt 3.txt 4.txt dir flash:/e flash:/etc/ flash:/ea.txt dir flash:/e flash:/etc/ flash:/ea.txt dir flash:/et dir flash:/etc/ sa/ nos.conf nos.conf.back nos.conf.bak ssh/ CML_DB.db dir flash:/etc/n dir flash:/nos.conf dir flash:/etc/n却变成了dir flash:/nos.conf,没有etc目录了,如何修复?最小的改动,在上边给出的代码的基础上
08-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值