tinyproxy/src/sock.c

363 lines
8.0 KiB
C
Raw Normal View History

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