/**************************************************************************************************
$Header: /pub/cvsroot/yencode/src/ypost/sock.c,v 1.2 2002/03/21 04:58:31 bboy Exp $
Copyright (C) 2002 Don Moore <bboy@bboy.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at Your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
**************************************************************************************************/
#include "ypost.h"
/* Current socket operation being performed (for timeout handler). */
static char *current_operation;
/* Macro to start a timeout condition block. */
#define START_TIMEOUT(s) \
if (opt_timeout) { \
current_operation = s; \
signal(SIGALRM, sock_timeout_handler); \
alarm(opt_timeout); \
}
/* Macro to stop a timeout condition block. */
#define STOP_TIMEOUT \
if (opt_timeout) { \
signal(SIGALRM, SIG_DFL); \
alarm(0); \
}
/* Read buffers grow by this many bytes at a time. */
#define BUFFER_INCREMENT 128
/* Current FD if sock_setfd() is used. */
int _sock_current_fd = -1;
#define Y_SOCKDEBUG_SEND 0
#define Y_SOCKDEBUG_RECV 1
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SOCKDEBUG
If debug is enabled, this will output the data sent or received, hiding wierd control chars
and converting CR and LF to '\r' and '\n'.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
sockdebug(int direction, unsigned char *buf, size_t buflen)
{
register int ct;
if (!buf)
return;
if (direction == Y_SOCKDEBUG_RECV)
fputs("[DEBUG] IN: `", stderr);
else
fputs("[DEBUG] OUT: `", stderr);
for (ct = 0; ct < buflen; ct++)
{
if (buf[ct] == CR)
{
fputc('\\', stderr);
fputc('r', stderr);
}
else if (buf[ct] == LF)
{
fputc('\\', stderr);
fputc('n', stderr);
}
// else if (iscntrl(buf[ct]))
// fputc('?', stderr);
else
fputc(buf[ct], stderr);
}
fputc('\'', stderr);
fputc('\n', stderr);
fflush(stderr);
}
/*--- sockdebug() -------------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SOCK_TIMEOUT_HANDLER
Called if socket operations time out.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
sock_timeout_handler(int signo)
{
Err("%s: %s\n", current_operation ? current_operation : _("socket operation"),
_("specified timeout exceeded"));
exit(EXIT_FAILURE);
}
/*--- sock_timeout_handler() --------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Given a host and port, which should be specified in the format "host:port", constructs and
returns a sockaddr_in record.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
struct sockaddr_in *
sock_getsockaddr(char *address)
{
static struct sockaddr_in sa; // Socket structureA
struct servent *ent; // Service entry
struct hostent *he; // Host entry
char *hostname, *portname; // Host and port part of `hostport'
memset(&sa, 0, sizeof(struct sockaddr_in)); // Reset socket structure
sa.sin_family = AF_INET;
portname = address;
hostname = strsep(&portname, ":");
if (!hostname)
Err("%s: %s", address, _("invalid server name"));
if (portname)
sa.sin_port = htons(atoi(portname));
else
{
START_TIMEOUT(_("service entry lookup"));
if (!(ent = getservbyname("nntp", "tcp")))
Err(_("unable to get default port for service `nntp/tcp'"));
STOP_TIMEOUT;
sa.sin_port = ent->s_port;
}
START_TIMEOUT(_("server name resolution"));
if (!(he = gethostbyname(hostname)))
Err("%s: %s", address, _("unable to resolve server address"));
STOP_TIMEOUT;
memcpy(&sa.sin_addr, he->h_addr_list[0], sizeof(struct in_addr));
return (&sa);
}
/*--- sock_getsockaddr() ------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Opens a connection to the specified address. Returns the new file descriptor. Errors fatal.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
int
sock_open(struct sockaddr_in *sa)
{
int fd; // File descriptor of new socket
int rv; // Return value from connect
START_TIMEOUT(_("socket creation"));
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) // Create socket
ErrERR(_("error creating socket"));
STOP_TIMEOUT;
START_TIMEOUT(_("socket connection"));
while ((rv = connect(fd, (struct sockaddr *)sa, sizeof(struct sockaddr_in))) != 0)
{
if (errno == EAGAIN)
continue;
else
ErrERR(_("error connecting to server"));
}
STOP_TIMEOUT;
return (fd);
}
/*--- sock_open() -------------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SOCK_READ_FD
Reads from the socket, obeying timeout. Returns number of bytes read.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
ssize_t
sock_read_fd(int fd, unsigned char *buf, size_t count)
{
int rv; // Return value from read()
memset(buf, 0, count); // XXX Is this necessary?
START_TIMEOUT(_("socket read"));
rv = read(fd, buf, count);
STOP_TIMEOUT;
if (rv < 0) // Will this fail falsely on EAGAIN?
ErrERR(_("error reading from socket"));
return (rv);
}
/*--- sock_read_fd() ----------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SOCK_GETS_FD
Reads from the specified descriptor until '\n' is read. Returns a pointer to a dynamically
allocated buffer, which should be free()'d when no longer needed.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
unsigned char *
sock_gets_fd(int fd)
{
int rv; // Return value from select
int offset = 0; // Number of characters read
int buflen = 0; // Current size of input buffer
unsigned char *buf; // Input buffer
// Start our input buffer off
buf = (unsigned char *)xmalloc(BUFFER_INCREMENT, 1);
memset(buf, 0, BUFFER_INCREMENT);
buflen = BUFFER_INCREMENT;
for (;;)
{
START_TIMEOUT(_("socket read"));
if (!(rv = sock_read_fd(fd, buf + offset, 1))) // Read one char at a time
Err(_("connection closed by remote"));
STOP_TIMEOUT;
if (buf[offset] == LF) // Are we done?
break;
// Grow the buffer if necessary
if (++offset == buflen - 1)
{
buf = xrealloc(buf, buflen + BUFFER_INCREMENT);
memset(buf + buflen, 0, BUFFER_INCREMENT);
buflen += BUFFER_INCREMENT;
}
};
if (opt_debug)
sockdebug(Y_SOCKDEBUG_RECV, buf, strlen(buf));
while ((buf[offset] == CR) || (buf[offset] == LF))
buf[offset--] = (unsigned char)'\0';
return (buf);
}
/*--- sock_gets_fd() ----------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SOCK_WRITE_FD
Writes to the socket, obeying timeout.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
sock_write_fd(int fd, const unsigned char *buf, int count)
{
int offset = 0; // Number of bytes written
int rv; // Return value from write()
do
{
START_TIMEOUT(_("socket write"));
rv = write(fd, buf + offset, count - offset);
STOP_TIMEOUT;
if (rv == 0)
Err(_("connection closed by remote"));
if (rv < 0)
{
if (errno == EAGAIN)
continue;
Err(_("error writing to socket"));
}
if (opt_debug)
sockdebug(Y_SOCKDEBUG_SEND, (unsigned char *)(buf+offset), rv);
offset += rv;
} while (offset < count);
}
/*--- sock_write_fd() ---------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SOCK_PUTS_FD
Writes a line to the descriptor. Appends CR/LF if necessary.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
sock_puts_fd(int fd, const unsigned char *data)
{
int datalen = strlen(data);
if (!strstr(data, CRLF)) // Append CR/LF if necessary
{
unsigned char *buf;
buf = (unsigned char *)xmalloc((datalen + 3) * sizeof(unsigned char));
memcpy(buf, data, datalen);
buf[datalen] = CR;
buf[datalen+1] = LF;
buf[datalen+2] = (unsigned char)'\0';
sock_write_fd(fd, buf, datalen + 2);
free(buf);
}
else
sock_write_fd(fd, data, datalen);
}
/*--- sock_puts_fd() ----------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SOCK_PRINTF
Only works if _sock_current_fd is set.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
sock_printf(const char *fmt, ...)
{
va_list ap;
size_t len;
unsigned char buf[BUFSIZ];
unsigned char *outbuf;
register unsigned char *i, *o;
if (_SOCK_CURRENT_FD == -1)
Err(_("unable to write; not connected to remote server"));
va_start(ap, fmt);
len = vsnprintf(buf, sizeof(buf)-4, fmt, ap);
va_end(ap);
/* Remove CR, convert LF to CRLF */
outbuf = (unsigned char *)xmalloc(((len * 2) + 1) * sizeof(unsigned char));
for (i = buf, o = outbuf; *i; i++)
{
if (*i == CR)
continue;
else if (*i == LF)
{
*(o++) = CR;
*(o++) = LF;
}
else
*(o++) = *i;
}
*o = '\0';
sock_puts_fd(_SOCK_CURRENT_FD, outbuf);
free(outbuf);
}
/*--- sock_printf() -----------------------------------------------------------------------------*/
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SOCK_PUTS_METER_FD
Writes a line to the descriptor, displaying a meter. Appends CR/LF if necessary.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
long
sock_puts_meter_fd(int fd, const unsigned char *buf, long current, long total, const char *desc)
{
int datalen = strlen(buf); // Length of data to write
unsigned char *data = NULL; // The data to send
register int offset = 0; // Number of bytes written
int rv; // Return value from write()
if (!strstr(buf, CRLF)) // Append CR/LF if necessary
{
data = (unsigned char *)xmalloc((datalen + 3) * sizeof(unsigned char));
memcpy(data, buf, datalen);
data[datalen] = CR;
data[datalen+1] = LF;
data[datalen+2] = (unsigned char)'\0';
datalen += 2;
}
else
data = (unsigned char *)xstrdup(buf);
/* Write the data */
do
{
START_TIMEOUT(_("socket write"));
rv = write(fd, data + offset, datalen - offset);
STOP_TIMEOUT;
if (rv == 0)
Err(_("connection closed by remote"));
if (rv < 0)
{
if (errno == EAGAIN)
continue;
Err(_("error writing to socket"));
}
if (opt_debug)
sockdebug(Y_SOCKDEBUG_SEND, (unsigned char *)(data+offset), rv);
offset += rv;
meter(current + offset, total, desc);
} while (offset < datalen);
free(data);
return (offset);
}
/*--- sock_puts_fd() ----------------------------------------------------------------------------*/
/* vi:set ts=3: */
syntax highlighted by Code2HTML, v. 0.9.1