/* * 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 #include #include #include #include #include #include #include #include #include #include #include #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))); }