庞大资源库的计算机教程网站!
设为首页
加入收藏
总编信箱
投稿或申请专栏请先 [登 陆]
首页 操作系统 程序设计 图形图像 媒体动画 机械电子 WEB开发 数 据 库 办公系列 路由技术 网络原理 网络应用
认证考试 安全技术
首页>操作系统>Linux>硬件问题>正文
资料搜索
Google搜索
Google
返回上级列表

推荐文章

快速保存网页中所有图片的方法
Windows中让光驱巧妙“隐身”技
防范非法用户入侵Win 2000/XP系
两款比较典型的ASP木马防范方法
有关表格边框的css语法整理
Windows XP中可以被禁用的服务
SQL Server导出导入数据方法
Javascript所有对象的属性的获
网页(HTML)中的特殊字符
与篮球共舞,尽显模式本色
QQ病毒的手工清除方法
Photoshop为极品美女打造性感睫
天衣无缝:IIS与PHP水火也相容
SQL Server存储过程编写和优化

一个简易的UDP Proxy程序

 作者:本站收集   日期:2005-4-15
字号选择〖 〗/ 双击滚屏 单击停止   
一个简易的UDP Proxy程序
作为<<一个简易的proxy程序的开发过程>>的补充

1、为什么开发这个UDP程序
网络状况如上文<<一个简易的proxy程序的开发过程>>。我们的socks代理是有权限的(相信很多公司
都有这种情况存在)。写这个程序的时候,我还没有socks代理的权限,所以不能上OICQ。这让我感到
很不方便。所以,我决定写一个UDP的代理程序来实现我上OICQ的愿望。原理同上文是一样的。只是在
具体实现上略有所不同。
先看看源代码,稍后再来解释。(由于这个版本是一个完全功能版,所以提供了很多sp.c没有提供的功
能。)上文中提到过的部分,这里就不再次一一解释了。
-----------------------------------------------------------------------------------------
/***************************************************
Program: pu.c
Description: a smart UDP proxy
Author: Alan Chen ( ariesram@may10.ca )
Date: Dec 1, 2000
***************************************************/

#include
#include
#include
#include
#include
#include
#include
#include

#define MAX_ID_LEN 12
#define LOGFILE "/usr/tmp/.pu.log"
#define ERRLOG "/usr/tmp/.pu.err"

void sig_int(int sig);
void do_receive(int fd_tran);
void p_error(const char * err_msg);
void p_log(const char * buffer, int len);

int
main(int argc, char ** argv)
{
int fd_listen, fd_tran;
struct sockaddr_in sin, out;
struct sockaddr_in r_in, r_out;
struct sockaddr_in stmp;
int port = 1250;
int pid;
char * ip;
int i;

fd_set fdset;
char buffer[2048*2];
int data_len, alen;
struct timeval val;

#ifndef _DEBUG
signal(SIGINT, SIG_IGN);
#endif
signal(SIGHUP, SIG_IGN);
/* signal(SIGTERM, SIG_IGN); */
signal(SIGABRT, SIG_IGN);
signal(SIGSTOP, SIG_IGN);
signal(SIGCHLD, SIG_IGN);

#ifndef _DEBUG
if (fork() != 0)
exit(0);

setsid();

for (i = 256; i >= 0; i --)
#endif

#ifdef _DEBUG
for (i = 256; i >= 3; i --)
#endif
close(i);
chdir("/usr/tmp");

bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(8000);
sin.sin_addr.s_addr = INADDR_ANY;

bzero(&out, sizeof(out));
out.sin_family = AF_INET;
/* out.sin_port = htons(5000); */
out.sin_port = htons(4000);
out.sin_addr.s_addr = INADDR_ANY;

/* remote server */
bzero(&r_out, sizeof(r_out));
r_out.sin_family = AF_INET;
r_out.sin_port = htons(8000);
r_out.sin_addr.s_addr = inet_addr("202.96.170.164");

/* remote client */
bzero(&r_in, sizeof(r_in));
r_in.sin_family = AF_INET;
r_in.sin_port = htons(4000);
r_in.sin_addr.s_addr = inet_addr("192.168.103.97");

fd_listen = socket(PF_INET, SOCK_DGRAM, 0);
if (fd_listen < 0) {
p_error("socket1 error");
exit(1);
}

fd_tran = socket(PF_INET, SOCK_DGRAM, 0);
if (fd_tran < 0) {
p_error("socket2 error");
exit(1);
}

if (bind(fd_listen, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
p_error("bind error1: ");
exit(1);
}

if (bind(fd_tran, (struct sockaddr *)&out, sizeof(out)) < 0) {
p_error("bind error2: ");
exit(1);
}

fcntl(fd_listen, F_SETFL, O_NONBLOCK);
fcntl(fd_tran, F_SETFL, O_NONBLOCK);

while (1) {
FD_ZERO(&fdset);
FD_SET(fd_tran, &fdset);
FD_SET(fd_listen, &fdset);
val.tv_sec = 1;
val.tv_usec = 0;

if (select(fd_tran + 1, &fdset, NULL, NULL, &val) < 0) {
p_error("select error: ");
continue;
}
if (FD_ISSET(fd_listen, &fdset)) {
alen = sizeof(r_in);
data_len = recvfrom (fd_listen, buffer, sizeof(buffer), 0,
(struct sockaddr *)&r_in, &alen);
if (data_len <= 0) {
p_error("socket closed by remote client");
close(fd_listen);
close(fd_tran);
exit(0);
}
#ifdef _DEBUG
ip = inet_ntoa(r_in.sin_addr.s_addr);
printf("received from %s , socket id: %d
", ip, fd_listen);
p_log("

received from inner:
", 23);
/* sizeof("

received from inner:
"); */
p_log(buffer, data_len);
#endif

if (sendto(fd_tran, (char *)buffer, data_len, 0,
(struct sockaddr *)&r_out, sizeof(r_out)) <=0 ) {
p_error("cann't send to remote server");
close(fd_tran);
close(fd_listen);
exit(0);
}
#ifdef _DEBUG
ip = inet_ntoa(r_out.sin_addr.s_addr);
printf("send to %s
", ip);
#endif

}
else if (FD_ISSET(fd_tran, &fdset)) {
alen = sizeof(stmp);
data_len = recvfrom (fd_tran, buffer, sizeof(buffer), 0,
(struct sockaddr *)&stmp, &alen);
if (data_len <= 0) {
p_error("socket closed by remote server");
close(fd_listen);
close(fd_tran);
exit(0);
}
#ifdef _DEBUG
ip = inet_ntoa(stmp.sin_addr.s_addr);
printf("received from %s , socket id: %d
", ip, fd_tran);
p_log("

received from outer:
", 23);
/* sizeof("

received from outer:
"); */
p_log(buffer, data_len);
#endif

if (sendto(fd_listen, (char *)buffer, data_len, 0,
(struct sockaddr *)&r_in,
sizeof(r_in)) <=0 ) {
p_error("cann't send to remote server");
close(fd_tran);
close(fd_listen);
exit(0);
}
#ifdef _DEBUG
ip = inet_ntoa(r_in.sin_addr.s_addr);
printf("send to %s
", ip);
#endif

}
}
return 0;

}

void
sig_int(int sig)
{
signal(SIGINT, sig_int);
exit(1);
}

void
p_error(const char * err_msg)
{
FILE * fp;

#ifdef _DEBUG
printf("%s
", err_msg);
#endif

fp = fopen(ERRLOG, "a");
if (fp == NULL)
return;
fprintf(fp, "%s
", err_msg);
fclose(fp);
}

void
p_log(const char * buffer, int len)
{
FILE * fp;

fp = fopen(LOGFILE, "ab");
if (fp == NULL)
return;
fwrite(buffer, len, 1, fp);
fclose(fp);

}

-----------------------------------------------------------------------------------------
编译,运行是同上文所述相同的。
但是,要注意UDP是没有连接的,所以程序的具体实现方式有所不同。
来看程序。
#ifndef _DEBUG
signal(SIGINT, SIG_IGN);
#endif
signal(SIGHUP, SIG_IGN);
/* signal(SIGTERM, SIG_IGN); */
signal(SIGABRT, SIG_IGN);
signal(SIGSTOP, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
忽略上述信号,使程序以后台方式运行。
#ifndef _DEBUG
if (fork() != 0)
exit(0);

setsid();

for (i = 256; i >= 0; i --)
#endif
如果不是DEBUG方式,程序以前台方式运行。
#ifdef _DEBUG
for (i = 256; i >= 3; i --)
#endif
close(i);
关闭描述字。
bzero(&r_in, sizeof(r_in));
r_in.sin_family = AF_INET;
r_in.sin_port = htons(4000);
r_in.sin_addr.s_addr = inet_addr("192.168.103.97");
192.168.103.97 是我机器的局域网IP地址。由于这个程序只是给我一个人使用的,所以没有在这上面处理
得更灵活一些。:-)
fd_listen = socket(PF_INET, SOCK_DGRAM, 0);
if (fd_listen < 0) {
p_error("socket1 error");
exit(1);
}
UDP的socket描述字是这样子建立的。
fcntl(fd_listen, F_SETFL, O_NONBLOCK);
fcntl(fd_tran, F_SETFL, O_NONBLOCK);
将这两个监听端口的socket方式设置为非阻塞的。

if (FD_ISSET(fd_listen, &fdset)) {
alen = sizeof(r_in);
data_len = recvfrom (fd_listen, buffer, sizeof(buffer), 0,
(struct sockaddr *)&r_in, &alen);
if (data_len <= 0) {
p_error("socket closed by remote client");
close(fd_listen);
close(fd_tran);
exit(0);
}
#ifdef _DEBUG
ip = inet_ntoa(r_in.sin_addr.s_addr);
printf("received from %s , socket id: %d
", ip, fd_listen);
p_log("

received from inner:
", 23);
/* sizeof("

received from inner:
"); */
p_log(buffer, data_len);
#endif

if (sendto(fd_tran, (char *)buffer, data_len, 0,
(struct sockaddr *)&r_out, sizeof(r_out)) <=0 ) {
p_error("cann't send to remote server");
close(fd_tran);
close(fd_listen);
exit(0);
}
#ifdef _DEBUG
ip = inet_ntoa(r_out.sin_addr.s_addr);
printf("send to %s
", ip);
#endif

}
对于UDP来说,用的是recvfrom和sendto. 在程序中同时加入了recvfrom时的客户端地址,便于在调试
时候来检查。同时,这里面也有一些调试的信息,比如,输出接收到的字符串,客户端地址等等。
else if (FD_ISSET(fd_tran, &fdset)) {
alen = sizeof(stmp);
data_len = recvfrom (fd_tran, buffer, sizeof(buffer), 0,
(struct sockaddr *)&stmp, &alen);
if (data_len <= 0) {
p_error("socket closed by remote server");
close(fd_listen);
close(fd_tran);
exit(0);
}
#ifdef _DEBUG
ip = inet_ntoa(stmp.sin_addr.s_addr);
printf("received from %s , socket id: %d
", ip, fd_tran);
p_log("

received from outer:
", 23);
/* sizeof("

received from outer:
"); */
p_log(buffer, data_len);
#endif

if (sendto(fd_listen, (char *)buffer, data_len, 0,
(struct sockaddr *)&r_in,
sizeof(r_in)) <=0 ) {
p_error("cann't send to remote server");
close(fd_tran);
close(fd_listen);
exit(0);
}
#ifdef _DEBUG
ip = inet_ntoa(r_in.sin_addr.s_addr);
printf("send to %s
", ip);
#endif

}
这一段是一样的。所不同的是,从fd_tran中读出来写回到fd_listen中去。

两个日志函数的说明。
void
p_error(const char * err_msg)
{
FILE * fp;

#ifdef _DEBUG
printf("%s
", err_msg);
#endif

fp = fopen(ERRLOG, "a");
if (fp == NULL)
return;
fprintf(fp, "%s
", err_msg);
fclose(fp);
}
调试的时候,将错误信息输出到标准输出上。
调试状态的时候,将错误信息输出到/usr/tmp/下的一个隐藏文件中。
void
p_log(const char * buffer, int len)
{
FILE * fp;

fp = fopen(LOGFILE, "ab");
if (fp == NULL)
return;
fwrite(buffer, len, 1, fp);
fclose(fp);

}
不论调试还是非调试状态,都把日志写到日志文件中。
由于我所使用的代理服务器不是我所管辖,所以我把日志文件写到了/usr/tmp下,这是一个相对来说比较隐蔽
的位置而且我也有权限。呵呵。:-). 正常使用时,可以把日志写到你的特定目录下。这只要修改
#define LOGFILE "/usr/tmp/.pu.log"
#define ERRLOG "/usr/tmp/.pu.err"
就行了。

进一步的改进:
为了让这个程序更加有通用性,可以修改一下,让他在命令行中能够设置服务端口号,远程服务器的
IP地址和端口号,能够设置日志文件的路径。
有什么问题,建议,可以通过email和我联系。
这个源代码是基于GNU的,如果你对这个源代码有所改动,也可以email给我。:-)
上一篇:Linux环境中网卡设备的驱动    下一篇:注解高端UNIX服务器之技术  
[发送给好友]  [关闭窗口]  [返回顶部]   转载请注明来源:www.it00.com   
特别声明: 本站除部分特别声明禁止转载的专稿外的其他文章可以自由转载,但请务必注明出处和原始作者。文章版权归文章原始作者所有。对于被本站转载文章的个人和网站,我们表示深深的谢意。如果本站转载的文章有版权问题请联系编辑人员,我们尽快予以更正。
责任编辑: 原点 投稿作者: 本站收集
信息来源: 网络 录入时间: 2005-4-15
关于我们 - 广告服务 - 版权申明 - 网站地图 - 联系方式 - 总编信箱 - 会员投稿