home *** CD-ROM | disk | FTP | other *** search
/ kermit.columbia.edu / kermit.columbia.edu.tar / kermit.columbia.edu / sredird / telnetcpcd-1.09.tar.gz / telnetcpcd-1.09.tar / sockets.c < prev    next >
C/C++ Source or Header  |  2003-08-12  |  11KB  |  442 lines

  1. /*
  2.     sockets.c
  3.  
  4.     Copyright (c) 2002,2003 Thomas J Pinkl <tom@pinkl.com>
  5.  
  6.     Functions for TCP sockets for client/server applications.
  7.  
  8.     Version 1.08    05/24/1996
  9.     Version 1.09    12/17/1996
  10.     Version 1.10    02/05/1998        Changed exit() to _exit()
  11.     Version 1.11    04/22/1998        Fixed a bug related to non-blocking mode.
  12.                                     The flags for fcntl() were set incorrectly.
  13.                                     Created nonblocking() function.
  14.     Version 1.12    04/28/1999        Added conditional support for Wietse 
  15.                                     Venema's TCP wrappers library.  Enabled 
  16.                                     when USE_TCP_WRAPPERS is defined at 
  17.                                     compilation time.
  18.     Version 1.13    12/06/2000        Changed logmsg() calls which pass "%m" to 
  19.                                     use "%s" and call strerror() instead.  This 
  20.                                     is because SCO OSR 5.x's syslog() isn't 
  21.                                     expanding the "%m", though the man page 
  22.                                     says it will.
  23.     Version 1.14    10/18/2001        Changed the API of concurrent_server() and 
  24.                                     iterative_server() to take two additional 
  25.                                     parameters.
  26.     Version 1.15    12/03/2001        Adapted for telnetcpcd
  27.     Version 1.16    01/16/2003        Adjusted the 2nd arg in create_server_socket()
  28.                                     to use the NONBLOCKING symbol.
  29. */
  30.  
  31. #include "telnetcpcd.h"
  32.  
  33. #ifdef USE_TCP_WRAPPERS
  34. #include <tcpd.h>
  35. extern int hosts_ctl(char *,char *,char *,char *);
  36. int allow_severity = LOG_INFO;
  37. int deny_severity  = LOG_NOTICE;
  38. #endif
  39.  
  40. /*
  41.     attach to a tcp server on the specified host at the specified 
  42.     port number.  
  43.  
  44.     returns a socket fd or -1 on error.
  45. */
  46. int attach_client_socket(char *host,int tcp_port)
  47. {
  48.     int sockfd;                            /* returned to caller */
  49.     char *addr;                            /* ptr to dotted decimal address */
  50.     struct sockaddr_in serv_addr;
  51.  
  52.     /*
  53.         Fill in the structure "serv_addr" with the address of the
  54.         server with which we want to connect.
  55.     */
  56.     bzero((char *) &serv_addr, sizeof(serv_addr));
  57.     serv_addr.sin_family = AF_INET;
  58.     if ((addr = getinetaddr(host)) == NULL)
  59.         return(-1);
  60.     serv_addr.sin_addr.s_addr = inet_addr(addr);
  61.     serv_addr.sin_port = htons(tcp_port);
  62.     syslog(LOG_INFO,"tcp/ip address %s, tcp port %d",addr,tcp_port);
  63.  
  64.     /*
  65.         Open a TCP socket (an Internet stream socket).
  66.     */
  67.     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  68.         syslog(LOG_ERR,"cannot open stream socket");
  69.         return(-1);
  70.     }
  71.  
  72.     /*
  73.         Connect to the server.
  74.     */
  75.     if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) {
  76.         syslog(LOG_ERR,"can't connect to server");
  77.         return(-1);
  78.     }
  79.  
  80.     return(sockfd);
  81. }
  82.  
  83. /*
  84.     returns a host's internet address as a string in dotted 
  85.     decimal notation (eg. "199.254.198.12") or NULL on error.
  86. */
  87. char *getinetaddr(char *name)
  88. {
  89. #ifndef AIX
  90.     extern int h_errno;
  91. #endif
  92.     static char inetaddr[128];            /* ptr to this buffer is returned */
  93.     char hostname[MAXHOSTNAMELEN+1];
  94.     struct hostent *host;
  95.     struct in_addr *addr;
  96.     int done = 0;
  97.     int retries = 30;
  98.  
  99.     /*
  100.         if no name (ie. hostname) was supplied, then use 
  101.         the internet hostname of *this* host
  102.     */
  103.     if ((name == NULL) || (*name == '\0')) {
  104.         if (gethostname(hostname,sizeof(hostname)) != 0) {
  105.             syslog(LOG_ERR,"can't get my host name");
  106.             return(NULL);
  107.         }
  108.     } else {
  109.         if (strlen(name) <= MAXHOSTNAMELEN) {
  110.             strcpy(hostname,name);
  111.         } else {
  112.             strncpy(hostname,name,MAXHOSTNAMELEN);
  113.             hostname[MAXHOSTNAMELEN] = '\0';
  114.         }
  115.     }
  116.  
  117.     /*
  118.         get the host's host entry structure
  119.     */
  120.     while (! done) {
  121.         host = gethostbyname(hostname);
  122.         if (host != NULL) {
  123.             done = 1;
  124.         } else {
  125.             if (h_errno != TRY_AGAIN) {
  126.                 syslog(LOG_ERR,"can't get my host entry");
  127.                 return(NULL);
  128.             } else {
  129.                 --retries;
  130.                 if (retries < 0)
  131.                     return(NULL);
  132.                 sleep(2);
  133.                 continue;
  134.             }
  135.         }
  136.     }
  137.  
  138.     /*
  139.         convert the binary internet address to the 
  140.         dotted decimal string.  note that, for a host 
  141.         with multiple addresses, we only deal with the 
  142.         first one.
  143.     */
  144.     addr = (struct in_addr *) *(host->h_addr_list);
  145.     strcpy(inetaddr,inet_ntoa(*addr));
  146.     return(inetaddr);
  147. }
  148.  
  149. /*
  150.     close the client's socket
  151.  
  152.     returns nothing.
  153. */
  154. void detach_client_socket(int sockfd)
  155. {
  156.     close(sockfd);
  157. }
  158.  
  159. /*
  160.     create a tcp server on this host at the specified port 
  161.     number.  
  162.  
  163.     returns a socket fd or -1 on error.
  164. */
  165. int create_server_socket(int tcp_port,int blockopt)
  166. {
  167.     extern int errno;
  168.     int sockfd;
  169.     int opt;
  170.     struct sockaddr_in serv_addr;
  171.  
  172.     /*
  173.         Open a TCP socket (an Internet stream socket).
  174.     */
  175.     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
  176.         syslog(LOG_ERR,"cannot open stream socket (%s)",strerror(errno));
  177.         return(-1);
  178.     }
  179.  
  180.     /*
  181.         set the socket to non-blocking mode if this option 
  182.         was passed in to us
  183.     */
  184.     if (blockopt == NONBLOCKING) {
  185.         if (nonblocking(sockfd) < 0) {
  186.             syslog(LOG_ERR,"cannot set socket to non-blocking mode (%s)",strerror(errno));
  187.             close(sockfd);
  188.             return(-1);
  189.         }
  190.     }
  191.  
  192.     /*
  193.         set socket options SO_KEEPALIVE and SO_REUSEADDR 
  194.         see Steven's UNP2e Vol 1, pgs 185 & 194
  195.     */
  196.     opt = 1;
  197.     if (setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,&opt,sizeof(opt)) < 0) {
  198.         syslog(LOG_ERR,"cannot set SO_KEEPALIVE (%s)",strerror(errno));
  199.         close(sockfd);
  200.         return(-1);
  201.     }
  202.     if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)) < 0) {
  203.         syslog(LOG_ERR,"cannot set SO_REUSEADDR (%s)",strerror(errno));
  204.         close(sockfd);
  205.         return(-1);
  206.     }
  207.  
  208.     /*
  209.         Bind the wildcard address so that the client can send to us.
  210.     */
  211.     bzero((char *) &serv_addr, sizeof(serv_addr));
  212.     serv_addr.sin_family = AF_INET;
  213.     serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  214.     serv_addr.sin_port = htons(tcp_port);
  215.  
  216.     if (bind(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) {
  217.         syslog(LOG_ERR,"cannot bind local address (%s)",strerror(errno));
  218.         return(-1);
  219.     }
  220.  
  221.     listen(sockfd, 5);        /* the 5 refers to the maximum number of */
  222.                             /* connections that can be queued. 5 is the max. */
  223.     return(sockfd);
  224. }
  225.  
  226. /*
  227.     set the socket to non-blocking mode 
  228. */
  229. int nonblocking(int sockfd)
  230. {
  231.     /*
  232.         Note: ioctl(fd,FIONBIO,...) is a BSD compatability feature 
  233.               under SCO Unix.  It seems to be accepted under AIX too.
  234.     */
  235. #ifdef BSD_COMPAT    /* use for AIX? */
  236.     int arg = 1;
  237.  
  238.     if (ioctl(sockfd,FIONBIO,&arg) < 0) {
  239.         return(-1);
  240.     }
  241. #else
  242.     int flags;
  243.  
  244.     flags = fcntl(sockfd,F_GETFL,0);
  245.     if (flags != -1) {
  246.         flags |= O_NONBLOCK;
  247.         flags = fcntl(sockfd,F_SETFL,flags);
  248.     }
  249.     if (flags == -1) {
  250.         return(flags);
  251.     }
  252. #endif /* BSD_COMPAT */
  253.     return(0);
  254. }
  255.  
  256. /*
  257.     this function provides a concurrent server (ie. one that 
  258.     accepts socket connections and forks to perform the 
  259.     specified function).  the function whose address is passed 
  260.     to us will be called with its socket fd as the only arg.
  261.  
  262.     note: this function does not return.  therefore, 
  263.           any errors encountered are fatal.
  264. */
  265. void concurrent_server(int sockfd,int (*funct)(int),int *signo,void (*sighandler)(int))
  266. {
  267.     extern int errno;
  268.     int newsockfd;
  269. #ifdef AIX
  270.     size_t cli_len;
  271. #else
  272.     int cli_len;
  273. #endif
  274.     int childpid;
  275.     struct sockaddr_in cli_addr;
  276.     int ret;
  277.  
  278.     for ( ; ; ) {
  279.         /*
  280.             Wait for a connection from a client process.
  281.         */
  282.         cli_len = sizeof(cli_addr);
  283.         errno = 0;
  284.         newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,&cli_len);
  285.         if (newsockfd < 0) {
  286.             if (errno != EINTR)
  287.                 syslog(LOG_ERR,"accept error (%s)",strerror(errno));
  288.             if ((signo != NULL) && (*signo != 0)) {
  289.                 (*sighandler)(*signo);    /* call the signal handler */
  290.             }
  291.             continue;
  292.         }
  293.  
  294. #ifdef USE_TCP_WRAPPERS
  295.         if (access_control(newsockfd) != 0) {
  296.             close(newsockfd);
  297.             continue;
  298.         }
  299. #endif
  300.  
  301.         if ((childpid = fork()) < 0) {
  302.             syslog(LOG_ERR,"fork error (%s)",strerror(errno));
  303.         } else if (childpid == 0) {        /* child process */
  304.             close(sockfd);                /* close original socket */
  305.             ret = (*funct)(newsockfd);    /* process the request */
  306.             _exit(ret);
  307.         }
  308.         close(newsockfd);                /* parent process */
  309.     }
  310. }
  311.  
  312. /*
  313.     this function provides an iterative server (ie. one that 
  314.     accepts socket connections and performs the specified 
  315.     function without fork()ing).  the function whose address 
  316.     is passed to us will be called with its socket fd as the 
  317.     only arg.
  318.  
  319.     note: this function does not return.  therefore, 
  320.           any errors encountered are fatal.
  321. */
  322. void iterative_server(int sockfd,int (*funct)(int),int *signo,void (*sighandler)(int))
  323. {
  324.     extern int errno;
  325.     int newsockfd;
  326. #ifdef AIX
  327.     size_t cli_len;
  328. #else
  329.     int cli_len;
  330. #endif
  331.     struct sockaddr_in cli_addr;
  332.     int ret;
  333.  
  334.     for ( ; ; ) {
  335.         /*
  336.             Wait for a connection from a client process.
  337.         */
  338.         cli_len = sizeof(cli_addr);
  339.         errno = 0;
  340.         newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,&cli_len);
  341.         if (newsockfd < 0) {
  342.             if (errno != EINTR)
  343.                 syslog(LOG_ERR,"accept error (%s)",strerror(errno));
  344.             if ((signo != NULL) && (*signo != 0)) {
  345.                 (*sighandler)(*signo);    /* call the signal handler */
  346.             }
  347.             continue;
  348.         }
  349. #ifdef USE_TCP_WRAPPERS
  350.         if (access_control(newsockfd) != 0) {
  351.             close(newsockfd);
  352.             continue;
  353.         }
  354. #endif
  355.         ret = (*funct)(newsockfd);        /* process the request */
  356.         close(newsockfd);
  357.     }
  358. }
  359.  
  360. /*
  361.     this function accepts a socket connection if there is a 
  362.     client who wants to connect to us
  363.  
  364.     returns the socket fd of the new connection, or -1 on error.
  365.     also returns -1 if no client is waiting to connect.
  366. */
  367. int accept_server_connect(int sockfd)
  368. {
  369.     int newsockfd;
  370. #ifdef AIX
  371.     size_t cli_len;
  372. #else
  373.     int cli_len;
  374. #endif
  375.     struct sockaddr_in cli_addr;
  376.  
  377.     /*
  378.         see if a client process wants to connect to us
  379.     */
  380.     cli_len = sizeof(cli_addr);
  381.     newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,&cli_len);
  382.     if (newsockfd < 0) {
  383.         if (errno != EWOULDBLOCK)
  384.             syslog(LOG_INFO,"accept error (%s)",strerror(errno));
  385.     }
  386. #ifdef USE_TCP_WRAPPERS
  387.     if (access_control(newsockfd) != 0) {
  388.         close(newsockfd);
  389.         newsockfd = -1;
  390.     }
  391. #endif
  392.     return(newsockfd);
  393. }
  394.  
  395. #ifdef USE_TCP_WRAPPERS
  396. extern char *progname;                /* name for access control rules */
  397. /*
  398.     this function is called with a newly accept()'d socket fd
  399.  
  400.     it returns 0 if the connection is allowed by the access control 
  401.     rules provided by the TCP wrappers library.  see hosts_access(5)
  402.     and hosts_options(5) for configuration information.
  403. */
  404. int access_control(int sockfd)
  405. {
  406.     extern int allow_severity;
  407.     extern int deny_severity;
  408.     struct sockaddr_in addr;
  409.     struct hostent *host;
  410.     char *peer_name;
  411.     char *peer_addr;
  412.     char *peer_user;
  413. #ifndef OSR5
  414.     size_t len;                        /* was "int len" */
  415. #else
  416.     int len;
  417. #endif
  418.  
  419.     peer_name = peer_addr = NULL;
  420.     peer_user = STRING_UNKNOWN;
  421.     len = sizeof(addr);
  422.     if (getpeername(sockfd,(struct sockaddr *) &addr,&len) == 0) {
  423.         peer_addr = inet_ntoa(addr.sin_addr);
  424.         host = gethostbyaddr((char *) &(addr.sin_addr.s_addr),sizeof(addr.sin_addr.s_addr),AF_INET);
  425.         if (host != NULL) {
  426.             peer_name = host->h_name;
  427.         }
  428.     }
  429.     if (peer_name == NULL)
  430.         peer_name = STRING_UNKNOWN;
  431.     if (peer_addr == NULL)
  432.         peer_addr = STRING_UNKNOWN;
  433.     if (hosts_ctl(progname,peer_name,peer_addr,peer_user) == 0) {
  434.         syslog(deny_severity,"denied connection from %s (%s)",peer_name,peer_addr);
  435.         return(1);
  436.     } else {
  437.         syslog(allow_severity,"accepted connection from %s (%s)",peer_name,peer_addr);
  438.     }
  439.     return(0);
  440. }
  441. #endif    /* USE_TCP_WRAPPERS */
  442.