动态创建的数组 char* A = new char[5]{ 'c', 'y','u','p','j' }; 如何获取其长度

#include <iostream>
#include <algorithm>
//using namespace std;
using std::cin;
using std::cout;
using std::endl;

template <typename T>
void increaseSize(T*& A, int oldsize, int newsize)
{
	if (oldsize < 0 || newsize < 0 || oldsize > newsize) throw " ";
	T* temp = new T[newsize];
	std::copy(A, A + oldsize, temp);
	delete[] A;
	A = temp;
}

int main()
{
	int* a = new int[10]{ 1,2, 3, 4, 5, 6, 7,8,9 };
	char* A = new char[5]{ 'c', 'y','u','p','j' };

	int result = sizeof(a);//4 指向int的指针 (地址)所占的内存大小 
	result = sizeof(A) / sizeof(A[0]);//1
	
	increaseSize(A, 5, 10);
    //如何获取数组A的尺寸 10?

	delete[] a;
	delete[] A;
	cout << endl << "ok" << endl;
	return 0;
}
#include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <unistd.h> #include <ctype.h> #include <strings.h> #include <string.h> #include <sys/stat.h> #include <pthread.h> #include <sys/wait.h> #include <stdlib.h> #include <dirent.h> #include <time.h> #include <errno.h> // 添加errno头文件 #define ISspace(x) isspace((int)(x)) #define SERVER_STRING "Server: jdbhttpd/0.2.0\r\n" // 函数声明 void* accept_request(void *); void bad_request(int); void cat(int, FILE *); void error_die(const char *); int get_line(int, char *, int); void headers(int, const char *, const char *); void not_found(int); void serve_file(int, const char *); void serve_file_post(int, const char *); int startup(unsigned short *); void unimplemented(int); void forbidden(int); void serve_directory(int, const char *); void url_decode(char *, const char *); const char *get_mime_type(const char *); void log_request(const char *, const char *, int); void set_socket_timeout(int, int); /**********************************************************************/ /* 处理客户端请求 */ /**********************************************************************/ void* accept_request(void *pclient) { int client = *(int*)pclient; free(pclient); set_socket_timeout(client, 2); char buf[65536]; int numchars; char method[255]; char url[255]; char path[1024]; // 增加路径长度 char decoded_url[1024]; // 存储解码后的URL size_t i, j; struct stat st; // 获取请求的第一行 numchars = get_line(client, buf, sizeof(buf)); i = 0; j = 0; while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) { method[i] = buf[j]; i++; j++; } method[i] = '\0'; // 只支持GET和POST方法 if (strcasecmp(method, "GET") && strcasecmp(method, "POST") && strcasecmp(method, "OPTIONS")) { unimplemented(client); return NULL; } // 读取URL i = 0; while (ISspace(buf[j]) && (j < sizeof(buf))) j++; while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) { url[i] = buf[j]; i++; j++; } url[i] = '\0'; // URL解码 url_decode(decoded_url, url); // 记录请求日志 log_request(method, decoded_url, client); // 构建文件路径(使用当前目录) sprintf(path, "web%s", decoded_url); // 防止路径遍历攻击 if (strstr(path, "..")) { forbidden(client); close(client); return NULL; } // 处理目录请求 if (path[strlen(path) - 1] == '/') strcat(path, "Index.html"); // 检查文件/目录是否存在 if (stat(path, &st) == -1) { // 检查是否存在.html扩展名文件 char alt_path[1024]; sprintf(alt_path, "%s.html", path); if (stat(alt_path, &st) == 0) { strcpy(path, alt_path); } else { /* 跳过剩余头部 */ while ((numchars > 0) && strcmp("\n", buf)) numchars = get_line(client, buf, sizeof(buf)); not_found(client); close(client); return NULL; } } // 如果是目录 if ((st.st_mode & S_IFMT) == S_IFDIR) { // 检查目录中是否有index.html char index_path[1024]; sprintf(index_path, "%s/Index.html", path); if (stat(index_path, &st) == 0) { strcpy(path, index_path); } else { // 显示目录列表 serve_directory(client, path); close(client); return NULL; } } if (strcasecmp(method, "GET") == 0) { serve_file(client, path); } else if (strcasecmp(method, "POST") == 0) { serve_file_post(client, path); } close(client); return NULL; } /**********************************************************************/ /* URL解码 */ /**********************************************************************/ void url_decode(char *dest, const char *src) { char *p = dest; while (*src) { if (*src == '%') { if (src[1] && src[2]) { char hex[3] = {src[1], src[2], '\0'}; *p++ = (char)strtol(hex, NULL, 16); src += 3; } else { *p++ = *src++; } } else if (*src == '+') { *p++ = ' '; src++; } else { *p++ = *src++; } } *p = '\0'; } /**********************************************************************/ /* 获取MIME类型 */ /**********************************************************************/ const char *get_mime_type(const char *filename) { const char *dot = strrchr(filename, '.'); if (!dot) return "text/plain"; if (strcasecmp(dot, ".html") == 0 || strcasecmp(dot, ".htm") == 0) return "text/html"; if (strcasecmp(dot, ".css") == 0) return "text/css"; if (strcasecmp(dot, ".js") == 0) return "application/javascript"; if (strcasecmp(dot, ".jpg") == 0 || strcasecmp(dot, ".jpeg") == 0) return "image/jpeg"; if (strcasecmp(dot, ".png") == 0) return "image/png"; if (strcasecmp(dot, ".gif") == 0) return "image/gif"; if (strcasecmp(dot, ".json") == 0) return "application/json"; if (strcasecmp(dot, ".ico") == 0) return "image/x-icon"; return "text/plain"; } /**********************************************************************/ /* 记录请求日志 */ /**********************************************************************/ void log_request(const char *method, const char *url, int client) { time_t now = time(NULL); struct tm *tm = localtime(&now); char timestamp[64]; strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm); struct sockaddr_in addr; socklen_t addr_len = sizeof(addr); getpeername(client, (struct sockaddr*)&addr, &addr_len); char *ip = inet_ntoa(addr.sin_addr); printf("[%s] %s %s %s\n", timestamp, ip, method, url); } /**********************************************************************/ /* 处理目录列表 */ /**********************************************************************/ void serve_directory(int client, const char *path) { char buf[4096]; // 发送HTTP头 sprintf(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); // 发送HTML头部 sprintf(buf, "<html><head><title>Index of %s</title></head>", path); send(client, buf, strlen(buf), 0); sprintf(buf, "<body><h1>Index of %s</h1><ul>", path); send(client, buf, strlen(buf), 0); // 打开目录 DIR *dir = opendir(path); if (dir) { struct dirent *ent; while ((ent = readdir(dir)) != NULL) { // 跳过隐藏文件 if (ent->d_name[0] == '.') continue; char full_path[1024]; sprintf(full_path, "%s/%s", path, ent->d_name); struct stat st; stat(full_path, &st); char size_buf[32]; if (S_ISDIR(st.st_mode)) { strcpy(size_buf, "[DIR]"); } else { if (st.st_size < 1024) { sprintf(size_buf, "%ld B", st.st_size); } else if (st.st_size < 1024 * 1024) { sprintf(size_buf, "%.1f KB", st.st_size / 1024.0); } else { sprintf(size_buf, "%.1f MB", st.st_size / (1024.0 * 1024)); } } sprintf(buf, "<li><a href=\"%s\">%s</a> - %s</li>", ent->d_name, ent->d_name, size_buf); send(client, buf, strlen(buf), 0); } closedir(dir); } // 发送HTML尾部 sprintf(buf, "</ul></body></html>\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* 400 Bad Request */ /**********************************************************************/ void bad_request(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>400 Bad Request</h1></body></html>\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* 403 Forbidden */ /**********************************************************************/ void forbidden(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 403 Forbidden\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>403 Forbidden</h1><p>Access to this resource is denied.</p></body></html>\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* 发送文件内容 */ /**********************************************************************/ void cat(int client, FILE *resource) { char buf[65536]; fgets(buf, sizeof(buf), resource); while (!feof(resource)) { send(client, buf, strlen(buf), 0); fgets(buf, sizeof(buf), resource); } } /**********************************************************************/ /* 错误处理 */ /**********************************************************************/ void error_die(const char *sc) { perror(sc); exit(1); } /**********************************************************************/ /* 读取一行 */ /**********************************************************************/ int get_line(int sock, char *buf, int size) { int i = 0; char c = '\0'; int n; while ((i < size - 1) && (c != '\n')) { n = recv(sock, &c, 1, 0); if (n > 0) { if (c == '\r') { n = recv(sock, &c, 1, MSG_PEEK); if ((n > 0) && (c == '\n')) recv(sock, &c, 1, 0); else c = '\n'; } buf[i] = c; i++; } else c = '\n'; } buf[i] = '\0'; return(i); } /**********************************************************************/ /* 发送HTTP头 */ /**********************************************************************/ void headers(int client, const char *filename, const char *content_type) { char buf[1024]; (void)filename; // 未使用 strcpy(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0); strcpy(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: %s\r\n", content_type); send(client, buf, strlen(buf), 0); strcpy(buf, "\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* 404 Not Found */ /**********************************************************************/ void not_found(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>404 Not Found</h1><p>The requested URL was not found on this server.</p></body></html>\r\n"); send(client, buf, strlen(buf), 0); } /**********************************************************************/ /* 服务静态文件(修复二进制文件处理) */ /**********************************************************************/ void serve_file(int client, const char *filename) { FILE *resource = NULL; int numchars = 1; char buf[1024]; size_t bytes_read; long file_size; // 丢弃请求头 buf[0] = 'A'; buf[1] = '\0'; while ((numchars > 0) && strcmp("\n", buf)) numchars = get_line(client, buf, sizeof(buf)); // 使用二进制模式打开文件 resource = fopen(filename, "rb"); if (resource == NULL) { not_found(client); return; } // 获取文件大小 fseek(resource, 0, SEEK_END); file_size = ftell(resource); fseek(resource, 0, SEEK_SET); // 发送HTTP头 const char *content_type = get_mime_type(filename); // 创建并发送头部 char header_buf[2048]; sprintf(header_buf, "HTTP/1.0 200 OK\r\n"); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, SERVER_STRING); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "Content-Type: %s\r\n", content_type); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "Content-Length: %ld\r\n", file_size); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "\r\n"); send(client, header_buf, strlen(header_buf), 0); // 使用二进制模式发送文件内容 while ((bytes_read = fread(buf, 1, sizeof(buf), resource)) > 0) { ssize_t sent = send(client, buf, bytes_read, MSG_NOSIGNAL); if (sent < 0) { // 处理发送错误(如连接关闭) break; } } fclose(resource); } /**********************************************************************/ /* 服务静态文件(专为POST请求设计,包含请求体处理) */ /**********************************************************************/ void serve_file_post(int client, const char *filename) { FILE *resource = NULL; int numchars = 1; char buf[1024]; size_t bytes_read; long file_size; size_t content_length = 0; // 用于存储请求体长度 // 1. 读取并处理请求头 while ((numchars > 0) && strcmp("\n", buf) != 0) { numchars = get_line(client, buf, sizeof(buf)); // 解析Content-Length头部 if (strncasecmp(buf, "Content-Length:", 15) == 0) { char *endptr; unsigned long temp = strtoul(buf + 15, &endptr, 10); if (endptr != buf + 15 && temp <= SIZE_MAX) { content_length = (size_t)temp; } } } // 2. 读取并丢弃请求体 if (content_length > 0) { size_t total_read = 0; struct timeval tv; tv.tv_sec = 5; // 设置5秒超时 tv.tv_usec = 0; setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); while (total_read < content_length) { size_t to_read = sizeof(buf); size_t remaining = content_length - total_read; if (to_read > remaining) { to_read = remaining; } bytes_read = recv(client, buf, to_read, 0); if (bytes_read <= 0) { if (bytes_read == 0) { break; // 连接关闭 } else if (errno == EAGAIN || errno == EWOULDBLOCK) { fprintf(stderr, "Request body read timeout\n"); break; // 超时 } else { perror("recv failed"); break; // 错误 } } total_read += bytes_read; } // 恢复默认超时设置 tv.tv_sec = 0; tv.tv_usec = 0; setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); } // 3. 打开并发送文件(与serve_file相同) resource = fopen(filename, "rb"); if (resource == NULL) { not_found(client); return; } // 获取文件大小 fseek(resource, 0, SEEK_END); file_size = ftell(resource); fseek(resource, 0, SEEK_SET); // 发送HTTP头 const char *content_type = get_mime_type(filename); char header_buf[2048]; sprintf(header_buf, "HTTP/1.0 200 OK\r\n"); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, SERVER_STRING); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "Content-Type: %s\r\n", content_type); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "Content-Length: %ld\r\n", file_size); send(client, header_buf, strlen(header_buf), 0); sprintf(header_buf, "\r\n"); send(client, header_buf, strlen(header_buf), 0); // 发送文件内容 while ((bytes_read = fread(buf, 1, sizeof(buf), resource)) > 0) { ssize_t sent = send(client, buf, bytes_read, MSG_NOSIGNAL); if (sent < 0) { // 处理发送错误 break; } } fclose(resource); } /**********************************************************************/ /* 启动服务器 */ /**********************************************************************/ int startup(unsigned short *port) { int httpd = 0; struct sockaddr_in name; /* 创建socket */ httpd = socket(PF_INET, SOCK_STREAM, 0); if (httpd == -1) error_die("socket"); memset(&name, 0, sizeof(name)); name.sin_family = AF_INET; name.sin_port = htons(*port); name.sin_addr.s_addr = htonl(INADDR_ANY); /* 绑定地址 */ if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0) error_die("bind"); /* 若预设端口为0,随机取用可用端口*/ if (*port == 0) { socklen_t namelen = sizeof(name); if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) error_die("getsockname"); *port = ntohs(name.sin_port); } /* 监听连接 */ if (listen(httpd, 5) < 0) error_die("listen"); return(httpd); } /**********************************************************************/ /* 501 Not Implemented */ /**********************************************************************/ void unimplemented(int client) { char buf[1024]; sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<html><body><h1>501 Not Implemented</h1><p>The requested method is not implemented.</p></body></html>\r\n"); send(client, buf, strlen(buf), 0); } // 设置套接字发送超时(在主循环accept后调用) void set_socket_timeout(int sockfd, int timeout_sec) { struct timeval tv; tv.tv_sec = timeout_sec; // 超时秒数 tv.tv_usec = 0; if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { perror("setsockopt SO_SNDTIMEO failed"); } } /**********************************************************************/ /* 主函数 */ /**********************************************************************/ int main(void) { int server_sock = -1; unsigned short port = 8080; int client_sock = -1; struct sockaddr_in client_name; socklen_t client_name_len = sizeof(client_name); pthread_t newthread; server_sock = startup(&port); printf("HTTP server running on port %d\n", port); signal(SIGPIPE, SIG_IGN); while (1) { /* 接受连接 */ client_sock = accept(server_sock,(struct sockaddr *)&client_name,&client_name_len); if (client_sock == -1) { perror("accept"); continue; // 继续接受新连接,而不是退出 } // 动态分配内存传递socket描述符 int *pclient = malloc(sizeof(int)); if (!pclient) { perror("malloc failed"); close(client_sock); continue; } *pclient = client_sock; // 创建线程处理请求(不再在主线程设置超时) if (pthread_create(&newthread, NULL, accept_request, pclient) != 0) { perror("pthread_create"); free(pclient); close(client_sock); } else { // 分离线程,使其结束后自动释放资源 pthread_detach(newthread); } } close(server_sock); return(0); } 以上是我通过 多线程方式 实现的webserver. 现在我想通过I/O多路复用的方式,实现同样的功能 请给出完整的实现
08-12
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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值