/* dircproxy
* Copyright (C) 2002 Scott James Remnant <scott@netsplit.com>.
* All Rights Reserved.
*
* dcc_net.c
* - Creating new DCC connections
* - Connecting to DCC Senders
* - The list of currently active DCC proxies
* - Miscellaneous DCC functions
* --
* @(#) $Id: dcc_net.c,v 1.12 2001/12/21 20:15:55 keybuk Exp $
*
* This file is distributed according to the GNU General Public
* License. For full details, read the top of 'main.c' or the
* file called COPYING that was distributed with this code.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <dircproxy.h>
#include "net.h"
#include "dns.h"
#include "timers.h"
#include "sprintf.h"
#include "stringex.h"
#include "dcc_chat.h"
#include "dcc_send.h"
#include "dcc_net.h"
/* forward declarations */
static int _dccnet_listen(struct dccproxy *, int *, size_t, int *);
static int _dccnet_connect(struct dccproxy *, struct in_addr, int,
int *, size_t, int *);
static int _dccnet_bind(int sock, int *, size_t, int *);
static void _dccnet_timedout(struct dccproxy *, void *);
static void _dccnet_accept(struct dccproxy *, int);
static void _dccnet_free(struct dccproxy *);
/* list of currently proxied connections */
static struct dccproxy *proxies = 0;
/* Create a new DCC connection */
int dccnet_new(int type, long timeout, int *range, size_t range_sz,
int *lport, struct in_addr addr, int port,
const char *filename, long maxsize,
int (*n_f)(void *, const char *), void *n_p, const char *n_msg) {
struct dccproxy *p;
p = (struct dccproxy *)malloc(sizeof(struct dccproxy));
memset(p, 0, sizeof(struct dccproxy));
p->type = type;
/* If we're capturing, we do not need to listen for the client connecting
because its not going to! */
if (p->type & DCC_SEND_CAPTURE) {
/* Unlink first for security */
if (unlink(filename) && (errno != ENOENT)) {
syscall_fail("unlink", filename, 0);
free(p);
return -1;
}
/* Open for writing */
p->cap_file = fopen(filename, "w");
if (!p->cap_file) {
syscall_fail("fopen", filename, 0);
free(p);
return -1;
}
p->cap_filename = x_strdup(filename);
p->bytes_max = maxsize * 1024;
/* Connect to the sender */
if (_dccnet_connect(p, addr, port, range, range_sz, lport)) {
fclose(p->cap_file);
free(p->cap_filename);
free(p);
return -1;
}
} else {
/* Do the connect first, because then that'll hopefully get a port,
which the listen socket can also use later anyway */
if (_dccnet_connect(p, addr, port, range, range_sz, lport)) {
free(p);
return -1;
}
/* Now listen, if this fails a bind() then thats fatal */
if (_dccnet_listen(p, range, range_sz, lport)) {
net_close(&(p->sender_sock));
free(p);
return -1;
}
}
p->notify_func = n_f;
p->notify_data = n_p;
if (n_msg)
p->notify_msg = x_strdup(n_msg);
p->next = proxies;
proxies = p;
timer_new((void *)p, "timeout", timeout, TIMER_FUNCTION(_dccnet_timedout), 0);
return 0;
}
/* Create socket to listen on */
static int _dccnet_listen(struct dccproxy *p, int *range, size_t range_sz,
int *port) {
int theport;
p->sendee_sock = net_socket();
if (p->sendee_sock == -1)
return -1;
if (_dccnet_bind(p->sendee_sock, range, range_sz, &theport)) {
net_close(&(p->sendee_sock));
return -1;
}
if (listen(p->sendee_sock, SOMAXCONN)) {
syscall_fail("listen", 0, 0);
net_close(&(p->sendee_sock));
return -1;
}
if (port)
*port = theport;
debug("Listening for DCC Sendees on port %d", theport);
p->sendee_status |= DCC_SENDEE_LISTENING;
net_hook(p->sendee_sock, SOCK_LISTENING, (void *)p,
ACTIVITY_FUNCTION(_dccnet_accept), 0);
return 0;
}
/* Connect to remote user */
static int _dccnet_connect(struct dccproxy *p, struct in_addr addr, int port,
int *range, size_t range_sz, int *bindport) {
int theport;
p->sender_addr.sin_family = AF_INET;
p->sender_addr.sin_addr.s_addr = htonl(addr.s_addr);
p->sender_addr.sin_port = htons(port);
debug("Connecting to DCC Sender %s:%d", inet_ntoa(p->sender_addr.sin_addr),
ntohs(p->sender_addr.sin_port));
p->sender_sock = net_socket();
if (p->sender_sock == -1)
return -1;
if (_dccnet_bind(p->sender_sock, range, range_sz, &theport)) {
debug("Connecting to DCC Sender from random port");
} else {
debug("Connecting to DCC Sender from port %d", theport);
if (bindport)
*bindport = theport;
}
if (connect(p->sender_sock, (struct sockaddr *)&(p->sender_addr),
sizeof(struct sockaddr_in)) && (errno != EINPROGRESS)) {
syscall_fail("connect", inet_ntoa(addr), 0);
net_close(&(p->sender_sock));
return -1;
}
p->sender_status |= DCC_SENDER_CREATED;
if (p->type & DCC_SEND) {
net_hook(p->sender_sock, SOCK_CONNECTING, (void *)p,
ACTIVITY_FUNCTION(dccsend_connected),
ERROR_FUNCTION(dccsend_connectfailed));
} else if (p->type & DCC_CHAT) {
net_hook(p->sender_sock, SOCK_CONNECTING, (void *)p,
ACTIVITY_FUNCTION(dccchat_connected),
ERROR_FUNCTION(dccchat_connectfailed));
}
return 0;
}
/* Bind a dcc socket to one from the allowed range */
static int _dccnet_bind(int sock, int *range, size_t range_sz, int *port) {
struct sockaddr_in local_addr;
int len;
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = INADDR_ANY;
if (range) {
int bound = 0;
size_t i;
int j;
for (i = 0; i < range_sz; i += 2) {
for (j = range[i]; j <= range[i + 1]; j++) {
debug("Trying to bind DCC to port %d", j);
local_addr.sin_port = htons(j);
if (!bind(sock, (struct sockaddr *)&local_addr,
sizeof(struct sockaddr_in))) {
bound = 1;
break;
}
}
if (bound)
break;
}
if (!bound) {
debug("No free ports to bind DCC to");
return -1;
}
} else {
debug("Binding DCC to random port");
local_addr.sin_port = 0;
if (bind(sock, (struct sockaddr *)&local_addr,
sizeof(struct sockaddr_in))) {
syscall_fail("bind", "dcc_listen", 0);
return -1;
}
}
len = sizeof(struct sockaddr_in);
if (getsockname(sock, (struct sockaddr *)&local_addr, &len)) {
syscall_fail("getsockname", 0, 0);
return -1;
}
if (port)
*port = ntohs(local_addr.sin_port);
return 0;
}
/* Timer hook to check if we've timed out */
static void _dccnet_timedout(struct dccproxy *p, void *data) {
if ((p->sender_status == DCC_SENDER_ACTIVE) && (p->type & DCC_SEND_CAPTURE)) {
debug("Capturing, and we've connected to sender");
return;
}
if (p->sendee_status != DCC_SENDEE_ACTIVE) {
if (p->type & DCC_CHAT) {
net_send(p->sender_sock, "--(%s)-- Timed out awaiting connection from "
"remote peer\n", PACKAGE);
} else if (p->type & DCC_SEND) {
if (p->notify_func)
p->notify_func(p->notify_data, p->notify_msg);
}
} else if (p->sender_status != DCC_SENDER_ACTIVE) {
if (p->type & DCC_CHAT) {
net_send(p->sendee_sock, "--(%s)-- Connection to remote peer timed out\n",
PACKAGE);
} else if (p->type & DCC_SEND) {
if (p->sender_status & DCC_SENDER_GONE) {
debug("Sender has come and gone, but sendee is connected");
return;
} else {
if (p->notify_func)
p->notify_func(p->notify_data, p->notify_msg);
}
}
} else {
debug("They are talking");
return;
}
p->dead = 1;
}
/* Accept a sendee connection */
static void _dccnet_accept(struct dccproxy *p, int sock) {
int newsock;
int len;
/* Accept the connection */
len = sizeof(struct sockaddr_in);
newsock = accept(sock, (struct sockaddr *)&(p->sendee_addr), &len);
if (newsock == -1) {
syscall_fail("accept", 0, 0);
p->dead = 1;
return;
}
/* Close the listening socket and make the new socket the sendee */
net_close(&(p->sendee_sock));
p->sendee_status &= ~(DCC_SENDEE_LISTENING);
p->sendee_sock = newsock;
net_create(&(p->sendee_sock));
if (p->sendee_sock != -1) {
p->sendee_status |= DCC_SENDEE_CONNECTED;
if (p->type & DCC_SEND) {
dccsend_accepted(p);
} else if (p->type & DCC_CHAT) {
dccchat_accepted(p);
}
debug("DCC Sendee connected from %s:%d",
inet_ntoa(p->sendee_addr.sin_addr),
ntohs(p->sendee_addr.sin_port));
}
}
/* Free a DCC proxy */
static void _dccnet_free(struct dccproxy *p) {
debug("Freeing DCC proxy");
if (p->sender_status & DCC_SENDER_CREATED)
net_close(&(p->sender_sock));
if (p->sendee_status & DCC_SENDEE_CREATED)
net_close(&(p->sendee_sock));
if (p->cap_filename) {
unlink(p->cap_filename);
free(p->cap_filename);
}
if (p->cap_file)
fclose(p->cap_file);
free(p->notify_msg);
free(p->buf);
dns_delall((void *)p);
timer_delall((void *)p);
free(p);
}
/* Get rid of any dead proxies */
int dccnet_expunge_proxies(void) {
struct dccproxy *p, *l;
l = 0;
p = proxies;
while (p) {
if (p->dead) {
struct dccproxy *n;
n = p->next;
_dccnet_free(p);
p = *(l ? &(l->next) : &(proxies)) = n;
} else {
l = p;
p = p->next;
}
}
return 0;
}
/* Delete all of the proxies */
void dccnet_flush(void) {
struct dccproxy *p;
p = proxies;
while (p) {
struct dccproxy *n;
n = p->next;
_dccnet_free(p);
p = n;
}
proxies = 0;
}
syntax highlighted by Code2HTML, v. 0.9.1