Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103131674
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类: C/C++

2008-04-16 22:45:35

作者:edwardcj   
/* mytcpwrap.c 
  功能:类似著名的 tcpwrap 
        根据配置文件,允许某些地址的用户进行登陆连接 
        除可记录登陆时间外,还可以记录退出时间 
        本程序应由超级服务器inetd启动 
  使用:替换/etc/inetd.conf 中要进行控制的端口 
        以telnet、ftp端口控制为例: 
        /etc/inetd.conf 原内容: 

        ftp    stream  tcp nowait  root   /etc/ftpd      ftpd 
        telnet stream  tcp nowait  NOLUID /etc/telnetd   telnetd 

        改为: 

        ftp    stream  tcp nowait  root   /etc/mytcpwrap /etc/ftpd 
        telnet stream  tcp nowait  NOLUID /etc/mytcpwrap /etc/telnetd 

  注意:改完后记得通知一下inetd进程 
*/ 
#include "stdio.h" 
#include "errno.h" 
#include "time.h" 
#include "signal.h" 

#include  
#include  
#include  
#include  
#include "sys/stat.h" 
#include "sys/times.h" 

#define INET_ADDRESTRLEN 18 
#define MAXSOCKADDR      64 

/* 地址配置文件 
  格式 port ips 
       '#' 打头表示本行为注释 
       'S' 打头表示网络日志记录地,格式为 S port ips 
           缺省为 5678 1.1.1.1 
  接受通配符 * 
  其中 port 记录本地端口,可以使用通配符 * 表示任意端口 
       ips  允许访问的远程IP地址,形如 nnn.nnn.nnn.nnn 
            可以使用通配符 * 表示任意地址 
  例:  21 1.1.1.1 表示1.1.1.1的机器可以访问本机21(ftp)端口 
       *  2.2.2.2 表示2.2.2.2的机器可以访问本机任意端口 
       23 3.3.3.* 表示3.3.3.*网段的机器可以访问本机23(telnet)端口 
*/ 
#define INIFILE      "/etc/mytcpwrap.conf" 
#define LOGFILE      "/usr/adm/mytcpwrap.log" 

short R_PORT=5678; 
char  R_ADDR[16]="1.1.1.1"

/* 简单字符串比较函数 */ 
int  cj_Compare(char *so, char *sd) 

  char *to,*td; 
  int  done,cjOk=0,i,k; 

  if (*sd==0) return 0; 
  for (done=0,to=so,td=sd;!done; ) { 
     if (*td=='?') { 
        td++; to++; 
     } 
     else if (*td=='*') { 
        if (cj_Compare(to,(td+1))==0) return 0; 
        else to++; 
     } 
     else { 
        cjOk = done = *to - *td; 
        td++; to++; 
     } 
     if (*to==0) for ( (*td=='?')||(*td=='*');td++); 
     if ((*to==0)&&(*td==0)) break; 
     else if (*to==0) done = cjOk = -1; 
     else if (*td==0) done = cjOk = -1; 
  } 
  return cjOk; 


int  is_denied(char *s, int lport) 

  int  k, ct; 
  char buf[128], ips[32], pts[8]; 
  FILE *fp; 

  /* 读配置文件 */ 
  fp = fopen(INIFILE, "rt"); 
  if (fp){ 
     memset(buf, 0, sizeof(buf)); 
     fgets(buf, sizeof(buf)-1, fp); 
     while (!feof(fp)){ 
        memset(pts, 0, sizeof(pts)); 
        memset(ips, 0, sizeof(ips)); 
        sscanf(buf, "%5s%15s", pts, ips); 
        if (pts[0]!='#'){ 
           if (pts[0]!='S' && pts[0]!='s'){ 
              /* 去除末尾单独的'.' */ 
              while (ips[strlen(ips)-1]=='.') ips[strlen(ips)-1]=0; 
              for (ct=k=0;k                 if (ips[k]=='.') ct++; 
              /* 补齐 nnn.nnn.nnn.nnn 的格式 */ 
              while (ct<3){ 
                 strcat(ips, ".*"); ct++; 
              } 
              /* 端口号是否一致或是任意端口 */ 
              if (pts[0]=='*' || atoi(pts)==lport){ 
                 /* 比较IP地址是否满足匹配规则 */ 
                 if (cj_Compare(s, ips)==0){ 
                    fclose(fp); 
                    /* 返回成功--接受登录 */ 
                    return 0; 
                 } 
              } 
           } 
           else{ 
              /* 读网络日志服务器地址、端口 */ 
              memset(pts, 0, sizeof(pts)); 
              memset(ips, 0, sizeof(ips)); 
              sscanf(buf, "%*s%5s%15s", pts, R_ADDR); 
              R_PORT = atoi(pts); 
           } 
        } 
        memset(buf, 0, sizeof(buf)); 
        fgets(buf, sizeof(buf)-1, fp); 
     } 
     fclose(fp); 
  } 
  /* 未找到匹配记录,拒绝登录 */ 
  return 1; 


char *inet_ntop(int f, void *a, char *s, int l) 

  u_char *p=(u_char *)a; 
  struct in_addr inv; 
  char tt[INET_ADDRESTRLEN]; 

  if (f==AF_INET){ 
     snprintf(tt, sizeof(tt), "%d.%d.%d.%d",p[0], p[1], p[2], p[3]); 
     if (strlen(tt)>l){ 
        errno = ENOSPC; 
        return NULL; 
     } 
     strcpy(s, tt); 
     return tt; 
  } 
  errno = EAFNOSUPPORT; 
  return NULL; 


sock_ntop(struct sockaddr * sa, int len, char *s) 

  char str[128]; 
  int  pt=-1; 
  struct sockaddr_in *sin=(struct sockaddr_in *)sa; 

  s[0] = 0; 
  switch(sa->sa_family){ 
  case AF_INET:if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))==NULL) 
                  return NULL; 
               pt = ntohs(sin->sin_port); 
               strcpy(s, str); 
               break; 
  }; 
  return pt; 


get_time(int t[6]) 

  struct tm *tm; 
  time_t n; 

  n = time(NULL); 
  tm = localtime(&n); 
  t[0] = tm->tm_year + 1900; 
  t[1] = tm->tm_mon+1; 
  t[2] = tm->tm_mday; 
  t[3] = tm->tm_hour; 
  t[4] = tm->tm_min; 
  t[5] = tm->tm_sec; 


long rowid=0; 
/* 将登录信息记录到网络上日志服务器上 
  通过两次提交记录完整的登陆、退出时间 
  
  第一次记录登录时间 
  发送格式: NETLOG|本地服务端口|客户地址|端口|成功标志| 
  服务端返回一个该记录的rowid值 
  第二次记录离开时间 
  发送格式: NETUPD|原记录的rowid 
*/ 
int  net_logit(int lport, char *ips, int rport, int succ) 

  int    i,s; 
  struct sockaddr_in sin; 
  struct hostent *hp; 
  char buf[128], ss[64]; 

  if (R_PORT==0 || strlen(R_ADDR)==0) return -1; 
  bzero((char *)&sin,sizeof(sin)); 
  sin.sin_family = AF_INET; 
  /* 取日志服务器地址 */ 
  hp = gethostbyname(R_ADDR); 
  if (hp!=NULL) 
     bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length); 
  else 
     inet_aton(R_ADDR, &sin.sin_addr); 
  sin.sin_port = htons(R_PORT); 
  s = socket(AF_INET, SOCK_STREAM, 0); 
  if (s<0) return 1; 
  /* 连接日志服务器 */ 
  if ((i=connect(s, (struct sockaddr *)&sin,sizeof(sin)))<0){ 
     close(s); 
     return 2; 
  } 
  /* 发送日志信息 格式: NETLOG|本地服务端口|客户地址|端口|成功标志| */ 
  /* 第一次记录登录时间 */ 
  if (rowid==0) 
     sprintf(ss, "NETLOG|%d|%s|%d|%d|", lport, ips, rport, succ); 
  else 
  /* 第二次记录离开时间,发送信息格式: NETUPD|原记录的rowid */ 
     sprintf(ss, "NETUPD|%d|", rowid); 
  sprintf(buf, "%04d%s", strlen(ss), ss); 
  write(s, buf, strlen(buf)); 
  memset(ss, 0, sizeof(ss)); 
  if (rowid==0){ 
     /* 取该记录的rowid */ 
     read(s, ss, sizeof(ss)-1); 
     rowid = atol(ss); 
  } 
  /* 关闭连接 */ 
  shutdown(s, 2); 
  close(s); 
  return 0; 


void mysig(int sig) 

  return  


main(int argc, char *argv[], char *envp[]) 

  int  srq[6], i, ct, k, rport, lport; 
  unsigned int len; 
  char buf[128], ips[32]; 
  FILE *fp; 
  union{ 
     struct sockaddr sa; 
     char   data[MAXSOCKADDR]; 
  }from; 

  /* 取本地端口 */ 
  len = sizeof(from); 
  k = getsockname(1, (struct sockaddr *)&from.data, &len); 
  lport = sock_ntop(&from.sa, len, ips); 
  /* 取远程端口、地址 */ 
  len = sizeof(from); 
  k = getpeername(1, (struct sockaddr *)&from.data, &len); 
  rport = sock_ntop(&from.sa, len, ips); 
  get_time(srq); 
  sprintf(buf, "端口 %d, 来自 %15s:%d, 时间 %04d/%02d/%02d %02d:%02d:%02d "
               lport, ips, rport, srq[0], srq[1], srq[2], 
               srq[3], srq[4], srq[5]); 
  fp = fopen(LOGFILE, "at"); 
  if (fp!=NULL) 
     fprintf(fp, "%s", buf); 
  k = is_denied(ips, lport); 
  /* 记录登录时间 */ 
  net_logit(lport, ips, rport, k); 
  if (k){ 
     fprintf(stderr,"对不起,您没有在此主机登录的权限!\007\r\n"); 
     if (fp!=NULL){ 
        fprintf(fp, "...拒绝[%d]!\n", rowid); 
        fclose(fp); 
     } 
     exit(1); 
  } 
  if (fp){ 
     fprintf(fp, "...接受[%s][%d]!\n", argv[0], rowid); 
     fclose(fp); 
  } 
  sigset(SIGCLD, mysig); 
  /* 子进程启动相应程序,父进程等待 */ 
  if (fork()==0) 
     execve(argv[0], argv, envp); 
  else pause(); 
  /* 记录退出登录的时间 */ 
  net_logit(lport, ips, rport, k); 
  return 0; 

阅读(210) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~