/*
* Copyright (c) 2002-2005 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
/* XXX HACK XXX */
#define XP_UNIX 1
#include "sm/generic.h"
SM_RCSID("@(#)$Id: smi-net.c,v 1.15 2005/07/12 23:23:36 ca Exp $")
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/fcntl.h"
#include <stdio.h>
#include <ctype.h>
#include "smi-net.h"
static bool net_initialized = false;
bool
net_is_ip_addr(const char * str)
{
int i = 0, oct = 1;
SM_ASSERT(str);
if (strlen(str) > 15)
/* Too many characters */
return false;
for (i = 0; str[i] != '\0'; i++)
{
if (! (isdigit(str[i]) || str[i] == '.'))
return false;
if (str[i] == '.')
{
if (i > 0 && str[i-1] == '.')
return false;
else
oct += 1;
}
}
if (oct != 4)
return false;
if (! isdigit(str[i-1]))
return false;
return true;
}
sm_ret_T
net_startup()
{
#ifdef XP_NT
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD( 2, 0 );
if (WSAStartup(wVersionRequested, &wsaData) != 0)
return FAILURE;
#endif /* XP_NT */
#ifdef XP_UNIX
/* If a client closes a connection, we definitly don't want to die */
signal(SIGPIPE, SIG_IGN);
#endif /* XP_UNIX */
net_initialized = true;
return SM_SUCCESS;
}
void
net_shutdown()
{
#ifdef XP_NT
WSACleanup();
#endif /* XP_NT */
net_initialized = false;
}
int
netserverlistenaddr(sockaddr_T *my_addr, int addrlen, int backlog)
{
int listenfd;
int sockopt;
SM_ASSERT(my_addr != NULL);
SM_ASSERT(backlog > 0);
sockopt = 1;
listenfd = INVALID_SOCKET;
SM_ASSERT(net_initialized);
/* Open listen port */
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
return sm_err_perm(errno);
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (void *) &sockopt,
sizeof(sockopt)) == -1)
{
closesocket(listenfd);
return sm_err_perm(errno);
}
if (bind(listenfd, (struct sockaddr *) my_addr, addrlen) == -1)
{
closesocket(listenfd);
return sm_err_perm(errno);
}
if (listen(listenfd, backlog) == -1)
{
closesocket(listenfd);
return sm_err_perm(errno);
}
return listenfd;
}
int
netserverlisten(char *ip, int port, int backlog)
{
struct sockaddr_in servaddr;
SM_ASSERT(port > 0);
sm_memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
if (ip == NULL || *ip == '\0')
ip = "127.0.0.1";
servaddr.sin_addr.s_addr = inet_addr(ip);
servaddr.sin_port = htons(port);
return netserverlistenaddr((sockaddr_T *)&servaddr,
sizeof(servaddr), backlog);
}
int
net_server_accept(int listenfd, struct sockaddr *addr, sockaddr_len_T *addrlen)
{
int connfd;
int sockopt;
SM_ASSERT(listenfd >= 0);
SM_ASSERT(addr != NULL);
SM_ASSERT(addrlen != NULL);
sockopt = 1;
if ((connfd = accept(listenfd, addr, addrlen)) == -1)
{
if (errno != EAGAIN)
return sm_err_perm(errno);
}
else
{
if (setsockopt(connfd, SOL_SOCKET, SO_KEEPALIVE,
(void *) &sockopt, sizeof(sockopt)) == -1)
return sm_err_perm(errno);
}
return connfd;
}
int
net_socket_create(int fd)
{
int sock;
#ifdef XP_UNIX
int flags;
#endif /* XP_UNIX */
#ifdef XP_NT
ulong ioctlarg = 1;
#endif /* XP_NT */
SM_ASSERT(fd >= 0);
sock = fd;
#ifdef XP_UNIX
if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
return sm_err_perm(errno);
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
return sm_err_perm(errno);
#endif /* XP_UNIX */
#ifdef XP_NT
if (ioctlsocket(fd, FIONBIO, &ioctlarg) == SOCKET_ERROR)
return sm_err_perm(errno);
#endif /* XP_NT */
return sock;
}
void
net_socket_close(int sock)
{
closesocket(sock);
}
#if 0
/*
** net_daemons_detach borrows from Stevens Networking Vol.1 pp 336
** (Chapter 12, Daemon Processes and the inetd Superserver),
** and from the MTA source main.c:disconnect()
*/
retcode
net_daemon_detach()
{
#ifdef XP_UNIX
int fd;
pid_t pid;
/* First fork off and terminate the parent */
pid = fork();
if (pid == -1)
return sm_err_perm(errno);
else if (pid > 0)
{
/* We're the parent, exit */
exit (EX_OK);
}
/* Become session leader */
if (setsid() == -1)
return sm_err_perm(errno);
/*
** Ignore SIGHUP and fork again. From Steven's page 336:
** "The purpose of this second fork is to guarantee that the daemon
** cannot automatically acquire a controlling terminal should it
** open a terminal device in the future. Under SVR4, when a session
** leader without a controlling terminal opens a terminal device
** (that is not currently some other session's controlling terminal),
** the terminal becomes the controlling terminal of the session
** leader. But by calling fork a second time we guarantee that the
** second child is no longer a session leader, so it cannot acquire
** a controlling terminal. We must ignore SIGHUP because when the
** session leader terminates (the first child), all processes in the
** session (our second child) are sent the SIGHUP signal."
*/
signal(SIGHUP, SIG_IGN);
pid = fork();
if (pid == -1)
return sm_err_perm(errno);
else if (pid > 0)
{
/* We're the parent (err, 1st child), exit */
exit (EX_OK);
}
/*
** chdir to root filesystem so that cores are in a known location
** and so that we don't prevent any filesystems from being unmounted
*/
if (chdir("/") == -1)
return sm_err_perm(errno);
/* Clear our umask */
umask(0);
/* Reopen stdin, stdout, and stderr to /dev/null */
freopen("/dev/null", "r", stdin);
fd = open("/dev/null", O_WRONLY, 0666);
(void) fflush(stdout);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
#endif /* XP_UNIX */
return SM_SUCCESS;
}
#endif /* 0 */
int
netclientconnect(char *ip, int port)
{
int sockfd;
struct sockaddr_in servaddr;
ulong hostaddr;
SM_ASSERT(ip != NULL);
SM_ASSERT(port > 0);
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return -1;
sm_memset(&servaddr, '\0', sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
if ((hostaddr = inet_addr(ip)) == INADDR_NONE)
return -1;
sm_memcpy(&servaddr.sin_addr, &hostaddr, sizeof(hostaddr));
if ((connect(sockfd, (struct sockaddr *) &servaddr,
sizeof(servaddr))) == -1)
return -1;
return sockfd;
}
int
net_client_connect_hostname(char *host, int port)
{
int sockfd;
struct sockaddr_in servaddr;
struct hostent * hostptr;
SM_ASSERT(host != NULL);
SM_ASSERT(port > 0);
if ((hostptr = gethostbyname(host)) == NULL)
return sm_err_perm(errno);
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return sm_err_perm(errno);
while (*(hostptr->h_addr_list) && hostptr->h_addrtype != AF_INET)
++hostptr->h_addr_list;
if (*(hostptr->h_addr_list) == NULL)
return sm_err_perm(errno);
sm_memset(&servaddr, '\0', sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
sm_memcpy(&servaddr.sin_addr, *(hostptr->h_addr_list), hostptr->h_length);
if ((connect(sockfd, (struct sockaddr *) &servaddr,
sizeof(servaddr))) == -1)
return -1;
return sockfd;
}
#if 0
/*
** NT does send so that it blocks until all data is away, Unix doesn't, so
** we have to check the return on Unix.
*/
ssize_t
net_out(int outfd, const uchar *src, size_t len)
{
ssize_t l, ret;
SM_ASSERT(outfd >= 0);
SM_ASSERT(src != NULL);
l = 0;
#ifdef XP_UNIX
/*
** Unix optimization:
** Since Unix properly notices sends to closed sockets (vs NT
** which blocks forever on a closed socket).
** For Unix send it, notice if it's block, and make sure it all
** went (this cuts the code down by 75% on 75% of the calls).
*/
ret = send(outfd, (char *) src, len, 0);
if (ret == -1 && errno == EPIPE)
{
/* Connection is closed */
return -1;
}
if (ret >= 0)
l += ret;
#endif /* XP_UNIX */
while (l < len)
{
/* XXX use write_wait() */
/* Use select to wait for the socket to open up */
fd_set writefds;
SM_ASSERT(outfd < FD_SETSIZE);
FD_ZERO(&writefds);
FD_SET(outfd, &writefds);
/* use poll if outfd too large?? */
if (select(outfd + 1, NULL, &writefds, NULL, NULL) != 1)
return sm_err_perm(errno);
/* It should now be clear to write some more */
ret = send(outfd, (char *) (src + l), len - l, 0);
if (ret == -1)
return sm_err_perm(errno);
if (ret > 0)
l += ret;
}
SM_ASSERT(l != -1);
return l;
}
/*
** net_voutf is "good enough" for now but needs to be replaced in the future
*/
static size_t
net_voutf(outfd, format, ap)
int outfd;
const char *format;
va_list ap;
{
size_t len;
/* XXX */
char buf[1024];
SM_ASSERT(outfd >= 0);
SM_ASSERT(format != NULL);
if ((len = vsnprintf(buf, sizeof(buf), format, ap)) >= sizeof(buf))
return sm_err_perm(EINVAL);
return net_out(outfd, buf, len);
}
size_t
#ifdef __STDC__
net_outf(int outfd, const char *format, ...)
#else /* __STDC__ */
net_outf(outfd, format, va_alist)
int outfd;
const char *format;
va_dcl
#endif /* __STDC__ */
{
size_t len;
va_list ap;
#ifdef __STDC__
va_start(ap, format);
#else /* __STDC__ */
va_start(va);
#endif /* __STDC__ */
len = net_voutf(outfd, format, ap);
va_end(ap);
return len;
}
size_t
net_socket_out(sock, src, len)
NetSocket sock;
const char *src;
size_t len;
{
return net_out(sock->fd, src, len);
}
/*
** net_socket_voutf is "good enough" for now but needs to be
** replaced in the future
*/
static size_t
net_socket_voutf(sock, format, ap)
NetSocket sock;
const char *format;
va_list ap;
{
size_t len;
/* XXX */
char buf[1024];
SM_ASSERT(sock != NULL);
SM_ASSERT(format != NULL);
if ((len = vsnprintf(buf, sizeof(buf), format, ap)) >= sizeof(buf))
return sm_err_perm(EINVAL);
return net_socket_out(sock, buf, len);
}
size_t
#ifdef __STDC__
net_socket_outf(NetSocket sock, const char *format, ...)
#else /* __STDC__ */
net_socket_outf(sock, format, va_alist)
NetSocket sock;
const char *format;
va_dcl
#endif /* __STDC__ */
{
size_t len;
va_list ap;
#ifdef __STDC__
va_start(ap, format);
#else /* __STDC__ */
va_start(va);
#endif /* __STDC__ */
len = net_socket_voutf(sock, format, ap);
va_end(ap);
return len;
}
#endif /* 0 */
syntax highlighted by Code2HTML, v. 0.9.1