被动式Telnet研究及实现(解决内外网远程维护的困难)-part A

欢迎转载,转载请保留原文链接:http://blog.csdn.net/mikulee/article/details/40149779

项目背景及需求:

最近公司有一个项目:

我们在一个arm主机上实现了一些客户需求,有一个web端和一个后台服务,这个主机放在客户的家里,连接上互联网。

arm主机的后台服务负责与我们公司的linux服务器进行通信,例如发送接收web请求等等。

现实总是残酷的,arm主机上总会出现各种问题,我们需要通过telnet对arm主机进行维护。

这通常有2个方案:

方案1.直接去客户家里用telnet进去查看。

方案2.通过端口映射,把arm主机的端口映射到公网上。

但上面2个方案都是有很大的缺点:

方案1:路途遥远,我们总不能出一点问题就跑到客户家,打扰到客户吧。

方案2:端口映射,只能针对简单网络实现。通常网络环境都十分复杂,很多都是经过好几级路由才到达arm主机,这时映射困难重重。

而且,如果我们需要维护的不仅仅是几个arm主机,而是很多很多,那端口映射就太麻烦了,简直是吃力不讨好。

通常的解决方案是:

在arm主机的服务里,定时检查服务器的状态,如果服务器有命令发过来,则在客户端执行该命令。

这个方案也是麻烦多多,要分别在客户端及服务端就行命令编程,统一协议命令,所支持的命令数量和定义的一致。这个模式是十分繁琐而且可维护性也十分有限。

最终解决方案:

就是在arm主机里,定时查询web服务器是否有维护请求,如果有维护请求,则服务器会把该服务器的ip和监听的端口返回给客户端,从而arm主机用获得ip和端口,先连接本机telnet端口23,然后主动发起一个连接到服务器,如果服务器端用一个经过改造的telnet客户端监听该端口,就能建立起一个连接,从而telnet连接建立成功。接着的事情,就是你想怎样就怎样了,哈哈,太坏了。

下面开始代码部分:

首先要实现的是arm主机端的端口转发程序:

代码中用到的线程池,请参考如下文章:传送门

<span style="font-size:14px;">/*
 ============================================================================
 Name        : TelnetService.c
 Author      : xr.lee
 Version     :
 Description :  Ansi-style
 ============================================================================
 */

#include	<sys/types.h>
#include	<sys/socket.h>
#include	<sys/time.h>
#include	<sys/file.h>
#include	<netinet/in.h>
#include	<arpa/inet.h>
#include <net/if.h>

#include	<string.h>

#include	<stdio.h>
#include	<stdlib.h>
#include	<netdb.h>
#include	<fcntl.h>
#include	<time.h>
#include	<ctype.h>
#include	<unistd.h>
#include	<signal.h>
#include	<errno.h>
#include	<sys/wait.h>
#include	<sys/ipc.h>
#include	<sys/shm.h>
#include <sys/ioctl.h>

#include	<malloc.h>
#include	<getopt.h>
#include	<termios.h>	// local echo off/on;

#include "thread_pool.h"

typedef struct NetConf {
	char ip[20];
	int port;
} NetConf;

int getLocalIp(char *in_name, char *buf) {
	int socket_fd;
	struct ifreq ifr;

	if ((socket_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
		return -1;
	}

	strcpy(ifr.ifr_name, in_name);
	if (ioctl(socket_fd, SIOCGIFADDR, &ifr) < 0) {
		return -1;
	}
	strcpy(buf, inet_ntoa(((struct sockaddr_in *) &(ifr.ifr_addr))->sin_addr));
	return 0;
}

//--------------------------------------------------------------------------------
// Set echo mode OFF/ON like stty proc;
//--------------------------------------------------------------------------------
static struct termios stored;
static int n_term_change = 0;

void echo_off(void) {
	struct termios new;
	tcgetattr(0, &stored);
	memcpy(&new, &stored, sizeof(struct termios));
	new.c_lflag &= (~ECHO);			// echo off ;
	new.c_lflag &= (~ICANON);		// set buffer to 1,
	new.c_cc[VTIME] = 0;				// no time-out ;
	new.c_cc[VMIN] = 1;
	tcsetattr(0, TCSANOW, &new);
	n_term_change = 1;
	return;
}

void echo_on(void) {
	if (n_term_change)
		tcsetattr(0, TCSANOW, &stored);		// restore terminal seeting ;
	n_term_change = 0;
	return;
}

int transfer(int fromfd, int tofd) {
	int readSize = -1;
	char buf[1024];
	while ((readSize = read(fromfd, buf, sizeof buf)) > 0) {
		if (write(tofd, buf, readSize) < 0) {
			return -1;
		}
	}
	printf("errno:%d\n", errno);
	if (readSize < 0 && errno != EAGAIN) {
		return -1;
	}

	if (readSize < 0 && errno == EAGAIN) {
		return 1;
	}

	return readSize;
}

int createSocketToServer(const char *dstIp, int dstPort) {
	struct sockaddr_in client_addr;
	bzero(&client_addr, sizeof(client_addr)); //把一段内存区的内容全部设置为0
	client_addr.sin_family = AF_INET;    //internet协议族
	client_addr.sin_addr.s_addr = htons(INADDR_ANY);    //INADDR_ANY表示自动获取本机地址
	client_addr.sin_port = htons(0); //0表示让系统自动分配一个空闲端口 //创建用于internet的流协议(TCP)socket,用client_socket代表客户机
	int client_socket = socket(AF_INET, SOCK_STREAM, 0);
	if (client_socket < 0) {
		printf("Create Socket Failed!\n");
		return -1;
	} //把客户机的socket和客户机的socket地址结构联系起来
	int fdflags = fcntl(client_socket, F_GETFL, 0);
	if (fcntl(client_socket, F_SETFL, fdflags | O_NONBLOCK) < 0) {
		printf("set O_NONBLOCK Error!\n");
		close(client_socket);
		return -1;
	}

	if (bind(client_socket, (struct sockaddr*) &client_addr,
			sizeof(client_addr))) {
		printf("Client Bind Port Failed!\n");
		close(client_socket);
		return -1;
	}  //设置一个socket地址结构server_addr,代表服务器的internet地址, 端口
	struct sockaddr_in server_addr;
	bzero(&server_addr, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	if (inet_aton(dstIp, &server_addr.sin_addr) == 0) //服务器的IP地址来自程序的参数
			{
		printf("Server IP Address Error!\n");
		close(client_socket);
		return -1;
	}
	server_addr.sin_port = htons(dstPort);
	printf("Connecting To %s,%d!\n", dstIp, dstPort);
	socklen_t server_addr_length = sizeof(server_addr); //向服务器发起连接,连接成功后client_socket代表了客户机和服务器的一个socket连接
	int ret = 0;
	if ((ret = connect(client_socket, (struct sockaddr*) &server_addr,
			server_addr_length)) < 0) {
		if (errno != EINPROGRESS) {
			printf("Can Not Connect To %s,%d!\n", dstIp, dstPort);
			return -1;
		}
	}
	if (ret == 0)
		goto done;

	struct timeval tval;
	tval.tv_sec = 10;
	tval.tv_usec = 0;

	fd_set rset;
	FD_ZERO(&rset);
	FD_SET(client_socket, &rset);
	if ((ret = select(client_socket + 1, NULL, &rset, NULL, &tval)) < 0) {
		printf("Connect To %s,%d EINTR!\n", dstIp, dstPort);
		close(client_socket);
		return -1;
	}
	if (ret == 0) {
		printf("Connect To %s,%d timeout!\n", dstIp, dstPort);
		close(client_socket);
		return -1;
	}
	int error = 0;
	int len = sizeof(error);
	getsockopt(client_socket, SOL_SOCKET, SO_ERROR, (void *) &error, &len);
	if (error) {
		fprintf(stderr, "Error in connection() %d - %s/n", error,
				strerror(error));
		return -1;
	}

	done: return client_socket;
}

void closeSocket(int socketFd) {
	if (socketFd != -1) {
		close(socketFd);
	}
}

void *spawnNewConnect(void *arg) {
	NetConf *conf = (NetConf *) arg;
	char localip[20];
	memset(localip, 0, 20);
	getLocalIp("eth0", localip);
	if (strlen(localip) > 0 && strlen(conf->ip) > 0) {
		int socket_server = createSocketToServer("192.168.1.101", 23);
		int socket_client = createSocketToServer(conf->ip, conf->port);
		if (socket_server != -1 && socket_client != -1) {
			int n_select = 0;
			fd_set n_read_fds;
			int ret = -1;
			while (1) {
				FD_ZERO(&n_read_fds);
				FD_SET(socket_server, &n_read_fds);
				FD_SET(socket_client, &n_read_fds);
				n_select =
						socket_server > socket_client ?
								socket_server : socket_client;
				ret = select(n_select + 1, &n_read_fds, NULL, NULL, NULL);
				perror("SELECT END\n");
				if (ret < 0) {
					perror("Select() error. \n");
					break;
				}
				if (ret == 0) {
					perror("Select() time out. \n");
					break;
				}
				if (FD_ISSET(socket_server, &n_read_fds)) {
					perror("FD_ISSET(socket_server, &n_read_fds)");
					ret = transfer(socket_server, socket_client);
					if (ret <= 0) {
						printf(
								"transfer(socket_server,socket_client)%s0 err.\n",
								ret < 0 ? "<" : "==");
						break;
					}

				}
				if (FD_ISSET(socket_client, &n_read_fds)) {
					perror("FD_ISSET(socket_client, &n_read_fds)");
					ret = transfer(socket_client, socket_server);
					if (ret <= 0) {
						printf(
								"transfer(socket_client,socket_server)%s0 err.\n",
								ret < 0 ? "<" : "==");
						break;
					}
				}

			}

		}
		closeSocket(socket_server);
		closeSocket(socket_client);
		socket_server = -1;
		socket_client = -1;
	}
	free(conf);
}

int main(void) {
	echo_off();

	if (tpool_create(3) != 0) {
		printf("tpool_create failed\n");
		exit(1);
	}

	int sockfd;
	char ack;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	struct GuardDev *dev = NULL;
	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
		fprintf(stderr, "Socket Error\n");
		exit(1);
	}

	bzero(&server_addr, sizeof(struct sockaddr_in));
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(10109);

	if (bind(sockfd, (struct sockaddr *) (&server_addr),
			sizeof(struct sockaddr)) == -1) {
		fprintf(stderr, "Bind error\n");
		exit(1);
	}

	if (listen(sockfd, 1) == -1) {
		fprintf(stderr, "listen error\n");
		exit(1);
	}
	char receive[100];
	while (1) {
		int sin_size = sizeof(struct sockaddr_in);
		sleep(1);
		int client_fd = -1;
		if ((client_fd = accept(sockfd, (struct sockaddr *) (&client_addr),
				&sin_size)) == -1) {
			fprintf(stderr, "Accrpt error\n");
			exit(1);
		}
		printf("Server get connection from %s,clientfd=%d\n",
				(unsigned char *) inet_ntoa(client_addr.sin_addr), client_fd);
		memset(receive, 0, 100);
		int ret = recv(client_fd, receive, 64, 0);
		if (ret < 0) {
			printf("receive error: %s", strerror(errno));
			close(client_fd);
			continue;
		}
		if (strlen(receive) > 0) {
			char ip[20], port[6];
			memset(ip, 0, 20);
			memset(port, 0, 6);
			sscanf(receive, "ip=%[^&]&port=%[^&]", ip, port);
			NetConf *conf = (NetConf *) malloc(sizeof(NetConf));
			memset(conf, 0, sizeof(NetConf));
			strcpy(conf->ip, ip);
			conf->port = atoi(port);
			tpool_add_work(spawnNewConnect, (void*) conf);
		}
		close(client_fd);
	}

	tpool_destroy();
	echo_on();

	return EXIT_SUCCESS;
}</span>

编译后,做为开机启动项,添加到arm主机上。

下一篇文章将提供服务端实现及使用方法,源码也一起附上

http://blog.csdn.net/mikulee/article/details/40150791

时间: 2024-10-16 21:51:15

被动式Telnet研究及实现(解决内外网远程维护的困难)-part A的相关文章

解决ArcGIS API for Silverlight 加载地图的内外网访问问题

原文:解决ArcGIS API for Silverlight 加载地图的内外网访问问题 先上一个类,如下: public class BaseClass { public static string getFullUri(string oldUriString) { string newUriString = oldUriString; //处理相对地址============================================================ if (newUri

笔记本电脑同时连接公司内外网网络的解决办法

公司做了内外网隔离之后,同一台电脑上网的时候可能需要来回切换,给工作带来不小的麻烦.通过修改windows系统默认路由设置,即可让笔记本实现一台电脑同时连接内网和外网. 以我遇到的情况为例:内网需要配置10.段IP,外网需要使用无线获取IP(192.段) 前提条件: 笔记本两个网卡,一般为无线网卡和有线网卡,你的电脑如果不是太旧,这个条件基本满足: 需要文件: 新建文本文件,保存为后缀名为.bat的批处理文件,例如"BridgingNet.bat",代码如下: @echo off ec

双网卡同时访问内外网

为了方便自己在一台机器上同时访问内网和外网,自己对系统路由作了一点点更改,既方便了自己,又学习到了相关的路由知识. 网络环境(我办公室真实的网络环境): 1.一台主机: 2.两张网卡: 3.外网通过SOHO路由器上ADSL,网关为192.168.0.1,本机外网IP为192.168.0.101: 4.内网全网网络地址为10.0.0.0 掩码为255.0.0.0 ,我所在区域的子网络为10.*.*.0,网关为:10.*.*.*,本机IP为10.*.*.* . 要解决的问题: 两张网卡要同时开启,并

CentOS内外网互调

大家好: VMware里CentOS7.0克隆时出现网卡错乱的解决办法,我这里给大家整理了一下,顺便附上几张截图,供大家参考:后面介绍CentOS7之前版本的解决办法: 一.CentOS 7 安装一台后,后续经克隆产生的虚拟机会出现网卡错乱的问题. 此问题经查是因为 CentOS 7 的网络配置是由 NetworkManager 及 systemd 联合造成的. ①安装完一台CentOS7,正常配置完ip之后,先运行 nmtui 选择编辑连接,然后在第二个界面内将所有连接删除,然后退出. 运行n

解决内网、外网同时上的问题

状况:平时大家经常遇到上内网的同时也需要上外网,这个时候我们经常会用两个网卡分别上内网,或一个网卡同时上内.外网. 问题:但是会遇到一种麻烦的情况就是内网和外网不能同时上(例如:腾讯通和QQ不能同时在线).怎么办呢? come on,让我们来着手解决问题吧! 在MS-DOS下我们输入route 这个命令会有相应的提示.输入route print 则是打印路由表.输入route -f 则是清空路由. 下面我们来看下路由表中都写了些什么吧. =============================

配置静态NAT实现内外网通信

今天初次接触到NAT,了解不是特别全面,在学习的同时我也查阅了一些对应资料,接下来我就为大家简单说一下我对NAT的理解及应用,有什么欠缺请大家及时指正. NAT英文全称是"Network Address Translation",中文意思是"网络地址转换", 它是一种把内部私有网络地址解释为合法网络IP地址的技术,所以在一定程度可以解决ip地址不够用的问题.同时NAT分为三种类型:静态NAT.动态NAT.网络地址端口转换NAPT,今天我主要通过一个小实验为大家讲解一

企业如何实现内外网文件安全交换?

随着企业数字化转型逐步深入,企业的业务开展,越来越依赖于不断增加的办公.生产.研发等IT系统,也越来越频繁地需要与外部进行持续大量的数据交换. 在企业的日常业务中,存在大量需要在不同网络之间进行文件传递的场景: 处在内网的员工,需要将设计图纸.项目资料等文件发送给外网的用户. 客户.供应商.合作伙伴,需要通过互联网将文件发送给内网员工. 科技型企业,需要持续将从外部获取的大量数据导入到研发网,进行机器学习.数据挖掘等数据处理工作,比如传感器采集的数据.测试数据.网络服务收集的数据等. 网络的物理

内外网共存环境迁移Exchange 2010至Exchange 2016

此博客重点: 安全迁移邮箱: 迁移后安全卸载exchange 2010: 关于此案例的共存环境如何搭建和用到的所有工具需要的可以访问上一篇博客:基于exchange 2010迁移exchange 2016搭建共存环境 一.案例分析 1.案例概述 本案例内网采用Windows活动目录管理网络资源,所有Windows服务器的版本均为Windows server 2008 R2,其中一台服务器运行着exchange server 2010邮件系统,负责企业的邮件通信.随着业务的不断拓展,企业规模越来越

SharePoint 2013:解决爬网出错的问题

现象: 以前一直正常的爬网突然无法顺利完成,总是在进行到某个部分就停滞不前. 调查: 在查看了log文件后,发现了这条错误 06/24/2014 11:14:51.86   NodeRunnerQuery1-734f5ee7-2cc2- (0x0DD4)  0x14A0    Search                            Common Processing                28    Information   Component and System=Que