一种不需要客户端ip的命令行远程工具

项目地址:Academy remote system: 一种不需要客户端ip的命令行远程工具 - Gitee.com

项目介绍:

传统的远程命令行工具如ssh,scp都需要目标服务器的ip才可以连接。

我设计的这款命令行远程工具可以基于多个中间服务器进行远程,而不需要设置目标ip。

部分代码(20240317年更新)

academy_client-aca.c:

cpp 复制代码
#include <stdio.h>
#include "yamlreader.h"
#include "codereader.h"
#include <string.h>
//#define DEBUGMODE
void show_copyright(void){
    printf("@copyright2024 Anchengan\n");
}
void show_help(void){
    printf("Help document for aca software:\n");
    printf("aca [authkey of remote client] [path to settings.yaml] [password of remote client]\n");
    printf("aca listenmode [path to settings.yaml]\n");
}
int is_path_in_ld_library_path(const char *path_to_check) {
    const char *ld_library_path = getenv("LD_LIBRARY_PATH");
    if (ld_library_path == NULL) {
        // 如果LD_LIBRARY_PATH未设置,则直接返回0(不在其中)
        return 0;
    }

    // 分割LD_LIBRARY_PATH并检查每个路径
    char *path_copy = strdup(ld_library_path); // 复制环境变量值
    char *token = strtok(path_copy, ":");
    while (token != NULL) {
        // 比较当前路径和要检查的路径
        if (strcmp(token, path_to_check) == 0) {
            free(path_copy); // 释放内存
            return 1; // 找到路径,返回1
        }
        token = strtok(NULL, ":"); // 继续查找下一个路径
    }

    free(path_copy); // 释放内存
    return 0; // 未找到路径,返回0
}
char* remove_char(const char* str, char c) {
    int count = 0;
    for (const char* p = str; *p; p++) {
        if (*p != c) {
            count++;
        }
    }

    // 分配足够的空间来存储新字符串
    char* result = (char*)malloc(count + 1);  // +1 为 '\0'
    if (!result) {
        return NULL;  // 内存分配失败
    }

    int i = 0;
    for (const char* p = str; *p; p++) {
        if (*p != c) {
            result[i++] = *p;
        }
    }
    result[i] = '\0';  // 确保新字符串以 '\0' 结尾

    return result;
}
int main(int argc, char *argv[]) {
    // 打印传递的参数
    if(argc-1==0){
	show_copyright();
        printf("Enter -h or --help after aca for help.\n");
	printf("Example:\naca -h\naca --help\n");
	return 0;
    }
    for(int i=1;i<=argc-1;i++){
        if(strcmp(argv[i],"-h")==0 || strcmp(argv[i],"--help")==0){
            show_copyright();
            show_help();
            return 0;
        }
    }
    if(argc-1>3){
        printf("Unknown usage of aca...\n");
        show_help();
        return 0;
    }
    int yamllength=strlen(argv[2]);
    if(yamllength<5 || argv[2][yamllength-5]!='.' || argv[2][yamllength-4]!='y' || argv[2][yamllength-3]!='a' || argv[2][yamllength-2]!='m' || argv[2][yamllength-1]!='l'){
        printf("Please check your path to settings.yaml!\n");
        printf("Example:./settings.yaml\n");
        show_help();
	return 0;
    }
    int islistenmode=0;
    if (strcmp(argv[1],"listenmode")==0){
        if(argc-1>2){
            printf("Unknown usage of aca...\n");
            show_help();
            return 0;
        }
	printf("listenmode setup successfully...\n");
	islistenmode=1;
    }else{
        printf("your authkey of remote client is:%s\n",argv[1]);
    }
    printf("setting env...\n");
    int env_return_value;
    const char *path_to_check = "./"; // 要检查的路径  
    const char *bashrc_path = getenv("HOME"); // 获取用户主目录
    if (bashrc_path == NULL) {
        perror("Error getting HOME environment variable");
        return EXIT_FAILURE;
    }

    // 拼接.bashrc文件的完整路径
    char full_path[1024];
    snprintf(full_path, sizeof(full_path), "%s/.bashrc", bashrc_path);

    // 打开.bashrc文件
    FILE *file = fopen(full_path, "r");
    if (file == NULL) {
        perror("Error opening ~/.bashrc");
        return EXIT_FAILURE;
    }

    // 要搜索的内容
    const char *search_content = "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./";
    char line[1024]; // 假设每行不会超过1023个字符

    // 逐行读取并搜索内容
    int found = 0; // 标记是否找到内容
    while (fgets(line, sizeof(line), file)) {
        if (strstr(line, search_content) != NULL) {
#ifdef DEBUGMODE
            printf("Found the content in ~/.bashrc: %s", line);
#endif
	    found = 1; // 找到内容,设置标记
            break; // 如果只需要找到一次就退出循环
        }
    }

    

    // 关闭文件
    fclose(file);

    if (!is_path_in_ld_library_path(path_to_check) && !found) {  
          
 
        // 使用system()函数运行命令行命令
        env_return_value = system("echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./' >> ~/.bashrc");

        // 检查命令的退出码
#ifdef DEBUGMODE

        if (env_return_value == -1) {
            // system()函数调用失败
            // 在这种情况下,你可以查看errno来获取错误详情(需要包含errno.h头文件)
            perror("system() failed");
        } else if (WIFEXITED(env_return_value)) {
            // 命令正常退出,WEXITSTATUS(env_return_value)可以获取命令的退出状态
            printf("Command exited with status %d\n", WEXITSTATUS(env_return_value));
        } else if (WIFSIGNALED(env_return_value)) {
            // 命令因为接收到信号而终止,WTERMSIG(env_return_value)可以获取信号的编号
            printf("Command terminated by signal %d\n", WTERMSIG(env_return_value));
        }
#endif
        printf("env set successfully...\n");
        printf("please run: source ~/.bashrc\n");
	return 0;
    }else if(!is_path_in_ld_library_path(path_to_check) && found){
        printf("please run: source ~/.bashrc\n");
	return 0;
    }
    #include "libsender.h"
    #include "libreceiver.h"
    printf("your path to settings.yaml is:%s\n",argv[2]);    
    
    printf("Reading settings.yaml...\n");
    struct item_ret* yaml_data = get_item(argv[2]);
    struct ip* ip_reader;
    struct port* port_reader;
    ip_reader=yaml_data->ip_items;
    port_reader=yaml_data->port_items;
    char mac_device[20];

    char c = ':';   
    strcpy(mac_device,yaml_data->mac_device);
    int iplength = 0;
    struct ip* ip_pin = ip_reader;
    while (ip_pin != NULL) {
        iplength++;
        ip_pin = ip_pin->next;
    }
    
    char send_ip_info[iplength*100];
    char send_port_info[iplength*100];
    while (ip_reader != NULL){
        printf("ip:%s\n", ip_reader->ip);
	strcat(send_ip_info,ip_reader->ip);
	strcat(send_ip_info,"-");
        ip_reader = ip_reader->next;
	for(int i=0;i<2;i++){
            printf("port:%s\n", port_reader->port);
	    strcat(send_port_info,port_reader->port);
	    strcat(send_port_info,"-");
            port_reader = port_reader->next;
        }
    }
    printf("settings.yaml read completed...\n");
    if(islistenmode){
	//waiting for connection...
        printf("Getting connecting code of this device...\n");
	struct device_ret* device_data=get_device_code(mac_device);
        char* device_code=device_data->mac_device;
	char* address_code=device_data->mac_address;
        printf("Connecting code of this device is:%s\n",device_code);
        char* password = remove_char(address_code, c);
	printf("Your password is:%s\n",password);
        Receiver(device_code,password,send_ip_info,send_port_info);
    }else{
	Sender(argv[1],argv[3],send_ip_info,send_port_info);
        //connecting...
    }
    return 0; 
}

academy_server-acade.go:

Go 复制代码
package main
import (
 "fmt"
 "bufio"
 "net"
 "sync"
 "io/ioutil"
 "log"
 "os"
 "strconv"
 "gopkg.in/yaml.v2"
)
type client struct {
        conn     net.Conn
        connid string
        name     string
        messages chan  string
}
type Message struct {
    conn net.Conn
    connid string
    SenderName string
    Content    string
}
var (
        entering = make(chan client)
        leaving  = make(chan client)
        messages = make(chan Message) // 所有接收的消息
)
type Config struct {
 Database struct {
 Host     string `yaml:"host"`
 Port     int    `yaml:"port"`
 Port2    int    `yaml:"port2"`
 } `yaml:"database"`
}
func broadcast() {
    clients := make(map[string]client) // 键是客户端的名称,值是客户端结构体实例

    for {
        select {
        case msg := <-messages:
            // 将消息广播给除了发送者以外的所有客户端
            for _,cli := range clients {
                if cli.name != msg.SenderName && cli.connid==msg.connid {
                   cli.messages<- msg.Content
                }
            }
        case newClient := <-entering:
            clients[newClient.name] = newClient
        case leavingClient := <-leaving:
        delete(clients, leavingClient.name)
           close(leavingClient.messages)
        }
    }
}
func handleConn(conn net.Conn,connid string) {
        input := bufio.NewScanner(conn)
        var wg sync.WaitGroup
        // 创建一个新的客户端
        ch := make(chan string,1)  // 客户端的消息通道
        go clientWriter(conn, ch)
        cli := client{conn: conn, connid:connid,name: conn.RemoteAddr().String(), messages: ch}
        entering <- cli
        fmt.Println(cli.name)
        wg.Add(1)
        go func() {
                defer wg.Done()
                for input.Scan() {
                        messages <-Message{SenderName: cli.name, Content: input.Text(),conn:conn,connid:connid} // cli.name + ": " + input.Text()
                        _, err := conn.Write([]byte("Received your message\n"))
                         if err != nil {
                                fmt.Println("Error writing to connection:", err.Error())
                                break
                         }
                }
        }()
        wg.Wait()
        leaving <- cli
        conn.Close()
}
func clientWriter(conn net.Conn, ch <-chan string) {

        for msg := range ch {
                fmt.Fprintln(conn, msg) // 注意:网络写入应该有错误处理
        }
}

func main() {
	argCount := len(os.Args) - 1
	if argCount == 0 {
		fmt.Println("没有传递任何参数。")
		fmt.Println("acade -h")
		fmt.Println("acade --help")
		os.Exit(1) // 使用非零状态码退出,表示错误  
	}

	// 如果参数数量不符合预期(例如,你需要恰好2个参数),提前返回  
	if argCount != 1 {
		fmt.Printf("需要1个参数,但传递了%d个参数。\n", argCount)
		os.Exit(1) // 使用非零状态码退出,表示错误  
	}

	yamlfilename:=os.Args[1]
	if yamlfilename=="-h" || yamlfilename=="--help"{
            fmt.Println("acade [path to settings.yaml]")
	    os.Exit(0)
	}
         // 读取YAML文件内容  
         yamlFile, err := ioutil.ReadFile(yamlfilename)
        if err != nil {
            log.Fatalf("无法读取YAML文件: %v", err)
         }
         // 解析YAML内容  
         var config Config
         err = yaml.Unmarshal(yamlFile, &config)
         if err != nil {
             log.Fatalf("无法解析YAML内容: %v", err)
         }
         // 输出解析后的内容  
         fmt.Printf("Host: %s\n", config.Database.Host)
         fmt.Printf("Port: %d\n", config.Database.Port)
	 fmt.Printf("Port2: %d\n", config.Database.Port2)
	 Host:=config.Database.Host
	 Port:=strconv.Itoa(config.Database.Port)
	 Port2:=strconv.Itoa(config.Database.Port2)
	 listener, err := net.Listen("tcp",Host+":"+Port)
	 if err != nil {
                fmt.Println(err)
                return
        }
	listener2, err2 := net.Listen("tcp",Host+":"+Port2)
        if err2 != nil {
                fmt.Println(err2)
                return
        }
        go broadcast()
        go func(){
        for {
                conn, err := listener.Accept()
                if err != nil {
                        fmt.Println(err)
                        continue
                }
                go handleConn(conn,"1")
            }
        }()
        go func(){
            for{
                conn2, err2 := listener2.Accept()
                if err2 != nil {
                        fmt.Println(err2)
                        continue
                }
                go handleConn(conn2,"2")
            }
        }()
        for{}
	// 程序正常执行完毕,使用零状态码退出  
	os.Exit(0)
}
相关推荐
珹洺3 分钟前
从 HTML 到 CSS:开启网页样式之旅(二)—— 深入探索 CSS 选择器的奥秘
前端·javascript·css·网络·html
Tony聊跨境5 分钟前
TCP vs UDP:如何选择适合的网络传输协议?
tcp/ip·ip
okmacong11 分钟前
4、MAC地址、ARP协议解析
服务器·网络·macos
C15751X1 小时前
联通光猫(烽火通信设备)改桥接教程
网络
yaoxin5211231 小时前
第二十九章 TCP 客户端 服务器通信 - 记录的拼接
服务器·网络·tcp/ip
网络安全Ash2 小时前
运维之网络安全抓包—— WireShark 和 tcpdump
网络·网络协议·安全·网络安全
向阳12182 小时前
http 流量接入 Dubbo 后端服务
网络协议·http·dubbo
萤火夜3 小时前
Linux之网络基础
网络
xxtzaaa4 小时前
住宅IP怎么在指纹浏览器设置运营矩阵账号
网络·网络协议·tcp/ip
okmacong4 小时前
3、集线器、交换机、路由器、ip的关系。
网络·tcp/ip·智能路由器