363 lines
8.0 KiB
C
363 lines
8.0 KiB
C
|
/* $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;
|
||
|
}
|