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