作为我的常用工具,对它的实现很感兴趣,而且(主要是)代码较少而且容易理解。简单解说,写在这里:

基于 1.4g。

初次执行 ssh 加入 -o StrictHostKeyChecking=no 可以防止阻塞,并使用公钥认证

程序会建立一个端口 P1 监听数据,然后使用 ssh tunnel 代理该端口到远程主机。再代理远程端口到本地 P2,从本地连接到 P2,写数据到 P2 之后程序可以从 P1 读到这份数据。

如果数据收发正常,说明隧道正常。如果有问题就重启 ssh 子进程。

Fork 进程

主要的逻辑位于 ssh_run 函数中:在 while 循环中,使用 fork 函数生成一个子进程。

cchild = fork();
// fork 函数的返回,子进程从此处执行,子进程返回 0,父进程返回子进程的 pid,错误返回 -1,errorno 说明错误原因
switch (cchild) {
case 0:
	errlog(LOG_DEBUG, "child of %d execing %s",
		getppid(), av[0]);
    // evecvp 执行的是 ssh 命令,此处阻塞执行
	execvp(av[0], av);
	errlog(LOG_ERR, "%s: %s", av[0], strerror(errno));
		/* else can loop restarting! */
    // 使用 SIGTERM kill 父进程
	kill(getppid(), SIGTERM);
	_exit(1);
	break;
case -1:
    // 出错的情况下
	cchild = 0;
	xerrlog(LOG_ERR, "fork: %s", strerror(errno));
	break;
default:
    // 父进程继续执行
	errlog(LOG_INFO, "ssh child pid is %d", (int)cchild);
	set_sig_handlers();
	retval = ssh_watch(sock);
	dolongjmp = 0;
	clear_alarm_timer();
	unset_sig_handlers();
	if (retval == P_EXITOK || retval == P_EXITERR)
		return retval;
	break;
}

监控子进程

在 conn_test 函数中实现了监听端口了传输数据的流程,用于 ssh 进程的隧道是否联通。

conn_test
|-- conn_remote(char *host, char *port)
|-- conn_poll_for_accept(int sock, struct pollfd *pfd)
|-- conn_send_and_receive(char *rp, char *wp, size_t len, struct pollfd *pfd, int ntopoll)

程序启动时已经调用了 conn_listen 监听端口。conn_test 调用的三个程序分别负责连接,accept 以及发送接收数据。

循环的时候,通过 SIGALRM 信号触发的连接测试,并定时准备下一次测试。



  • No labels