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 >
Wrap
C/C++ Source or Header
|
2003-08-12
|
11KB
|
442 lines
/*
sockets.c
Copyright (c) 2002,2003 Thomas J Pinkl <tom@pinkl.com>
Functions for TCP sockets for client/server applications.
Version 1.08 05/24/1996
Version 1.09 12/17/1996
Version 1.10 02/05/1998 Changed exit() to _exit()
Version 1.11 04/22/1998 Fixed a bug related to non-blocking mode.
The flags for fcntl() were set incorrectly.
Created nonblocking() function.
Version 1.12 04/28/1999 Added conditional support for Wietse
Venema's TCP wrappers library. Enabled
when USE_TCP_WRAPPERS is defined at
compilation time.
Version 1.13 12/06/2000 Changed logmsg() calls which pass "%m" to
use "%s" and call strerror() instead. This
is because SCO OSR 5.x's syslog() isn't
expanding the "%m", though the man page
says it will.
Version 1.14 10/18/2001 Changed the API of concurrent_server() and
iterative_server() to take two additional
parameters.
Version 1.15 12/03/2001 Adapted for telnetcpcd
Version 1.16 01/16/2003 Adjusted the 2nd arg in create_server_socket()
to use the NONBLOCKING symbol.
*/
#include "telnetcpcd.h"
#ifdef USE_TCP_WRAPPERS
#include <tcpd.h>
extern int hosts_ctl(char *,char *,char *,char *);
int allow_severity = LOG_INFO;
int deny_severity = LOG_NOTICE;
#endif
/*
attach to a tcp server on the specified host at the specified
port number.
returns a socket fd or -1 on error.
*/
int attach_client_socket(char *host,int tcp_port)
{
int sockfd; /* returned to caller */
char *addr; /* ptr to dotted decimal address */
struct sockaddr_in serv_addr;
/*
Fill in the structure "serv_addr" with the address of the
server with which we want to connect.
*/
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
if ((addr = getinetaddr(host)) == NULL)
return(-1);
serv_addr.sin_addr.s_addr = inet_addr(addr);
serv_addr.sin_port = htons(tcp_port);
syslog(LOG_INFO,"tcp/ip address %s, tcp port %d",addr,tcp_port);
/*
Open a TCP socket (an Internet stream socket).
*/
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
syslog(LOG_ERR,"cannot open stream socket");
return(-1);
}
/*
Connect to the server.
*/
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) {
syslog(LOG_ERR,"can't connect to server");
return(-1);
}
return(sockfd);
}
/*
returns a host's internet address as a string in dotted
decimal notation (eg. "199.254.198.12") or NULL on error.
*/
char *getinetaddr(char *name)
{
#ifndef AIX
extern int h_errno;
#endif
static char inetaddr[128]; /* ptr to this buffer is returned */
char hostname[MAXHOSTNAMELEN+1];
struct hostent *host;
struct in_addr *addr;
int done = 0;
int retries = 30;
/*
if no name (ie. hostname) was supplied, then use
the internet hostname of *this* host
*/
if ((name == NULL) || (*name == '\0')) {
if (gethostname(hostname,sizeof(hostname)) != 0) {
syslog(LOG_ERR,"can't get my host name");
return(NULL);
}
} else {
if (strlen(name) <= MAXHOSTNAMELEN) {
strcpy(hostname,name);
} else {
strncpy(hostname,name,MAXHOSTNAMELEN);
hostname[MAXHOSTNAMELEN] = '\0';
}
}
/*
get the host's host entry structure
*/
while (! done) {
host = gethostbyname(hostname);
if (host != NULL) {
done = 1;
} else {
if (h_errno != TRY_AGAIN) {
syslog(LOG_ERR,"can't get my host entry");
return(NULL);
} else {
--retries;
if (retries < 0)
return(NULL);
sleep(2);
continue;
}
}
}
/*
convert the binary internet address to the
dotted decimal string. note that, for a host
with multiple addresses, we only deal with the
first one.
*/
addr = (struct in_addr *) *(host->h_addr_list);
strcpy(inetaddr,inet_ntoa(*addr));
return(inetaddr);
}
/*
close the client's socket
returns nothing.
*/
void detach_client_socket(int sockfd)
{
close(sockfd);
}
/*
create a tcp server on this host at the specified port
number.
returns a socket fd or -1 on error.
*/
int create_server_socket(int tcp_port,int blockopt)
{
extern int errno;
int sockfd;
int opt;
struct sockaddr_in serv_addr;
/*
Open a TCP socket (an Internet stream socket).
*/
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
syslog(LOG_ERR,"cannot open stream socket (%s)",strerror(errno));
return(-1);
}
/*
set the socket to non-blocking mode if this option
was passed in to us
*/
if (blockopt == NONBLOCKING) {
if (nonblocking(sockfd) < 0) {
syslog(LOG_ERR,"cannot set socket to non-blocking mode (%s)",strerror(errno));
close(sockfd);
return(-1);
}
}
/*
set socket options SO_KEEPALIVE and SO_REUSEADDR
see Steven's UNP2e Vol 1, pgs 185 & 194
*/
opt = 1;
if (setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,&opt,sizeof(opt)) < 0) {
syslog(LOG_ERR,"cannot set SO_KEEPALIVE (%s)",strerror(errno));
close(sockfd);
return(-1);
}
if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)) < 0) {
syslog(LOG_ERR,"cannot set SO_REUSEADDR (%s)",strerror(errno));
close(sockfd);
return(-1);
}
/*
Bind the wildcard address so that the client can send to us.
*/
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(tcp_port);
if (bind(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) {
syslog(LOG_ERR,"cannot bind local address (%s)",strerror(errno));
return(-1);
}
listen(sockfd, 5); /* the 5 refers to the maximum number of */
/* connections that can be queued. 5 is the max. */
return(sockfd);
}
/*
set the socket to non-blocking mode
*/
int nonblocking(int sockfd)
{
/*
Note: ioctl(fd,FIONBIO,...) is a BSD compatability feature
under SCO Unix. It seems to be accepted under AIX too.
*/
#ifdef BSD_COMPAT /* use for AIX? */
int arg = 1;
if (ioctl(sockfd,FIONBIO,&arg) < 0) {
return(-1);
}
#else
int flags;
flags = fcntl(sockfd,F_GETFL,0);
if (flags != -1) {
flags |= O_NONBLOCK;
flags = fcntl(sockfd,F_SETFL,flags);
}
if (flags == -1) {
return(flags);
}
#endif /* BSD_COMPAT */
return(0);
}
/*
this function provides a concurrent server (ie. one that
accepts socket connections and forks to perform the
specified function). the function whose address is passed
to us will be called with its socket fd as the only arg.
note: this function does not return. therefore,
any errors encountered are fatal.
*/
void concurrent_server(int sockfd,int (*funct)(int),int *signo,void (*sighandler)(int))
{
extern int errno;
int newsockfd;
#ifdef AIX
size_t cli_len;
#else
int cli_len;
#endif
int childpid;
struct sockaddr_in cli_addr;
int ret;
for ( ; ; ) {
/*
Wait for a connection from a client process.
*/
cli_len = sizeof(cli_addr);
errno = 0;
newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,&cli_len);
if (newsockfd < 0) {
if (errno != EINTR)
syslog(LOG_ERR,"accept error (%s)",strerror(errno));
if ((signo != NULL) && (*signo != 0)) {
(*sighandler)(*signo); /* call the signal handler */
}
continue;
}
#ifdef USE_TCP_WRAPPERS
if (access_control(newsockfd) != 0) {
close(newsockfd);
continue;
}
#endif
if ((childpid = fork()) < 0) {
syslog(LOG_ERR,"fork error (%s)",strerror(errno));
} else if (childpid == 0) { /* child process */
close(sockfd); /* close original socket */
ret = (*funct)(newsockfd); /* process the request */
_exit(ret);
}
close(newsockfd); /* parent process */
}
}
/*
this function provides an iterative server (ie. one that
accepts socket connections and performs the specified
function without fork()ing). the function whose address
is passed to us will be called with its socket fd as the
only arg.
note: this function does not return. therefore,
any errors encountered are fatal.
*/
void iterative_server(int sockfd,int (*funct)(int),int *signo,void (*sighandler)(int))
{
extern int errno;
int newsockfd;
#ifdef AIX
size_t cli_len;
#else
int cli_len;
#endif
struct sockaddr_in cli_addr;
int ret;
for ( ; ; ) {
/*
Wait for a connection from a client process.
*/
cli_len = sizeof(cli_addr);
errno = 0;
newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,&cli_len);
if (newsockfd < 0) {
if (errno != EINTR)
syslog(LOG_ERR,"accept error (%s)",strerror(errno));
if ((signo != NULL) && (*signo != 0)) {
(*sighandler)(*signo); /* call the signal handler */
}
continue;
}
#ifdef USE_TCP_WRAPPERS
if (access_control(newsockfd) != 0) {
close(newsockfd);
continue;
}
#endif
ret = (*funct)(newsockfd); /* process the request */
close(newsockfd);
}
}
/*
this function accepts a socket connection if there is a
client who wants to connect to us
returns the socket fd of the new connection, or -1 on error.
also returns -1 if no client is waiting to connect.
*/
int accept_server_connect(int sockfd)
{
int newsockfd;
#ifdef AIX
size_t cli_len;
#else
int cli_len;
#endif
struct sockaddr_in cli_addr;
/*
see if a client process wants to connect to us
*/
cli_len = sizeof(cli_addr);
newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,&cli_len);
if (newsockfd < 0) {
if (errno != EWOULDBLOCK)
syslog(LOG_INFO,"accept error (%s)",strerror(errno));
}
#ifdef USE_TCP_WRAPPERS
if (access_control(newsockfd) != 0) {
close(newsockfd);
newsockfd = -1;
}
#endif
return(newsockfd);
}
#ifdef USE_TCP_WRAPPERS
extern char *progname; /* name for access control rules */
/*
this function is called with a newly accept()'d socket fd
it returns 0 if the connection is allowed by the access control
rules provided by the TCP wrappers library. see hosts_access(5)
and hosts_options(5) for configuration information.
*/
int access_control(int sockfd)
{
extern int allow_severity;
extern int deny_severity;
struct sockaddr_in addr;
struct hostent *host;
char *peer_name;
char *peer_addr;
char *peer_user;
#ifndef OSR5
size_t len; /* was "int len" */
#else
int len;
#endif
peer_name = peer_addr = NULL;
peer_user = STRING_UNKNOWN;
len = sizeof(addr);
if (getpeername(sockfd,(struct sockaddr *) &addr,&len) == 0) {
peer_addr = inet_ntoa(addr.sin_addr);
host = gethostbyaddr((char *) &(addr.sin_addr.s_addr),sizeof(addr.sin_addr.s_addr),AF_INET);
if (host != NULL) {
peer_name = host->h_name;
}
}
if (peer_name == NULL)
peer_name = STRING_UNKNOWN;
if (peer_addr == NULL)
peer_addr = STRING_UNKNOWN;
if (hosts_ctl(progname,peer_name,peer_addr,peer_user) == 0) {
syslog(deny_severity,"denied connection from %s (%s)",peer_name,peer_addr);
return(1);
} else {
syslog(allow_severity,"accepted connection from %s (%s)",peer_name,peer_addr);
}
return(0);
}
#endif /* USE_TCP_WRAPPERS */