summaryrefslogtreecommitdiffhomepage
path: root/src/sock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sock.c')
-rw-r--r--src/sock.c362
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;
+}