/*
* Copyright 2001-2004 Brandon Long
* All Rights Reserved.
*
* ClearSilver Templating System
*
* This code is made available under the terms of the ClearSilver License.
* http://www.clearsilver.net/license.hdf
*
*/
#include "cs_config.h"
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "neo_misc.h"
#include "neo_err.h"
#include "neo_net.h"
#include "neo_str.h"
static int ShutdownAccept = 0;
void ne_net_shutdown()
{
ShutdownAccept = 1;
}
/* Server side */
NEOERR *ne_net_listen(int port, int *fd)
{
int sfd = 0;
int on = 1;
/* int flags; */
struct sockaddr_in serv_addr;
if ((sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
return nerr_raise_errno(NERR_IO, "Unable to create socket");
if (setsockopt (sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
sizeof(on)) == -1)
{
close(sfd);
return nerr_raise_errno(NERR_IO, "Unable to setsockopt(SO_REUSEADDR)");
}
if(setsockopt (sfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&on,
sizeof(on)) == -1)
{
close(sfd);
return nerr_raise_errno(NERR_IO, "Unable to setsockopt(SO_KEEPALIVE)");
}
if(setsockopt (sfd, IPPROTO_TCP, TCP_NODELAY, (void *)&on,
sizeof(on)) == -1)
{
close(sfd);
return nerr_raise_errno(NERR_IO, "Unable to setsockopt(TCP_NODELAY)");
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
if (bind(sfd,(struct sockaddr *)&(serv_addr),sizeof(struct sockaddr)) == -1)
{
close(sfd);
return nerr_raise_errno(NERR_IO, "Unable to bind to port %d", port);
}
/* If set non-block, then we have to use select prior to accept...
* typically we don't, so we'll leave this out until we have a need
* for it and then figure out how to work it into the common code */
/*
flags = fcntl(sfd, F_GETFL, 0 );
if (flags == -1)
{
close(sfd);
return nerr_raise_errno(NERR_IO, "Unable to get socket flags for port %d",
port);
}
if (fcntl(sfd, F_SETFL, flags | O_NDELAY) == -1)
{
close(sfd);
return nerr_raise_errno(NERR_IO, "Unable to set O_NDELAY for port %d",
port);
}
*/
if (listen(sfd, 100) == -1)
{
close(sfd);
return nerr_raise_errno(NERR_IO, "Unable to listen on port %d", port);
}
*fd = sfd;
return STATUS_OK;
}
NEOERR *ne_net_accept(NSOCK **sock, int sfd, int data_timeout)
{
NSOCK *my_sock;
int fd;
struct sockaddr_in client_addr;
socklen_t len;
len = sizeof(struct sockaddr_in);
while (1)
{
fd = accept(sfd, (struct sockaddr *)&client_addr, &len);
if (fd >= 0) break;
if (ShutdownAccept || errno != EINTR)
{
return nerr_raise_errno(NERR_IO, "accept() returned error");
}
if (errno == EINTR)
{
ne_warn("accept received EINTR");
}
}
my_sock = (NSOCK *) calloc(1, sizeof(NSOCK));
if (my_sock == NULL)
{
close(fd);
return nerr_raise(NERR_NOMEM, "Unable to allocate memory for NSOCK");
}
my_sock->fd = fd;
my_sock->remote_ip = ntohl(client_addr.sin_addr.s_addr);
my_sock->remote_port = ntohs(client_addr.sin_port);
my_sock->data_timeout = data_timeout;
*sock = my_sock;
return STATUS_OK;
}
/* Client side */
NEOERR *ne_net_connect(NSOCK **sock, const char *host, int port,
int conn_timeout, int data_timeout)
{
struct sockaddr_in serv_addr;
struct hostent hp;
struct hostent *php;
int fd;
int r = 0, x;
int flags;
struct timeval tv;
fd_set fds;
int optval;
socklen_t optlen;
NSOCK *my_sock;
/* FIXME: This isn't thread safe... but there's no man entry for the _r
* version? */
php = gethostbyname(host);
if (php == NULL)
{
return nerr_raise(NERR_IO, "Host not found: %s", hstrerror(h_errno));
}
hp = *php;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd == -1)
return nerr_raise_errno(NERR_IO, "Unable to create socket");
flags = fcntl(fd, F_GETFL, 0 );
if (flags == -1)
{
close(fd);
return nerr_raise_errno(NERR_IO, "Unable to get socket flags");
}
if (fcntl(fd, F_SETFL, flags | O_NDELAY) == -1)
{
close(fd);
return nerr_raise_errno(NERR_IO, "Unable to set O_NDELAY");
}
x = 0;
while (hp.h_addr_list[x] != NULL)
{
memcpy(&(serv_addr.sin_addr), hp.h_addr_list[x], sizeof(struct in_addr));
errno = 0;
r = connect(fd, (struct sockaddr *) &(serv_addr), sizeof(struct sockaddr_in));
if (r == 0 || errno == EINPROGRESS) break;
x++;
}
if (r != 0)
{
if (errno != EINPROGRESS)
{
close(fd);
return nerr_raise_errno(NERR_IO, "Unable to connect to %s:%d",
host, port);
}
tv.tv_sec = conn_timeout;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(fd, &fds);
r = select(fd+1, NULL, &fds, NULL, &tv);
if (r == 0)
{
close(fd);
return nerr_raise(NERR_IO, "Connection to %s:%d failed: Timeout", host,
port);
}
if (r < 0)
{
close(fd);
return nerr_raise_errno(NERR_IO, "Connection to %s:%d failed", host,
port);
}
optlen = sizeof(optval);
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1)
{
close(fd);
return nerr_raise_errno(NERR_IO,
"Unable to getsockopt to determine connection error");
}
if (optval)
{
close(fd);
errno = optval;
return nerr_raise_errno(NERR_IO, "Connection to %s:%d failed", host,
port);
}
}
/* Re-enable blocking... we'll use select on read/write for timeouts
* anyways, and if we want non-blocking version in the future we'll
* add a flag or something.
*/
flags = fcntl(fd, F_GETFL, 0 );
if (flags == -1)
{
close(fd);
return nerr_raise_errno(NERR_IO, "Unable to get socket flags");
}
if (fcntl(fd, F_SETFL, flags & ~O_NDELAY) == -1)
{
close(fd);
return nerr_raise_errno(NERR_IO, "Unable to set O_NDELAY");
}
my_sock = (NSOCK *) calloc(1, sizeof(NSOCK));
if (my_sock == NULL)
{
close(fd);
return nerr_raise(NERR_NOMEM, "Unable to allocate memory for NSOCK");
}
my_sock->fd = fd;
my_sock->remote_ip = ntohl(serv_addr.sin_addr.s_addr);
my_sock->remote_port = port;
my_sock->data_timeout = data_timeout;
my_sock->conn_timeout = conn_timeout;
*sock = my_sock;
return STATUS_OK;
}
NEOERR *ne_net_close(NSOCK **sock)
{
NEOERR *err;
if (sock == NULL || *sock == NULL) return STATUS_OK;
err = ne_net_flush(*sock);
close((*sock)->fd);
free((*sock));
*sock = NULL;
return nerr_pass(err);
}
/* Low level data interface ... we are implementing a buffered stream
* here, and the fill and flush are designed for that. More over, our
* buffered stream assumes a certain type of protocol design where we
* flush the write buffer before reading... there are possible protocols
* where this would be grossly inefficient, but I don't expect to use
* anything like that */
/* Also, an annoyance here... what to do with the EOF case? Currently,
* we're just returing with a ol of 0, which means in most cases when
* calling this we have to check that case as well as standard errors.
* We could raise an NERR_EOF or something, but that seems like
* overkill. We should probably have a ret arg for the case... */
static NEOERR *ne_net_fill(NSOCK *sock)
{
NEOERR *err;
struct timeval tv;
fd_set fds;
int r;
/* Ok, we are assuming a model where one side of the connection is the
* consumer and the other the producer... and then it switches. So we
* flush the output buffer (if any) before we read */
if (sock->ol)
{
err = ne_net_flush(sock);
if (err) return nerr_pass(err);
}
/* Ok, we want connections to fail if they don't connect in
* conn_timeout... but with higher listen queues, the connection could
* actually connect, but the remote server won't get to it within the
* conn_timeout, we still want it to fail. We do that by using the
* conn_timeout on the first read ... this isn't quite the same as we
* might actually timeout at almost 2x conn_timeout (if we had to wait
* for connect and the first read) but its still better then waiting
* the full data timeout */
if (sock->conn_timeout)
{
tv.tv_sec = sock->conn_timeout;
sock->conn_timeout = 0;
}
else
{
tv.tv_sec = sock->data_timeout;
}
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(sock->fd, &fds);
r = select(sock->fd+1, &fds, NULL, NULL, &tv);
if (r == 0)
{
return nerr_raise(NERR_IO, "read failed: Timeout");
}
if (r < 0)
{
return nerr_raise_errno(NERR_IO, "select for read failed");
}
sock->ibuf[0] = '\0';
r = read(sock->fd, sock->ibuf, NET_BUFSIZE);
if (r < 0)
{
return nerr_raise_errno(NERR_IO, "read failed");
}
sock->ib = 0;
sock->il = r;
return STATUS_OK;
}
NEOERR *ne_net_flush(NSOCK *sock)
{
fd_set fds;
struct timeval tv;
int r;
int x = 0;
if (sock->conn_timeout)
{
tv.tv_sec = sock->conn_timeout;
}
else
{
tv.tv_sec = sock->data_timeout;
}
tv.tv_usec = 0;
x = 0;
while (x < sock->ol)
{
FD_ZERO(&fds);
FD_SET(sock->fd, &fds);
r = select(sock->fd+1, NULL, &fds, NULL, &tv);
if (r == 0)
{
return nerr_raise(NERR_IO, "write failed: Timeout");
}
if (r < 0)
{
return nerr_raise_errno(NERR_IO, "select for write failed");
}
r = write(sock->fd, sock->obuf + x, sock->ol - x);
if (r < 0)
{
return nerr_raise_errno(NERR_IO, "select for write failed");
}
x += r;
}
sock->ol = 0;
return STATUS_OK;
}
/* hmm, we may need something to know how much we've read here... */
NEOERR *ne_net_read(NSOCK *sock, UINT8 *buf, int buflen)
{
NEOERR *err;
int x = 0;
int l;
x = buflen;
while (x > 0)
{
if (sock->il - sock->ib > 0)
{
if (sock->ib + x <= sock->il)
l = x;
else
l = sock->il - sock->ib;
memcpy(buf + buflen - x, sock->ibuf + sock->ib, l);
sock->ib += l;
x -= l;
}
else
{
err = ne_net_fill(sock);
if (err) return nerr_pass(err);
if (sock->il == 0) return STATUS_OK;
}
}
return STATUS_OK;
}
NEOERR *ne_net_read_line(NSOCK *sock, char **buf)
{
NEOERR *err;
STRING str;
UINT8 *nl;
int l;
string_init(&str);
while (1)
{
if (sock->il - sock->ib > 0)
{
nl = memchr(sock->ibuf + sock->ib, '\n', sock->il - sock->ib);
if (nl == NULL)
{
l = sock->il - sock->ib;
err = string_appendn(&str, (char *)(sock->ibuf + sock->ib), l);
sock->ib += l;
if (err) break;
}
else
{
l = nl - (sock->ibuf + sock->ib);
err = string_appendn(&str, (char *)(sock->ibuf + sock->ib), l);
sock->ib += l;
if (err) break;
*buf = str.buf;
return STATUS_OK;
}
}
else
{
err = ne_net_fill(sock);
if (err) break;
if (sock->il == 0) return STATUS_OK;
}
}
string_clear(&str);
return nerr_pass(err);
}
static NEOERR *_ne_net_read_int(NSOCK *sock, int *i, char end)
{
NEOERR *err;
int x = 0;
char buf[32];
char *ep = NULL;
while (x < sizeof(buf))
{
while (sock->il - sock->ib > 0)
{
buf[x] = sock->ibuf[sock->ib++];
if (buf[x] == end) break;
x++;
if (x == sizeof(buf)) break;
}
if (buf[x] == end) break;
err = ne_net_fill(sock);
if (err) return nerr_pass(err);
if (sock->il == 0) return STATUS_OK;
}
if (x == sizeof(buf))
return nerr_raise(NERR_PARSE, "Format error on stream, expected '%c'", end);
buf[x] = '\0';
*i = strtol(buf, &ep, 10);
if (ep && *ep)
{
return nerr_raise(NERR_PARSE, "Format error on stream, expected number followed by '%c'", end);
}
return STATUS_OK;
}
NEOERR *ne_net_read_binary(NSOCK *sock, UINT8 **b, int *blen)
{
NEOERR *err;
UINT8 *data;
UINT8 buf[5];
int l;
err = _ne_net_read_int(sock, &l, ':');
if (err) return nerr_pass(err);
/* Special case to read a NULL */
if (l < 0)
{
*b = NULL;
if (blen != NULL) *blen = l;
return STATUS_OK;
}
data = (UINT8 *) malloc(l + 1);
if (data == NULL)
{
/* We might want to clear the incoming data here... */
return nerr_raise(NERR_NOMEM,
"Unable to allocate memory for binary data %d" , l);
}
err = ne_net_read(sock, data, l);
if (err)
{
free(data);
return nerr_pass(err);
}
/* check for comma separator */
err = ne_net_read(sock, buf, 1);
if (err)
{
free(data);
return nerr_pass(err);
}
if (buf[0] != ',')
{
free(data);
return nerr_raise(NERR_PARSE, "Format error on stream, expected ','");
}
*b = data;
if (blen != NULL) *blen = l;
return STATUS_OK;
}
NEOERR *ne_net_read_str_alloc(NSOCK *sock, char **s, int *len)
{
NEOERR *err;
int l;
/* just use the binary read and null terminate the string... */
err = ne_net_read_binary(sock, (UINT8 **)s, &l);
if (err) return nerr_pass(err);
if (*s != NULL)
{
(*s)[l] = '\0';
}
if (len != NULL) *len = l;
return STATUS_OK;
}
NEOERR *ne_net_read_int(NSOCK *sock, int *i)
{
return nerr_pass(_ne_net_read_int(sock, i, ','));
}
NEOERR *ne_net_write(NSOCK *sock, const char *b, int blen)
{
NEOERR *err;
int x = 0;
int l;
x = blen;
while (x > 0)
{
if (sock->ol < NET_BUFSIZE)
{
if (sock->ol + x <= NET_BUFSIZE)
{
l = x;
}
else
{
l = NET_BUFSIZE - sock->ol;
}
memcpy(sock->obuf + sock->ol, b + blen - x, l);
sock->ol += l;
x -= l;
}
else
{
err = ne_net_flush(sock);
if (err) return nerr_pass(err);
}
}
return STATUS_OK;
}
NEOERR *ne_net_write_line(NSOCK *sock, const char *s)
{
NEOERR *err;
err = ne_net_write(sock, s, strlen(s));
if (err) return nerr_pass(err);
err = ne_net_write(sock, "\n", 1);
if (err) return nerr_pass(err);
return STATUS_OK;
}
NEOERR *ne_net_write_binary(NSOCK *sock, const char *b, int blen)
{
NEOERR *err;
char buf[32];
if (b == NULL) blen = -1;
snprintf(buf, sizeof(buf), "%d:", blen);
err = ne_net_write(sock, buf, strlen(buf));
if (err) return nerr_pass(err);
if (blen > 0)
{
err = ne_net_write(sock, b, blen);
if (err) return nerr_pass(err);
}
err = ne_net_write(sock, ",", 1);
if (err) return nerr_pass(err);
return STATUS_OK;
}
NEOERR *ne_net_write_str(NSOCK *sock, const char *s)
{
NEOERR *err;
if (s == NULL)
err = ne_net_write_binary(sock, s, -1);
else
err = ne_net_write_binary(sock, s, strlen(s));
return nerr_pass(err);
}
NEOERR *ne_net_write_int(NSOCK *sock, int i)
{
char buf[32];
snprintf(buf, sizeof(buf), "%d,", i);
return nerr_pass(ne_net_write(sock, buf, strlen(buf)));
}