/cntlm-0.92/socket.c
C | 283 lines | 156 code | 37 blank | 90 comment | 43 complexity | ef2d32b2198dd8ffbcb06911298da312 MD5 | raw file
Possible License(s): GPL-2.0
- /*
- * These are socket routines for the main module of CNTLM
- *
- * CNTLM 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.
- *
- * CNTLM 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., 51 Franklin
- * St, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * Copyright (c) 2007 David Kubicek
- *
- */
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/time.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <errno.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <netdb.h>
- #include <syslog.h>
- #include "utils.h"
- extern int debug;
- /*
- * gethostbyname() wrapper. Return 1 if OK, otherwise 0.
- */
- int so_resolv(struct in_addr *host, const char *name) {
- /*
- struct hostent *resolv;
- resolv = gethostbyname(name);
- if (!resolv)
- return 0;
- memcpy(host, resolv->h_addr_list[0], resolv->h_length);
- return 1;
- */
- struct addrinfo hints, *res, *p;
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_STREAM;
- int rc = getaddrinfo(name, NULL, &hints, &res);
- if (rc != 0) {
- if (debug)
- printf("so_resolv: %s failed (%d: %s)\n", name, rc, gai_strerror(rc));
- return 0;
- }
- if (debug)
- printf("Resolve %s:\n", name);
- int addr_set = 0;
- for (p = res; p != NULL; p = p->ai_next) {
- struct sockaddr_in *ad = (struct sockaddr_in*)(p->ai_addr);
- if (ad == NULL) {
- freeaddrinfo(res);
- return 0;
- }
- if (!addr_set) {
- memcpy(host, &ad->sin_addr, p->ai_addrlen);
- addr_set = 1;
- if (debug)
- printf(" -> %s\n", inet_ntoa(ad->sin_addr));
- } else
- if (debug)
- printf(" %s\n", inet_ntoa(ad->sin_addr));
- }
- freeaddrinfo(res);
- return 1;
- }
- /*
- * Connect to a host. Host is required to be resolved
- * in the struct in_addr already.
- * Returns: socket descriptor
- */
- int so_connect(struct in_addr host, int port) {
- int flags, fd, rc;
- struct sockaddr_in saddr;
- // struct timeval tv;
- // fd_set fds;
- if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
- if (debug)
- printf("so_connect: create: %s\n", strerror(errno));
- return -1;
- }
- memset(&saddr, 0, sizeof(saddr));
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(port);
- saddr.sin_addr = host;
- if ((flags = fcntl(fd, F_GETFL, 0)) < 0) {
- if (debug)
- printf("so_connect: get flags: %s\n", strerror(errno));
- close(fd);
- return -1;
- }
- /* NON-BLOCKING connect with timeout
- if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
- if (debug)
- printf("so_connect: set non-blocking: %s\n", strerror(errno));
- close(fd);
- return -1;
- }
- */
- rc = connect(fd, (struct sockaddr *)&saddr, sizeof(saddr));
- /*
- printf("connect = %d\n", rc);
- if (rc < 0 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS)) {
- FD_ZERO(&fds);
- FD_SET(fd, &fds);
- tv.tv_sec = 10;
- tv.tv_usec = 0;
- printf("select!\n");
- rc = select(fd+1, NULL, &fds, NULL, &tv) - 1;
- printf("select = %d\n", rc);
- }
- */
- if (rc < 0) {
- if (debug)
- printf("so_connect: %s\n", strerror(errno));
- close(fd);
- return -1;
- }
- if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0) {
- if (debug)
- printf("so_connect: set blocking: %s\n", strerror(errno));
- close(fd);
- return -1;
- }
- return fd;
- }
- /*
- * Bind the specified port and listen on it.
- * Return socket descriptor if OK, otherwise 0.
- */
- int so_listen(int port, struct in_addr source) {
- struct sockaddr_in saddr;
- int fd;
- socklen_t clen;
- fd = socket(PF_INET, SOCK_STREAM, 0);
- if (fd < 0) {
- if (debug)
- printf("so_listen: new socket: %s\n", strerror(errno));
- close(fd);
- return -1;
- }
- clen = 1;
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &clen, sizeof(clen));
- memset((void *)&saddr, 0, sizeof(saddr));
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(port);
- saddr.sin_addr.s_addr = source.s_addr;
- if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr))) {
- syslog(LOG_ERR, "Cannot bind port %d: %s!\n", port, strerror(errno));
- close(fd);
- return -1;
- }
- if (listen(fd, 5)) {
- close(fd);
- return -1;
- }
- return fd;
- }
- /*
- * Return 1 if data is available on the socket,
- * 0 if connection was closed
- * -1 if error (errno is set)
- */
- int so_recvtest(int fd) {
- char buf;
- int i;
- #ifndef MSG_DONTWAIT
- unsigned int flags;
- flags = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, flags | O_NONBLOCK);
- i = recv(fd, &buf, 1, MSG_PEEK);
- fcntl(fd, F_SETFL, flags);
- #else
- i = recv(fd, &buf, 1, MSG_DONTWAIT | MSG_PEEK);
- #endif
- return i;
- }
- /*
- * Return true if there are some data on the socket
- */
- int so_dataready(int fd) {
- return so_recvtest(fd) > 0;
- }
- /*
- * Reliable way of finding out whether a connection was closed
- * on the remote end, without actually reading from it.
- */
- int so_closed(int fd) {
- int i;
- if (fd == -1)
- return 1;
- i = so_recvtest(fd);
- return (i == 0 || (i == -1 && errno != EAGAIN && errno != ENOENT)); /* ENOENT, you ask? Perhap AIX devels could explain! :-( */
- }
- /*
- * Receive a single line from the socket. This is no super-efficient
- * implementation, but more than we need to read in a few headers.
- * What's more, the data is actually recv'd from a socket buffer.
- *
- * I had to time this in comparison to recv with block read :) and
- * the performance was very similar. Given the fact that it keeps us
- * from creating a whole buffering scheme around the socket (HTTP
- * connection is both line and block oriented, switching back and forth),
- * it is actually OK.
- */
- int so_recvln(int fd, char **buf, int *size) {
- int len = 0;
- int r = 1;
- char c = 0;
- char *tmp;
- while (len < *size-1 && c != '\n') {
- r = read(fd, &c, 1);
- if (r <= 0)
- break;
- (*buf)[len++] = c;
- /*
- * End of buffer, still no EOL? Resize the buffer
- */
- if (len == *size-1 && c != '\n') {
- if (debug)
- printf("so_recvln(%d): realloc %d\n", fd, *size*2);
- *size *= 2;
- tmp = realloc(*buf, *size);
- if (tmp == NULL)
- return -1;
- else
- *buf = tmp;
- }
- }
- (*buf)[len] = 0;
- return r;
- }