diff options
Diffstat (limited to 'src/sock.c')
-rw-r--r-- | src/sock.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/src/sock.c b/src/sock.c new file mode 100644 index 0000000..dc415e5 --- /dev/null +++ b/src/sock.c @@ -0,0 +1,362 @@ +/* $Id: sock.c,v 1.1.1.1 2000-02-16 17:32:23 sdyoung Exp $ + * + * Sockets are created and destroyed here. When a new connection comes in from + * a client, we need to copy the socket and the create a second socket to the + * remote server the client is trying to connect to. Also, the listening + * socket is created and destroyed here. Sounds more impressive than it + * actually is. + * + * Copyright (C) 1998 Steven Young + * Copyright (C) 1999 Robert James Kaes (rjkaes@flarenet.com) + * Copyright (C) 2000 Chris Lightfoot (chris@ex-parrot.com) + * + * 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, 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. + */ + +#ifdef HAVE_CONFIG_H +#include <defines.h> +#endif + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <errno.h> +#include <netdb.h> +#include <string.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <fcntl.h> +#include <assert.h> + +#include "tinyproxy.h" +#include "sock.h" +#include "log.h" +#include "utils.h" +#include "dnscache.h" + +/* This routine is so old I can't even remember writing it. But I do + * remember that it was an .h file because I didn't know putting code in a + * header was bad magic yet. anyway, this routine opens a connection to a + * system and returns the fd. + */ + +/* + * Cleaned up some of the code to use memory routines which are now the + * default. Also, the routine first checks to see if the address is in + * dotted-decimal form before it does a name lookup. Finally, the newly + * created socket is made non-blocking. + * - rjkaes + */ +int opensock(char *ip_addr, int port) +{ + int sock_fd, flags; + struct sockaddr_in port_info; + + assert(ip_addr); + assert(port > 0); + + memset((struct sockaddr *) &port_info, 0, sizeof(port_info)); + + port_info.sin_family = AF_INET; + + /* chris - Could block; neet to ensure that this is never called + * before a non-blocking DNS query happens for this address. Not + * relevant in the code as it stands. + */ + if (dnscache(&port_info.sin_addr, ip_addr) < 0) { + log("ERROR opensock: Could not lookup address: %s", ip_addr); + return -1; + } + + port_info.sin_port = htons(port); + + if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + log("ERROR opensock: socket (%s)", strerror(errno)); + return -1; + } + + flags = fcntl(sock_fd, F_GETFL, 0); + fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK); + + if (connect + (sock_fd, (struct sockaddr *) &port_info, sizeof(port_info)) < 0) { + if (errno != EINPROGRESS) { + log("ERROR opensock: connect (%s)", strerror(errno)); + return -1; + } + } + + return sock_fd; +} + +/* chris - added this to open a socket given a struct in_addr */ +int opensock_inaddr(struct in_addr *inaddr, int port) +{ + int sock_fd, flags; + struct sockaddr_in port_info; + + assert(inaddr); + assert(port > 0); + + memset((struct sockaddr *) &port_info, 0, sizeof(port_info)); + + port_info.sin_family = AF_INET; + + memcpy(&port_info.sin_addr, inaddr, sizeof(struct in_addr)); + + port_info.sin_port = htons(port); + + if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + log("ERROR opensock_inaddr: socket (%s)", strerror(errno)); + return -1; + } + + flags = fcntl(sock_fd, F_GETFL, 0); + fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK); + + if (connect + (sock_fd, (struct sockaddr *) &port_info, sizeof(port_info)) < 0) { + if (errno != EINPROGRESS) { + log("ERROR opensock: connect (%s)", strerror(errno)); + return -1; + } + } + + return sock_fd; +} + +int setup_fd; +static struct sockaddr listen_sock_addr; + +/* + * Start listening to a socket. + */ +int init_listen_sock(int port) +{ + struct sockaddr_in laddr; + int i = 1; + + assert(port > 0); + + if ((setup_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + log("ERROR init_listen_sock: socket (%s)", strerror(errno)); + return -1; + } + + if (setsockopt(setup_fd, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) { + log("ERROR init_listen_sock: setsockopt (%s)", + strerror(errno)); + return -1; + } + + memset(&listen_sock_addr, 0, sizeof(listen_sock_addr)); + memset(&laddr, 0, sizeof(laddr)); + laddr.sin_family = AF_INET; + laddr.sin_port = htons(port); + + if (config.ipAddr) { + laddr.sin_addr.s_addr = inet_addr(config.ipAddr); + } else { + laddr.sin_addr.s_addr = inet_addr("0.0.0.0"); + } + if (bind(setup_fd, (struct sockaddr *) &laddr, sizeof(laddr)) < 0) { + log("ERROR init_listen_sock: bind (%s)", strerror(errno)); + return -1; + } + if ((listen(setup_fd, MAXLISTEN)) != 0) { + log("ERROR init_listen_sock: listen (%s)", strerror(errno)); + return -1; + } + + return 0; +} + +/* + * Grab a pending connection + */ +int listen_sock(void) +{ + static int sock; + int sz = sizeof(listen_sock_addr); + + if ((sock = accept(setup_fd, &listen_sock_addr, &sz)) < 0) { + if (errno != ECONNABORTED +#ifdef EPROTO + && errno != EPROTO +#endif +#ifdef EWOULDBLOCK + && errno != EWOULDBLOCK +#endif + && errno != EINTR) + log("ERROR listen_sock: accept (%s)", strerror(errno)); + return -1; + } + stats.num_listens++; + + return sock; +} + +/* + * Stop listening on a socket. + */ +void de_init_listen_sock(void) +{ + close(setup_fd); +} + +/* + * Takes a socket descriptor and returns the string contain the peer's + * IP address. + */ +char *getpeer_ip(int fd, char *ipaddr) +{ + struct sockaddr_in name; + int namelen = sizeof(name); + + assert(fd >= 0); + assert(ipaddr); + + memset(ipaddr, '\0', PEER_IP_LENGTH); + + if (getpeername(fd, (struct sockaddr *) &name, &namelen) != 0) { + log("ERROR Connect: 'could not get peer name'"); + } else { + strncpy(ipaddr, + inet_ntoa(*(struct in_addr *) &name.sin_addr.s_addr), + PEER_IP_LENGTH - 1); + } + + return ipaddr; +} + +/* + * Takes a socket descriptor and returns the string containing the peer's + * address. + */ +char *getpeer_string(int fd, char *string) +{ + struct sockaddr_in name; + int namelen = sizeof(name); + struct hostent *peername; + + assert(fd >= 0); + assert(string); + + memset(string, '\0', PEER_STRING_LENGTH); + + if (getpeername(fd, (struct sockaddr *) &name, &namelen) != 0) { + log("ERROR Connect: 'could not get peer name'"); + } else { + if ( + (peername = + gethostbyaddr((char *) &name.sin_addr.s_addr, + sizeof(name.sin_addr.s_addr), + AF_INET)) != NULL) { + strncpy(string, peername->h_name, + PEER_STRING_LENGTH - 1); + } + } + + return string; +} + + +/* + * Read one line of the header + */ +int readline(int fd, struct buffer_s *buffer, char **line) +{ + char inbuf[BUFFER]; + int bytesin; + char *endline = NULL; + char *newline; + struct bufline_s *oldline; + unsigned long len = 0, length; + + assert(fd >= 0); + assert(buffer); + assert(line); + + *line = NULL; + + if ((bytesin = recv(fd, inbuf, BUFFER - 1, MSG_PEEK)) <= 0) { + goto CONN_ERROR; + } + + endline = xstrstr(inbuf, "\n", bytesin, FALSE); + + if (endline) { + endline++; + *endline = '\0'; + + length = strlen(inbuf); + memset(inbuf, '\0', BUFFER); + + /* Actually pull it off the queue */ + if ((bytesin = recv(fd, inbuf, length, 0)) <= 0) { + goto CONN_ERROR; + } + + *line = xstrdup(inbuf); + + if (buffer_size(buffer) > 0) { + if (!(newline = xmalloc(buffer_size(buffer) + + length + 1))) { + return -1; + } + + newline[0] = '\0'; + + while ((oldline = shift_buffer(buffer))) { + memcpy(newline + len, oldline->string, + oldline->length); + + len += oldline->length; + free_line(oldline); + } + + memcpy(newline + len, *line, strlen(*line) + 1); + safefree(*line); + + *line = newline; + } + + return strlen(*line); + } + + /* + * We didn't find a '\r\n', but we've filled the buffer.. pull it off + * and try again later... + */ + if ((bytesin = recv(fd, inbuf, bytesin, 0)) <= 0) { + goto CONN_ERROR; + } + + if (!(*line = xmalloc(bytesin + 1))) { + return -1; + } + + memcpy(*line, inbuf, bytesin); + (*line)[bytesin] = '\0'; + + push_buffer(buffer, *line, strlen(*line)); + + return 0; + + CONN_ERROR: + if (bytesin == 0 || (errno != EAGAIN && errno != EINTR)) { + return -1; + } + return 0; +} |