/* ARISA - Interface and DCC Send/Recv/Chat Functions * Copyright (C) 2003, 2004 Carl Ritson * * 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 of the License, 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #define WANT_POLL #define WANT_STATFS #include "arisa.h" #include "user.h" #include "sendfile.h" #include "line-drive.h" #include "dcc.h" #include #include #include #include #include #include #include #include /** Defines and Macros **/ #define POLL_ERRORS (POLLERR | POLLHUP | POLLNVAL) /** Typedefs and Enums **/ typedef enum cl_colour_t { CL_GREEN = 0x01, CL_YELLOW = 0x02, CL_RED = 0x04, CL_WHITE = 0x08 } cl_colour_t; typedef enum functype_t { CHECK_PREPOLL = 0, CHECK_POSTPOLL = 1, CONN_PREPOLL = 2, CONN_POSTPOLL = 3, NEGO_PREPOLL = 4, NEGO_POSTPOLL = 5, DELETE = 6, RUN_PREPOLL = 7, RUN_POSTPOLL = 8, RUN = 9, BANDWIDTH = 10, FUNC_MAX = 11 } functype_t; typedef enum call_t { DOWNWARD = 0, UPWARD = 1, INPLACE = 2 } call_t; typedef enum ns_t { NS_NOCHANGE = 0, NS_CONNECT = 1, NS_NEGOTIATE = 2, NS_RUN = 3, NS_DELETE = 4 } ns_t; typedef struct cl_t cl_t; typedef struct node_t node_t; typedef struct sok_t sok_t; typedef struct intf_t intf_t; typedef int (*func_t)(call_t, functype_t, intf_t *, node_t *); #define FUNC_TYPE(funcname) \ static int funcname(call_t ct, functype_t ft, intf_t *intf, node_t *n) /** Struct Definitions **/ struct cl_t { cl_colour_t colour; node_t *front; int size; }; struct node_t { ns_t state; cl_colour_t colour; uint32_t p_rate; /* present rate, for this time slice */ uint32_t c_rate; /* ceiling rate */ uint32_t a_rate; /* assured rate */ uint32_t max_rate; /* externally configured maximum speed */ uint32_t min_rate; /* externally configured minimum speed */ node_t *next; node_t *prev; /* sub nodes, in coloured lists */ cl_t *green; cl_t *yellow; cl_t *red; cl_t *white; func_t funcs[FUNC_MAX]; int last_ret; void *data; /* mostly shared */ int fd; int sok; int err; off_t ack; off_t pos; off_t size; short events; struct pollfd *poll; char *file; int complete; /* send - specific */ pool_t *pool; /* recv - specific */ time_t connected; // only for DCC SERVER /* chat - specific */ time_t time_limit; linebuf_t *snd_buf; linebuf_t *rcv_buf; }; struct sok_t { int port; int sok; int inuse; struct pollfd *poll; }; struct intf_t { interface_t *interface; interfacetype_t type; int mode; int end; int ticks_low; int ticks_high; unsigned short port; unsigned short no_ports; unsigned short dccserver_port; unsigned int dcc_timeout; int accept_per_sec; int secure_mode; off_t max_size; size_t token_size; size_t snd_bufsize; size_t rcv_bufsize; char *bind_ip; struct in_addr p_bind_ip; char *advertise_ip; struct in_addr p_advertise_ip; struct pollfd *fds; int no_fds; int size_fds; sok_t **sok_used; pqueue_t *sok_unused; int no_soks; int no_bound; char *buffer; size_t bufsize; node_t *root; }; /** allocators and deallocators **/ static cl_t *alloc_cl(cl_colour_t colour) { cl_t *c = xalloc(sizeof(cl_t)); c->colour = colour; c->front = NULL; c->size = 0; return c; } static void free_cl(cl_t *c) { xfree(c); } /** circular list manipulation functions **/ static inline int CL_ISEMPTY(cl_t *c) { if(c->size == 0) return 1; else return 0; } static inline node_t *CL_FRONT(cl_t *c) { return c->front; } static inline node_t *CL_BACK(cl_t *c) { if(c->front != NULL) return c->front->prev; else return NULL; } static inline void CL_FOREACH(cl_t *c, void (*func)(node_t *)) { node_t *p, *n = c->front; if(c->size == 0) return; do { p = n; n = n->next; func(p); } while(n != c->front); } static inline node_t *CL_POP(cl_t *c, node_t *n) { if(c->size == 0 || n == NULL) return NULL; n->prev->next = n->next; n->next->prev = n->prev; if(n == c->front) { if(c->size == 1) c->front = NULL; else c->front = n->next; } c->size--; return n; } #define CL_POP_BACK(c) CL_POP((c),CL_BACK((c))) #define CL_POP_FRONT(c) CL_POP((c),CL_FRONT((c))) static inline void CL_PUSH(cl_t *c, node_t *n, int front) { if(n == NULL) return; if(c->size == 0) { c->front = n; n->next = n; n->prev = n; } else { n->next = c->front; n->prev = c->front->prev; c->front->prev->next = n; c->front->prev = n; if(front) c->front = n; } n->colour = c->colour; c->size++; } #define CL_PUSH_FRONT(c,n) CL_PUSH((c),(n),1) #define CL_PUSH_BACK(c,n) CL_PUSH((c),(n),0) static int CL_IN_LIST(cl_t *c, node_t *node) { node_t *n = c->front; if(n != NULL) { do { if(n == node) return 1; } while(n != c->front); } return 0; } static int CL_DATA_IN_LIST(cl_t *c, void *data) { node_t *n = c->front; if(n != NULL) { do { if(n->data == data) return 1; n = n->next; } while(n != c->front); } return 0; } static inline node_t *CL_REMOVE_NODE(cl_t *c, node_t *n) { if(c->size > 1) { n->prev->next = n->next; n->next->prev = n->prev; if(c->front == n) c->front = n->next; } else c->front = NULL; c->size--; return n; } /** Rest of allocators and deallocators **/ static node_t *alloc_node(void) { node_t *n = xalloc(sizeof(node_t)); int i; n->state = NS_DELETE; // FIXME: is this a good idea? n->colour = CL_GREEN; n->p_rate = 0; n->c_rate = 0; n->a_rate = 0; n->max_rate = 0; n->min_rate = 0; n->next = n; n->prev = n; n->green = alloc_cl(CL_GREEN); n->yellow = alloc_cl(CL_YELLOW); n->red = alloc_cl(CL_RED); n->white = alloc_cl(CL_WHITE); for(i = 0; i < FUNC_MAX; ++i) n->funcs[i] = NULL; n->last_ret = 0; n->data = NULL; n->fd = -1; n->sok = -1; n->err = 0; n->ack = 0; n->pos = 0; n->size = 0; n->err = 0; n->events = 0; n->poll = NULL; n->file = NULL; n->complete = 0; n->pool = NULL; n->connected = 0; n->time_limit = 0; n->snd_buf = NULL; n->rcv_buf = NULL; return n; } static void free_node(intf_t *intf, node_t *n); // pre-declaration static void free_cl_and_nodes(intf_t *intf, cl_t *cl) { int i; if(cl == NULL) return; for(i = cl->size; i > 0; --i) { node_t *n = CL_POP_FRONT(cl); free_node(intf,n); } free_cl(cl); } static int run_func(call_t ct, functype_t ft, intf_t *intf, node_t *n); // pre-declaration static void close_socket(intf_t *intf, int *sokp); // pre-declaration static void close_fd(int *fdp); // pre-declaration static void free_node(intf_t *intf, node_t *n) { //D("intf: %p, n: %p",intf,n); run_func(INPLACE,DELETE,intf,n); free_cl_and_nodes(intf,n->green); free_cl_and_nodes(intf,n->yellow); free_cl_and_nodes(intf,n->red); free_cl_and_nodes(intf,n->white); if(n->fd != -1) close_fd(&(n->fd)); if(n->sok != -1) close_socket(intf,&(n->sok)); if(n->file != NULL) xfree(n->file); if(n->snd_buf != NULL) free_linebuf(n->snd_buf); if(n->rcv_buf != NULL) free_linebuf(n->rcv_buf); xfree(n); } static intf_t *alloc_intf(interfacetype_t type, interface_t *interface) { intf_t *intf = xalloc(sizeof(intf_t)); intf->interface = interface; intf->type = type; intf->mode = 0; intf->end = 0; intf->ticks_low = 4; intf->ticks_high = 8; intf->port = 0; intf->no_ports = 0; intf->dccserver_port = 0; intf->dcc_timeout = 180; intf->accept_per_sec = 1; intf->secure_mode = 0; intf->max_size = 0; intf->token_size = 1024; intf->snd_bufsize = 0; intf->rcv_bufsize = 0; intf->bind_ip = NULL; intf->advertise_ip = NULL; intf->fds = xalloc(sizeof(struct pollfd)*4); intf->no_fds = 0; intf->size_fds = 4; intf->sok_used = NULL; intf->sok_unused = NULL; intf->no_soks = 0; intf->no_bound = 0; intf->bufsize = 4096; intf->buffer = xalloc(intf->bufsize); intf->root = alloc_node(); intf->root->data = interface; return intf; } static void close_sok(int *sok); // pre-declaration static void free_intf(intf_t *intf) { if(intf->root != NULL) free_node(intf,intf->root); if(intf->bind_ip != NULL) xfree(intf->bind_ip); if(intf->advertise_ip != NULL) xfree(intf->advertise_ip); if(intf->fds != NULL) xfree(intf->fds); if(intf->sok_used != NULL) { int i; for(i = 0; i < intf->no_soks; ++i) { if(intf->sok_used[i] != NULL) { if(intf->sok_used[i]->sok != -1) close_sok(&(intf->sok_used[i]->sok)); xfree(intf->sok_used[i]); } } xfree(intf->sok_used); } if(intf->sok_unused != NULL) { sok_t *s; while((s = pqueue_pop_front(intf->sok_unused)) != NULL) { if(s->sok != -1) close_sok(&(s->sok)); xfree(s); } pqueue_deinit(intf->sok_unused,xfree,0); } if(intf->buffer != NULL) xfree(intf->buffer); xfree(intf); } /** Node Function Calling */ static int run_func(call_t ct, functype_t ft, intf_t *intf, node_t *n); // pre-declaration static void run_cl_func(call_t ct, functype_t ft, intf_t *intf, cl_t *cl) { node_t *n; int i,ret; if(cl == NULL) return; for(i = cl->size; i > 0; --i) { n = CL_POP_FRONT(cl); ret = run_func(ct,ft,intf,n); if(ret != NS_DELETE) { int tmp = n->colour; CL_PUSH_BACK(cl,n); n->colour = tmp; } else free_node(intf,n); } } static int make_call(call_t ct, functype_t ft, intf_t *intf, node_t *n) { func_t f = NULL; int ret; //D("%d %d %p %p",ct,ft,intf,n); switch (ft) { case BANDWIDTH: case CHECK_PREPOLL: case CHECK_POSTPOLL: case DELETE: f = n->funcs[ft]; break; case CONN_PREPOLL: case CONN_POSTPOLL: if(n->state == NS_CONNECT) f = n->funcs[ft]; break; case NEGO_PREPOLL: case NEGO_POSTPOLL: if(n->state == NS_NEGOTIATE) f = n->funcs[ft]; break; case RUN_PREPOLL: case RUN_POSTPOLL: case RUN: if(n->state == NS_RUN) f = n->funcs[ft]; break; default: assert(0); break; } if(f != NULL) { ret = f(ct,ft,intf,n); if(ret != NS_NOCHANGE) n->state = ret; return ret; } else return NS_NOCHANGE; } static int run_func(call_t ct, functype_t ft, intf_t *intf, node_t *n) { int ret = NS_NOCHANGE; //D("%d %d %p %p",ct,ft,intf,n); if(intf == NULL || n == NULL) return NS_NOCHANGE; if(ct == INPLACE || ct == DOWNWARD) { ret = make_call(ct,ft,intf,n); if(ret == NS_DELETE) return ret; } if(ct != INPLACE) { run_cl_func(ct,ft,intf,n->green); run_cl_func(ct,ft,intf,n->yellow); run_cl_func(ct,ft,intf,n->red); run_cl_func(ct,ft,intf,n->white); } if(ct == UPWARD) ret = make_call(ct,ft,intf,n); return ret; } static inline int N_RUN(intf_t *intf, node_t *n) { make_call(INPLACE,RUN,intf,n); return n->last_ret; } static inline void set_func(node_t *node, functype_t type, func_t func) { node->funcs[type] = func; } /** Logging Functions **/ static char *hostport_str(struct sockaddr_in *addr, char *buffer, size_t bufsize) { char addrbuf[64]; inet_ntop(AF_INET,&(addr->sin_addr),addrbuf,sizeof(addrbuf)); snprintf(buffer,bufsize,"%s:%u",addrbuf,ntohs(addr->sin_port)); return buffer; } static char *peer_hostport_str(int sok, char *buffer, size_t bufsize) { struct sockaddr_in addr; socklen_t len = sizeof(addr); getpeername(sok,(struct sockaddr *)&addr,&len); return hostport_str(&addr,buffer,bufsize); } static char *sock_hostport_str(int sok, char *buffer, size_t bufsize) { struct sockaddr_in addr; socklen_t len = sizeof(addr); getsockname(sok,(struct sockaddr *)&addr,&len); return hostport_str(&addr,buffer,bufsize); } static char *netnick_str(intf_t *intf, node_t *n, char *buffer, size_t bufsize) { char netname[64],nick[64]; network_t *net = NULL; netname[0] = '\0'; nick[0] = '\0'; if(intf->type == INTERFACE_SEND || intf->type == INTERFACE_RECV) { send_t *s = (send_t *)n->data; LOCK(s); if(s->nick != NULL) xstrncpy(nick,s->nick,sizeof(nick)); net = s->network; UNLOCK(s); } else if(intf->type == INTERFACE_CHAT) { chat_t *c = (chat_t *)n->data; LOCK(c); if(c->nick != NULL) xstrncpy(nick,c->nick,sizeof(nick)); net = c->network; UNLOCK(c); } if(net != NULL) { LOCK(net); xstrncpy(netname,net->name,sizeof(netname)); UNLOCK(net); } snprintf(buffer,bufsize,"%s%s%s", netname, netname[0] != '\0' ? ":" : "", nick); return buffer; } static char *file_str(intf_t *intf, node_t *n, char *buffer, size_t bufsize) { if(intf->type == INTERFACE_SEND || intf->type == INTERFACE_RECV) { send_t *s = (send_t *)n->data; xstrncpy(buffer,"FILE",bufsize); LOCK(s); if(s->pack != NULL) { RLOCK(s->pack); if(s->pack->file != NULL) xstrncpy(buffer,s->pack->file,bufsize); RUNLOCK(s->pack); } UNLOCK(s); } else if(intf->type == INTERFACE_CHAT) { xstrncpy(buffer,"CHAT",bufsize); } else { xstrncpy(buffer,"UNKNOWN",bufsize); } return buffer; } static void log_unverifiable(intf_t *intf, node_t *n, struct sockaddr_in *addr) { char hp[64],nn[128],fs[256]; LOGTP(L_INT,"Unable to verify that connection from (%s) as [%s] for {\"%s\"}, closing.", hostport_str(addr,hp,sizeof(hp)), netnick_str(intf,n,nn,sizeof(nn)), file_str(intf,n,fs,sizeof(fs))); } static void log_verified(intf_t *intf, node_t *n, struct sockaddr_in *addr) { char hp[64],nn[128]; LOGTP(L_INT,"Verified connection from (%s) for [%s].", hostport_str(addr,hp,sizeof(hp)), netnick_str(intf,n,nn,sizeof(nn))); } static void log_unverified(intf_t *intf, node_t *n, struct sockaddr_in *addr) { char hp[64],nn[128],fs[256]; LOGTP(L_INT,"Accepted unverified connection from (%s) as [%s] for {\"%s\"}.", hostport_str(addr,hp,sizeof(hp)), netnick_str(intf,n,nn,sizeof(nn)), file_str(intf,n,fs,sizeof(fs))); } static void log_connect_attempt(intf_t *intf, node_t *n, struct sockaddr_in *addr) { char hp[64],nn[128],fs[256]; LOGTP(L_INT,"Attempting to connect to (%s) [%s] for {\"%s\"}.", hostport_str(addr,hp,sizeof(hp)), netnick_str(intf,n,nn,sizeof(nn)), file_str(intf,n,fs,sizeof(fs))); } static void log_upload_active(intf_t *intf, node_t *n) { char hs[64],ps[64]; LOGTP(L_INT,"Connection Established (%s) <-> (%s) for upload of {\"%s\",%lld}.", sock_hostport_str(n->sok,hs,sizeof(hs)), peer_hostport_str(n->sok,ps,sizeof(ps)), n->file,n->pos); } static void log_dccserv_upload(intf_t *intf, node_t *n) { char ps[64]; LOGTP(L_INT,"Connection from (%s) to DCC Server port negotiated as upload.", peer_hostport_str(n->sok,ps,sizeof(ps))); } static void log_dccserv_close(intf_t *intf, node_t *n) { char ps[64],errbuf[96]; if(n->err == 0) { LOGTP(L_INT,"Closing failed connection attempt from (%s) to DCC Server port.", peer_hostport_str(n->sok,ps,sizeof(ps))); } else { LOGTP(L_INT,"Closing failed connection attempt from (%s) to DCC Server port, Error: %s.", peer_hostport_str(n->sok,ps,sizeof(ps)), lstrerror_r(n->err,errbuf,sizeof(errbuf))); } } static void log_dccserv_incoming(intf_t *intf, node_t *n) { char ps[64]; LOGTP(L_INT,"Received connection from (%s) to DCC Server port.", peer_hostport_str(n->sok,ps,sizeof(ps))); } static void log_connected(intf_t *intf, node_t *n) { char hs[64],ps[64],nn[128]; LOGTP(L_INT,"Connection Established (%s) <-> (%s) as [%s] for {\"%s\",%lld}.", sock_hostport_str(n->sok,hs,sizeof(hs)), peer_hostport_str(n->sok,ps,sizeof(ps)), netnick_str(intf,n,nn,sizeof(nn)), n->file != NULL ? n->file : "CHAT",n->pos); } /** Socket Functions **/ static struct pollfd *poll_add(intf_t *intf, int sok, int events) { int no = intf->no_fds; //D("intf: %p, sok: %d, events: %x",intf,sok,events); if(intf->no_fds == intf->size_fds) { intf->size_fds += 4; intf->fds = xreloc(intf->fds, intf->size_fds * sizeof(struct pollfd)); } intf->no_fds++; intf->fds[no].fd = sok; intf->fds[no].events = events; intf->fds[no].revents = 0; return &(intf->fds[no]); } static void poll_wipe(intf_t *intf) { intf->no_fds = 0; } static int set_nonblocking(int sok) { return fcntl(sok,F_SETFL,O_NONBLOCK); } static int alloc_socket(struct sockaddr_in *addr) { char errbuf[96], addrbuf[64]; int sok,ret; sok = socket(PF_INET, SOCK_STREAM, 0); if(sok == -1) { E(errno); LOGTP(L_ERR,"Failed to allocate socket: %s", lstrerror_r(errno,errbuf,sizeof(errbuf))); return -1; } #ifdef SO_REUSEADDR { int tmp = 1; setsockopt(sok,SOL_SOCKET,SO_REUSEADDR,&tmp,sizeof(tmp)); } #endif if(addr != NULL) { ret = bind(sok,(struct sockaddr *)addr, sizeof(struct sockaddr_in)); if(ret == -1) { E(errno); LOGTP(L_ERR,"Failed to bind socket to (%s): %s", hostport_str(addr,addrbuf,sizeof(addrbuf)), lstrerror_r(errno,errbuf,sizeof(errbuf))); close_sok(&sok); return -1; } } ret = set_nonblocking(sok); if(ret == -1) { E(errno); LOGTP(L_ERR,"Failed to set socket (%s) non-blocking: %s", sock_hostport_str(sok,addrbuf,sizeof(addrbuf)), lstrerror_r(errno,errbuf,sizeof(errbuf))); close_sok(&sok); return -1; } return sok; } static int get_socket(intf_t *intf) { struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(0); if(intf->bind_ip != NULL) { memcpy(&(addr.sin_addr),&(intf->p_bind_ip), sizeof(struct in_addr)); } else { addr.sin_addr.s_addr = INADDR_ANY; } return alloc_socket(&addr); } static int get_listening_socket(intf_t *intf) { if(intf->port != 0) { sok_t *sok = NULL; int i; sok = pqueue_pop_front(intf->sok_unused); if(sok == NULL) { errno = EAGAIN; return -1; } //D("intf->no_soks = %d",intf->no_soks); for(i = 0; i < intf->no_soks; ++i) { //D("intf->sok_used[%d] = %p", // i,intf->sok_used[i]); if(intf->sok_used[i] == NULL) { intf->sok_used[i] = sok; break; } } sok->inuse = 1; return sok->sok; } else { int ret, sok = get_socket(intf); if(sok != -1) { ret = listen(sok,1); if(ret == -1) { char addrbuf[64],errbuf[96]; E(errno); LOGTP(L_ERR,"Failed to listen on (%s): %s", sock_hostport_str(sok,addrbuf, sizeof(addrbuf)), lstrerror_r(errno,errbuf, sizeof(errbuf))); close_sok(&sok); return -1; } } return sok; } } static void close_sok(int *sok); // pre-declaration static void close_socket(intf_t *intf, int *sokp) { if(intf->port == 0 || intf->no_soks == 0) { close_sok(sokp); } else { sok_t *sok = NULL; int i; for(i = 0; i < intf->no_soks && sok == NULL; ++i) { if(intf->sok_used[i] != NULL) { //D("intf->sok_used[%d]->sok = %u, *sokp = %u", // i,intf->sok_used[i]->sok,*sokp); if(intf->sok_used[i]->sok == *sokp) { sok = intf->sok_used[i]; intf->sok_used[i] = NULL; } } } if(sok != NULL) { sok->inuse = 0; pqueue_push_back(intf->sok_unused,sok); } else { close_sok(sokp); } } *sokp = -1; } static int accept_socket(int sok, struct sockaddr *addr, socklen_t *len) { int ret, asok; asok = accept(sok,addr,len); if(asok == -1) return -1; ret = set_nonblocking(asok); if(ret == -1) { char addrbuf[64],errbuf[96]; E(errno); LOGTP(L_ERR,"Failed to set socket (%s) non-blocking: %s", sock_hostport_str(asok,addrbuf,sizeof(addrbuf)), lstrerror_r(errno,errbuf,sizeof(errbuf))); close_sok(&asok); return -1; } return asok; } static int socket_error(int sok) { socklen_t len; int ret, val; len = sizeof(val); ret = getsockopt(sok,SOL_SOCKET,SO_ERROR,&val,&len); return ret != -1 ? val : errno; } static int set_sndbuf(intf_t *intf, int sok) { int size = (int) intf->snd_bufsize; return setsockopt(sok,SOL_SOCKET,SO_SNDBUF,&size,sizeof(int)); } static int set_rcvbuf(intf_t *intf, int sok) { int size = (int) intf->rcv_bufsize; return setsockopt(sok,SOL_SOCKET,SO_RCVBUF,&size,sizeof(int)); } static void close_sok(int *sok) { shutdown(*sok,SHUT_RDWR); close(*sok); //D("close: %d",*sok); *sok = -1; } static void close_fd(int *fd) { close(*fd); //D("close: %d",*fd); *fd = -1; } static int do_connect(intf_t *intf, node_t *n, struct sockaddr_in *addr) { int ret; n->sok = get_socket(intf); if(n->sok == -1 && errno == EAGAIN) return 0; else if(n->sok == -1) { n->err = errno; E(n->err); return -1; } log_connect_attempt(intf,n,addr); ret = connect(n->sok,(struct sockaddr *)addr, sizeof(struct sockaddr_in)); if(ret == -1 && errno != EINPROGRESS) { n->err = errno; E(n->err); return -1; } return 1; } /** Settings **/ static int set_bind_ip(intf_t *intf, const char *host) { struct in_addr *addrs; int no_addrs; intf->p_bind_ip.s_addr = INADDR_ANY; no_addrs = aresolv_lookup(global->aresolver,host,&addrs); if(no_addrs > 0) { if(intf->bind_ip != NULL) xfree(intf->bind_ip); intf->bind_ip = xstrdup(host); memcpy(&(intf->p_bind_ip), &(addrs[0]),sizeof(struct in_addr)); xfree(addrs); return 0; } else return -1; } static int set_advertise_ip(intf_t *intf, const char *host) { struct in_addr *addrs; int no_addrs; intf->p_advertise_ip.s_addr = INADDR_ANY; no_addrs = aresolv_lookup(global->aresolver,host,&addrs); if(no_addrs > 0) { if(intf->advertise_ip != NULL) xfree(intf->advertise_ip); intf->advertise_ip = xstrdup(host); memcpy(&(intf->p_advertise_ip), &(addrs[0]),sizeof(struct in_addr)); xfree(addrs); return 0; } else return -1; } static void intf_rehash_ports(intf_t *intf) { struct sockaddr_in addr; socklen_t len; int i,j,found,used; pqueue_t list, to_keep; sok_t *s; pqueue_init(&list,0); pqueue_init(&to_keep,0); if(intf->sok_used != NULL) { for(i = 0; i < intf->no_soks; ++i) { if(intf->sok_used[i] != NULL) pqueue_push_back(&list,intf->sok_used[i]); } } if(intf->sok_unused != NULL) { while((s = pqueue_pop_front(intf->sok_unused)) != NULL) pqueue_push_back(&list,s); } while((s = pqueue_pop_front(&list)) != NULL) { if(intf->mode == IMODE_NORMAL && s->port >= intf->port && (s->port < (intf->port + intf->no_ports))) { len = sizeof(addr); getsockname(s->sok,(struct sockaddr *)&addr,&len); if(addr.sin_addr.s_addr == INADDR_ANY && intf->bind_ip == NULL) { pqueue_push_back(&to_keep,s); continue; } else if(memcmp(&(addr.sin_addr), &(intf->p_bind_ip), sizeof(struct in_addr)) == 0) { pqueue_push_back(&to_keep,s); continue; } } if(!s->inuse) close_sok(&(s->sok)); xfree(s); } if(intf->sok_used != NULL && (intf->no_ports == 0 || intf->mode != IMODE_NORMAL)) { xfree(intf->sok_used); intf->sok_used = NULL; } if(intf->sok_unused != NULL && (intf->no_ports == 0 || intf->mode != IMODE_NORMAL)) { pqueue_deinit(intf->sok_unused,xfree,0); intf->sok_unused = NULL; } if(intf->no_ports == 0 || intf->mode != IMODE_NORMAL) { intf->no_soks = 0; intf->no_bound = 0; return; } intf->no_bound = 0; intf->no_soks = intf->no_ports; intf->sok_used = xreloc(intf->sok_used, sizeof(sok_t *) * intf->no_soks); for(i = 0, used = -1; i < intf->no_soks; ++i) { intf->sok_used[i] = pqueue_pop_front(&to_keep); if(used == -1 && intf->sok_used[i] == NULL) used = i; } if(used == -1) used = intf->no_soks; if(intf->sok_unused == NULL) intf->sok_unused = pqueue_init(NULL,0); for(i = intf->port; i < (intf->port + intf->no_ports); ++i) { for(j = 0, found = 0; j < used && !found; ++j) { //D("%d == %d",intf->sok_used[j]->port,i); if(intf->sok_used[j]->port == i) found = 1; } if(!found) { sok_t *s = xalloc(sizeof(sok_t)); int ret; //D("i: %d",i); addr.sin_family = AF_INET; if(intf->bind_ip != NULL) memcpy(&(addr.sin_addr),&(intf->p_bind_ip), sizeof(struct in_addr)); else addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons((unsigned short)i); s->sok = alloc_socket(&addr); if(s->sok == -1) { char buffer[64]; inet_ntop(AF_INET,&(addr.sin_addr), buffer,sizeof(buffer)); LOGTP(L_ERR,"Failed to bind (%s:%d)",buffer,i); xfree(s); continue; } ret = listen(s->sok,1); if(ret == -1) { char buffer[64]; inet_ntop(AF_INET,&(addr.sin_addr), buffer,sizeof(buffer)); LOGTP(L_ERR,"Failed to listen on (%s:%d)", buffer,i); close_sok(&(s->sok)); xfree(s); continue; } s->port = i; s->inuse = 0; s->poll = NULL; intf->sok_used[used++] = s; intf->no_bound++; } else intf->no_bound++; } for(i = 0; i < intf->no_soks; ++i) { if(intf->sok_used[i] != NULL) { if(intf->sok_used[i]->inuse == 0) { pqueue_push_back(intf->sok_unused, intf->sok_used[i]); intf->sok_used[i] = NULL; } } } } static void recv_rehash_ports(intf_t *intf) { struct sockaddr_in addr; socklen_t len; int ret; if(intf->root->sok != -1) { len = sizeof(addr); getsockname(intf->root->sok, (struct sockaddr *)&addr,&len); if(ntohs(addr.sin_port) != intf->dccserver_port || memcmp(&(intf->p_bind_ip),&(addr.sin_addr),sizeof(struct in_addr)) != 0) close_sok(&(intf->root->sok)); else return; } if(intf->dccserver_port == 0) return; addr.sin_family = AF_INET; if(intf->bind_ip != NULL) memcpy(&(addr.sin_addr),&(intf->p_bind_ip), sizeof(struct in_addr)); else addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(intf->dccserver_port); ret = alloc_socket(&addr); if(ret == -1) { char buffer[64]; inet_ntop(AF_INET,&(addr.sin_addr),buffer,sizeof(buffer)); LOGTP(L_ERR,"Failed to allocate DCC SERVER socket (%s:%d)", buffer,intf->dccserver_port); return; } else intf->root->sok = ret; ret = listen(intf->root->sok,5); if(ret == -1) { char buffer[64]; inet_ntop(AF_INET,&(addr.sin_addr),buffer,sizeof(buffer)); LOGTP(L_ERR,"Failed to listen on DCC Server socket (%s:%d)", buffer,intf->dccserver_port); close_sok(&(intf->root->sok)); } } static void load_interface_settings(intf_t *intf) { int rehash_ports = 0; intf->snd_bufsize = intf->interface->snd_bufsize * 1024L; if(intf->snd_bufsize == 0) intf->snd_bufsize = max_sokbuf(0); intf->rcv_bufsize = intf->interface->rcv_bufsize * 1024L; if(intf->rcv_bufsize == 0) intf->rcv_bufsize = max_sokbuf(1); LOCK(intf->interface); intf->mode = intf->interface->mode; if(intf->port != intf->interface->dcc_port) { intf->port = intf->interface->dcc_port; rehash_ports = 1; } if(intf->no_ports != intf->interface->no_ports) { intf->no_ports = intf->interface->no_ports; rehash_ports = 1; } if(intf->dccserver_port != intf->interface->dccserver_port) { intf->dccserver_port = intf->interface->dccserver_port; rehash_ports = 1; } intf->root->c_rate = intf->interface->bandwidth * 1024L; intf->dcc_timeout = intf->interface->dcc_timeout; intf->token_size = intf->interface->dcc_packet; intf->accept_per_sec = intf->interface->accept_per_sec; intf->secure_mode = intf->interface->secure_mode; if(intf->type == INTERFACE_RECV) intf->max_size = intf->interface->recv.max_size; if(intf->bind_ip == NULL && intf->interface->bind_ip != NULL) { if(set_bind_ip(intf,intf->interface->bind_ip) == 0) rehash_ports = 1; } else if(intf->bind_ip != NULL && intf->interface->bind_ip == NULL) { xfree(intf->bind_ip); intf->bind_ip = NULL; } else if(intf->bind_ip == NULL && intf->interface->bind_ip == NULL) { // NOP } else if(strcasecmp(intf->bind_ip,intf->interface->bind_ip) != 0) { if(set_bind_ip(intf,intf->interface->bind_ip) == 0) rehash_ports = 1; } if(intf->advertise_ip == NULL && intf->interface->advertise_ip != NULL) { set_advertise_ip(intf,intf->interface->advertise_ip); } else if(intf->advertise_ip != NULL && intf->interface->advertise_ip == NULL) { xfree(intf->advertise_ip); intf->advertise_ip = NULL; } else if(intf->advertise_ip == NULL && intf->interface->advertise_ip == NULL) { } else if(strcasecmp(intf->advertise_ip, intf->interface->advertise_ip) != 0) { set_advertise_ip(intf,intf->interface->advertise_ip); } UNLOCK(intf->interface); if(rehash_ports //|| (intf->no_soks != intf->no_bound)) { || (intf->no_soks != 0 && intf->no_bound == 0)) { switch (intf->type) { case INTERFACE_RECV: recv_rehash_ports(intf); case INTERFACE_SEND: case INTERFACE_CHAT: intf_rehash_ports(intf); break; } } } /** Misc **/ /* Assumes that the interface is locked */ static char *calculate_fn(intf_t *intf, const char *file) { size_t fn_size = strlen(file); char *fn; if(intf->interface->recv.dir != NULL) fn_size += strlen(intf->interface->recv.dir); else fn_size += 1; fn_size += 2; // seperator and terminator fn = xalloc(fn_size); snprintf(fn,fn_size,"%s/%s", intf->interface->recv.dir != NULL ? intf->interface->recv.dir : ".", file); return fn; } static void calculate_resume(intf_t *intf, node_t *n) { struct stat sbuf; send_t *r = (send_t *)n->data; int ret; ret = stat(n->file,&sbuf); if(ret == 0) { n->pos = sbuf.st_size; } else // assume that all failures of stat mean the file doesn't exist n->pos = 0; if(r != NULL) { LOCK(r); r->pos = n->pos; UNLOCK(r); } } static int valid_file_size(intf_t *intf, off_t size) { int ret = 0; LOCK(intf->interface); if(intf->interface->recv.max_size == 0 || size <= intf->interface->recv.max_size) ret = 1; UNLOCK(intf->interface); return ret; } static void stat_update(uint32_t *mib, uint32_t *carry, const uint32_t add) { uint32_t i; (*carry) += add; i = (*carry) / (1024UL * 1024UL); (*mib) += i; (*carry) -= i * 1024UL * 1024UL; } static void update_bandwidth_stats(intf_t *intf) { interface_t *i = intf->interface; node_t *n = intf->root; time_t now = xtime(); LOCK(i); stat_update(&i->stat_mib,&i->stat_carry,n->p_rate); i->avgspeed[now % AVGSPEED] = n->p_rate; if(n->p_rate > i->record_rate) i->record_rate = n->p_rate; if(n->c_rate != 0 && n->p_rate > n->c_rate) n->c_rate -= (n->p_rate - n->c_rate); if(i->bandwidth != 0 && n->c_rate <= 0) n->c_rate = (i->bandwidth * 256L); n->p_rate = 0; n->a_rate = 0; UNLOCK(i); } /** Node Running Functions **/ static inline enum cl_colour_t place_by_rate(node_t *n, node_t *p) { //D("n: %p, p: %p, p->c_rate: %ld, p->a_rate: %ld, p->p_rate: %ld", // n,p,p->c_rate,p->a_rate,p->p_rate); if(p->c_rate != 0 && p->p_rate >= p->c_rate) { //D("red"); CL_PUSH_BACK(n->red,p); return CL_RED; } else if(p->a_rate != 0 && p->p_rate >= p->a_rate && n->yellow != NULL) { //D("yellow"); CL_PUSH_BACK(n->yellow,p); return CL_YELLOW; } else { //D("green"); CL_PUSH_BACK(n->green,p); return CL_GREEN; } } FUNC_TYPE(colour_run) { uint32_t tmp; node_t *p; int i,ret; //D("n: %p",n); for(i = n->green->size; i > 0; --i) { p = CL_POP_FRONT(n->green); tmp = p->p_rate; ret = N_RUN(intf,p); if(ret == -1) CL_PUSH_BACK(n->red,p); else if(ret == 0) CL_PUSH_BACK(n->white,p); else { n->p_rate += p->p_rate - tmp; place_by_rate(n,p); n->last_ret = 1; return NS_NOCHANGE; } } for(i = n->yellow->size; i > 0; --i) { p = CL_POP_FRONT(n->yellow); tmp = p->p_rate; ret = N_RUN(intf,p); if(ret == -1) CL_PUSH_BACK(n->red,p); else if(ret == 0) CL_PUSH_BACK(n->white,p); else { n->p_rate += p->p_rate - tmp; place_by_rate(n,p); n->last_ret = 1; return NS_NOCHANGE; } } n->last_ret = 0; return NS_NOCHANGE; } static inline int need_acks(intf_t *intf, node_t *n) { return (n->ack + (off_t)(intf->snd_bufsize * 2) < n->pos || (n->ack < n->pos && ((n->size - n->pos) < (off_t)(intf->snd_bufsize * 2)))); } FUNC_TYPE(send_run) { uint32_t ackpos = 0; ssize_t sret; int ret; while((ret = recv(n->sok,&ackpos,sizeof(ackpos),0)) == sizeof(ackpos)) { ackpos = ntohl(ackpos); if(n->size > (off_t)UINT32_MAX) { // we need 64bit DCC off_t ack64 = ((n->ack / (off_t)UINT32_MAX) * (off_t)UINT32_MAX) + ackpos; if(ack64 < n->ack) { ack64 += UINT32_MAX - ((n->ack / (off_t)UINT32_MAX) * (off_t)UINT32_MAX); } if(ack64 <= n->ack || ack64 > n->pos) { // 64bit n->err = ESPIPE; goto out; } else // 64bit n->ack = ack64; } else if(ackpos <= n->ack || ackpos > n->pos) { // 32bit // something weird! n->err = ESPIPE; goto out; } else // 32bit n->ack = (off_t) ackpos; } if(errno != 0 && errno != EAGAIN) { n->err = errno; goto out; } if(need_acks(intf,n)) { // this implements DCC FAST send of a kind switches to // RFC dcc in the last part of the file if(n->last_ret == 0 && (n->events & POLLIN) && ackpos == 0) // anti-spin n->events = 0; else n->events = POLLIN; n->last_ret = 0; } else if (n->pos < n->size) { //D("sok:%d, fd:%d, token_size:%d",n->sok,n->fd,n->token_size); sret = l_sendfile(n->sok,n->fd,&(n->pos),intf->token_size); //D("sret: %ld",sret); if(sret <= 0 && errno != EAGAIN) { n->err = errno; goto out; } else if(sret <= 0 && errno == EAGAIN) { if(n->events == POLLOUT && n->last_ret == 0) { /* check we didn't hit the end of the file */ off_t eofpos = lseek(n->fd,0,SEEK_END); lseek(n->fd,n->pos,SEEK_SET); if(n->pos == eofpos || eofpos == (off_t)-1) { n->err = EIO; goto out; } else n->events = POLLOUT; } else n->events = POLLOUT; n->last_ret = 0; } else { n->p_rate += sret; n->last_ret = 1; } } else if(n->ack < n->pos) { // end of file // shutdown(n->sok,SHUT_WR); // FIXME: ponder this one! D("ack: %lld, pos: %lld",n->ack,n->pos); n->events = POLLIN; n->last_ret = 0; } else { n->last_ret = -1; } out: if(n->err != 0) { E(n->err); n->last_ret = -1; } return NS_NOCHANGE; } FUNC_TYPE(recv_run) { uint32_t ackpos; ssize_t sret; int ret = 1; if(n->ack < n->pos) { ackpos = htonl((uint32_t)n->pos); sret = send(n->sok,&ackpos,sizeof(ackpos),0); if(sret <= 0) { if(errno == EAGAIN) { n->events = POLLOUT; ret = 0; } else { n->err = errno; ret = -1; } } else n->ack = n->pos; } if(ret == 1) { sret = recv(n->sok, intf->buffer, intf->bufsize, 0); if(sret > 0) { n->p_rate += sret; sret = write(n->fd, intf->buffer, sret); if(sret > 0) { n->pos += sret; ackpos = htonl((uint32_t)n->pos); sret = send(n->sok,&ackpos,sizeof(ackpos),0); if(sret > 0) { n->ack = n->pos; n->events = POLLIN; } else if(errno == EAGAIN) { n->events = POLLOUT; ret = 0; } else { n->err = errno; ret = -1; } } else { n->err = errno; ret = -1; } } else if(errno == EAGAIN) { if(n->last_ret != 0 && n->events == POLLIN) // anti-spin n->events = POLLIN; else n->events = 0; ret = 0; } else { n->err = errno; ret = -1; } } n->last_ret = ret; return NS_NOCHANGE; } FUNC_TYPE(chat_run) { chat_t *c = (chat_t *)n->data; size_t len,rem = 512; char *ptr; int tmp, ret; /* Lack of locking the chat is intentional, access to the message * queue is locked by the pqueue functions so this should all be ok. */ if((n->events & POLLOUT) && n->complete != 2) { while(rem > 0) { if(!linebuf_inuse(n->snd_buf)) { if(pqueue_length(&c->msgqueue) <= 0) { if(n->complete == 1) { n->complete = 2; n->events = 0; shutdown(n->sok,SHUT_RDWR); } else n->events = POLLIN; break; } ptr = pqueue_pop_front(&(c->msgqueue)); len = strlen(ptr); if(len > 510) len = 510; if(len > (rem-2)) { pqueue_push_front((&c->msgqueue),ptr); break; } load_linebuf(n->snd_buf,ptr); rem -= len; } ret = send_line(n->sok,n->snd_buf); if(ret == -1) { if(errno != EAGAIN && errno != EINTR) { n->err = errno; E(n->err); } break; } else if(ret == 0) break; } if(n->err != 0) { n->last_ret = -1; } else if(rem != 512) { n->p_rate += 512 - rem; n->last_ret = 1; } else { n->last_ret = 0; } } else if(n->complete == 0) { ret = recv_line(n->sok,n->rcv_buf); if(ret > 0) { char buffer[514]; size_t len; len = copy_linebuf(n->rcv_buf,buffer,sizeof(buffer)); n->p_rate += len; tmp = chat_list_cmd(c,buffer); if(tmp == -1) { n->complete = 2; shutdown(n->sok,SHUT_RDWR); n->events = 0; n->last_ret = -1; } else if(tmp == 1) { n->complete = 1; shutdown(n->sok,SHUT_RD); n->events = POLLOUT; n->last_ret = 1; } else if(pqueue_length(&c->msgqueue) > 0) { n->events = POLLOUT; n->last_ret = 1; } } else if(ret == -1 && errno != EAGAIN) { n->err = errno; E(n->err); n->last_ret = -1; } else { if(!(n->last_ret == 0 && n->events == POLLIN)) n->events = POLLIN; else n->events = 0; // anti-spin n->last_ret = 0; } } else { // complete = 2 n->events = 0; n->last_ret = -1; } return NS_NOCHANGE; } FUNC_TYPE(common_run_prepoll) { node_t *pn; if((pn = CL_FRONT(n->white)) != NULL) { do { if(pn->state == NS_RUN && pn->sok >= 0) pn->poll = poll_add(intf,pn->sok,pn->events); pn = pn->next; } while(pn != CL_FRONT(n->white)); } return NS_NOCHANGE; } FUNC_TYPE(common_run_postpoll) { node_t *pn; int i; //D("n: %p, n->state: %d",n,n->state); for(i = n->white->size; i > 0; --i) { pn = CL_POP_FRONT(n->white); if(pn->state == NS_RUN && pn->sok >= 0) { if(pn->poll->revents != 0) place_by_rate(n,pn); else CL_PUSH_BACK(n->white,pn); } else if(pn->state == NS_RUN) { if(pn->green->size > 0 || pn->yellow->size > 0) place_by_rate(n,pn); else if(pn->white->size > 0) CL_PUSH_BACK(n->white,pn); else CL_PUSH_BACK(n->red,pn); } else CL_PUSH_BACK(n->white,pn); } /* D("green: %d, yellow: %d, red: %d, white: %d", n->green->size, n->yellow->size, n->red->size, n->white->size); */ return NS_NOCHANGE; } /** Connect Functions **/ FUNC_TYPE(dcc_listen_prepoll) { struct sockaddr_in addr; socklen_t len = sizeof(addr); if(n->complete == 0) { struct in_addr *adv = NULL; n->sok = get_listening_socket(intf); if(n->sok == -1 && errno == EAGAIN) { return NS_NOCHANGE; } else if(n->sok == -1) { n->err = errno; E(n->err); return NS_DELETE; } if(intf->advertise_ip != NULL) adv = &(intf->p_advertise_ip); getsockname(n->sok,(struct sockaddr *)&addr,&len); if(intf->type == INTERFACE_SEND) dcc_send_msg(adv,(send_t *)n->data, ntohs(addr.sin_port)); else if(intf->type == INTERFACE_RECV) dcc_rsend_accept_msg(adv,(send_t *)n->data, ntohs(addr.sin_port)); else if(intf->type == INTERFACE_CHAT) dcc_chat_msg(adv,(chat_t *)n->data, ntohs(addr.sin_port)); n->complete = 1; n->poll = poll_add(intf,n->sok,POLLIN); return NS_NOCHANGE; } else if(n->complete == 1) { n->poll = poll_add(intf,n->sok,POLLIN); return NS_NOCHANGE; } else if(n->complete == 2) { char host[256],**hosts = NULL; int i = 0,ret = 0,found = 0; host[0] = '\0'; getpeername(n->fd,(struct sockaddr *)&addr,&len); ret = aresolv_lookup_reverse(global->aresolver, &(addr.sin_addr),&hosts); D("ret: %d",ret); if(ret == 0) return NS_NOCHANGE; if(intf->type == INTERFACE_SEND || intf->type == INTERFACE_RECV) { send_t *s = (send_t *)n->data; if(s->host != NULL) xstrncpy(host,s->host,sizeof(host)); } else if(intf->type == INTERFACE_CHAT) { chat_t *c = (chat_t *)n->data; if(c->host != NULL) xstrncpy(host,c->host,sizeof(host)); } if(host[0] == '\0') { xfree(hosts); return NS_NOCHANGE; } if(ret > 0) { for(i = 0; i < ret && !found; ++i) { if(strcasecmp(hosts[i],host) == 0) found = 1; } } if(!found) { char addrbuf[64]; inet_ntop(AF_INET,&(addr.sin_addr),addrbuf,sizeof(addrbuf)); if(strcasecmp(addrbuf,host) == 0) found = 1; } if(hosts != NULL) xfree(hosts); if(!found) { log_unverifiable(intf,n,&addr); close_sok(&(n->fd)); n->complete = 1; return NS_NOCHANGE; } else { log_verified(intf,n,&addr); close_socket(intf,&(n->sok)); n->sok = n->fd; n->fd = -1; n->complete = 0; return NS_NEGOTIATE; } } return NS_DELETE; } FUNC_TYPE(dcc_listen_postpoll) { if(n->complete == 1) { struct sockaddr_in addr; socklen_t len = sizeof(addr); if(n->poll->revents & POLL_ERRORS) { n->err = socket_error(n->sok); E(n->err); return NS_DELETE; } else if(n->poll->revents & POLLIN) { n->fd = accept_socket(n->sok, (struct sockaddr *)&addr,&len); if(n->fd != -1) { if(intf->secure_mode) { n->complete = 2; } else { n->complete = 0; close_socket(intf,&(n->sok)); n->sok = n->fd; n->fd = -1; log_unverified(intf,n,&addr); return NS_NEGOTIATE; } } } } return NS_NOCHANGE; } FUNC_TYPE(dccserv_conn_prepoll) { if(n->sok == -1) { struct sockaddr_in addr; struct in_addr *addrs; char host[256]; int ret; host[0] = '\0'; if(intf->type == INTERFACE_SEND) { send_t *s = (send_t *)n->data; LOCK(s); if(s->host != NULL) xstrncpy(host,s->host,sizeof(host)); UNLOCK(s); } else if(intf->type == INTERFACE_CHAT) { chat_t *c = (chat_t *)n->data; LOCK(c); if(c->host != NULL) xstrncpy(host,c->host,sizeof(host)); UNLOCK(c); } if(host[0] == '\0') return NS_NOCHANGE; ret = aresolv_lookup(global->aresolver,host,&addrs); if(ret == -1) { n->err = EDESTADDRREQ; E(n->err); return NS_DELETE; } else if(ret == 0) return NS_NOCHANGE; addr.sin_family = AF_INET; addr.sin_port = htons(intf->dccserver_port); memcpy(&(addr.sin_addr),&(addrs[0]),sizeof(struct in_addr)); xfree(addrs); if(do_connect(intf,n,&addr) == -1) return NS_DELETE; } n->poll = poll_add(intf,n->sok,POLLIN | POLLOUT); return NS_NOCHANGE; } FUNC_TYPE(dcc_outbound_postpoll) { D("%d %d %p %p",ct,ft,intf,n); if(n->sok != -1) { if(n->poll->revents & POLL_ERRORS) { n->err = socket_error(n->sok); E(n->err); return NS_DELETE; } else if(n->poll->revents & (POLLIN | POLLOUT)) { n->complete = 0; return NS_NEGOTIATE; } } return NS_NOCHANGE; } FUNC_TYPE(dcc_outbound_prepoll) { D("%d %d %p %p",ct,ft,intf,n); if(n->sok == -1 && n->complete == 0) { if(intf->type == INTERFACE_SEND) dcc_rsend_msg((send_t *)n->data); else if(intf->type == INTERFACE_CHAT) dcc_rchat_msg((chat_t *)n->data); else if(intf->type == INTERFACE_RECV) dcc_recv_msg((send_t *)n->data); n->complete = 1; } else if(n->sok == -1 && n->complete == 1) { struct sockaddr_in addr; dcc_aux_t *aux = NULL; addr.sin_family = AF_INET; if(intf->type == INTERFACE_SEND) { send_t *s = (send_t *)n->data; LOCK(s); aux = (dcc_aux_t *)s->aux; } else if(intf->type == INTERFACE_CHAT) { chat_t *c = (chat_t *)n->data; LOCK(c); aux = (dcc_aux_t *)c->aux; } else if(intf->type == INTERFACE_RECV) { send_t *r = (send_t *)n->data; LOCK(r); if(r->state != STATE_RESUME) aux = (dcc_aux_t *)r->aux; } if(aux != NULL) { memcpy(&(addr.sin_addr),&(aux->addr), sizeof(struct in_addr)); addr.sin_port = htons(aux->port); } if(intf->type == INTERFACE_SEND || intf->type == INTERFACE_RECV) UNLOCK((send_t *)n->data); else if(intf->type == INTERFACE_CHAT) UNLOCK((chat_t *)n->data); if(aux == NULL) return NS_NOCHANGE; else if(do_connect(intf,n,&addr) == -1) return NS_DELETE; n->complete = 0; n->poll = poll_add(intf,n->sok,POLLOUT | POLLIN); } else { n->poll = poll_add(intf,n->sok,POLLOUT); } return NS_NOCHANGE; } /** Negotiate Functions **/ FUNC_TYPE(send_nego_prepoll) { send_t *s = (send_t *)n->data; LOCK(s); s->state = STATE_ACTIVE; s->bytes_sent = 0; s->connected = xtime(); s->last_contact = s->connected; n->pos = s->pos; n->ack = n->pos; UNLOCK(s); n->events = POLLOUT; set_sndbuf(intf,n->sok); n->fd = open(n->file,O_RDONLY | O_LARGEFILE); if(n->fd != -1) { log_connected(intf,n); se_notify_send(1,intf->interface,s,0); return NS_RUN; } else { n->err = errno; E(n->err); return NS_DELETE; } } FUNC_TYPE(send_chat_dccserv_nego_prepoll) { n->poll = poll_add(intf,n->sok,n->complete == 0 ? POLLOUT : POLLIN); return NS_NOCHANGE; } static void get_network_nick(send_t *s, chat_t *c, char *buffer, size_t bufsize) { network_t *net = NULL; if(s != NULL) { LOCK(s); net = s->network; UNLOCK(s); } else if(c != NULL) { LOCK(c); net = c->network; UNLOCK(c); } if(net == NULL) return; LOCK(net); if(net->info != NULL) { LOCK(net->info); xstrncpy(buffer,net->info->nick,sizeof(buffer)); UNLOCK(net->info); } UNLOCK(net); } FUNC_TYPE(send_dccserv_nego_postpoll) { char buffer[514]; send_t *s = (send_t *)n->data; int ret = -1; if(n->poll->revents & POLL_ERRORS) { n->err = socket_error(n->sok); E(n->err); return NS_DELETE; } else if(n->complete == 0 && (n->poll->revents & POLLOUT)) { char nick[64] = "ARISA"; get_network_nick(s,NULL,nick,sizeof(nick)); pathtofn(n->file,intf->buffer,intf->bufsize); ret = IMIN( snprintf(buffer,513, "120 %s %lld %s\r\n", nick,n->size,intf->buffer), 512 ); ret = send(n->sok,buffer,ret,0); if(ret == -1 && (errno == EAGAIN || errno == EINTR)) return NS_NOCHANGE; else if(ret == -1) { n->err = errno; E(n->err); return NS_DELETE; } n->complete = 1; } else if(n->complete == 1 && (n->poll->revents & POLLIN)) { char *ptr; ret = recv(n->sok,buffer,ret,0); if(ret == -1 && (errno == EAGAIN || errno == EINTR)) return NS_NOCHANGE; else if(ret == -1 || ret == 0) { n->err = errno; E(n->err); return NS_DELETE; } buffer[ret] = '\0'; ret = strtol(buffer,NULL,10); ptr = strchr(buffer,' '); if(ptr != NULL) n->pos = strtoull(ptr + 1,NULL,10); if(ret != 121 || n->pos >= n->size) { if(ret != 121) n->err = EPROTONOSUPPORT; else n->err = ERANGE; E(n->err); return NS_DELETE; } LOCK(s); s->pos = n->pos; UNLOCK(s); set_func(n,NEGO_PREPOLL,send_nego_prepoll); set_func(n,NEGO_POSTPOLL,NULL); } return NS_NOCHANGE; } FUNC_TYPE(recv_nego_prepoll) { char buffer[4096]; struct utimbuf ubuf; struct stat sbuf; send_t *r = (send_t *)n->data; int ret; if(n->file == NULL) { n->err = EINVAL; E(n->err); return NS_DELETE; } #ifdef HAVE_STATFS { struct statfs sfsbuf; LOCK(intf->interface); if(intf->interface->recv.dir != NULL) xstrncpy(buffer,intf->interface->recv.dir,sizeof(buffer)); else xstrncpy(buffer,".",sizeof(buffer)); UNLOCK(intf->interface); ret = statfs(buffer,&sfsbuf); if(ret == 0) { if((n->size - n->pos) > ((off_t)sfsbuf.f_bsize * (off_t)sfsbuf.f_bavail)) { n->err = ENOMEM; E(n->err); return NS_DELETE; } } } #endif /* HAVE_STATFS */ if(n->pos == 0) { n->fd = open(n->file, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP); } else { ret = stat(n->file,&sbuf); if(ret == -1) { n->err = errno; E(n->err); return NS_DELETE; } else if((xtime() - sbuf.st_mtime) < 5) { n->err = EBUSY; E(n->err); return NS_DELETE; } else if(sbuf.st_size < n->pos) { n->err = EIO; E(n->err); return NS_DELETE; } n->fd = open(n->file,O_WRONLY | O_LARGEFILE); } if(n->fd == -1) { n->err = errno; return NS_DELETE; } /* Set the modification time now so that if by chance we attempt to * resume this file multiple times at once further attempts will fail. */ ubuf.actime = ubuf.modtime = xtime(); utime(n->file,&ubuf); // ignore errors if(n->pos != 0) { if(sbuf.st_size > n->pos) ftruncate(n->fd,n->pos); // ignore errors if(lseek(n->fd,n->pos,SEEK_SET) == -1) { n->err = errno; E(n->err); return NS_DELETE; } } LOCK(r); r->state = STATE_ACTIVE; if(r->connected == 0) r->connected = xtime(); r->last_contact = xtime(); r->pos = n->pos; se_notify_recv(0,intf->interface,r,0); UNLOCK(r); set_rcvbuf(intf,n->sok); n->state = NS_RUN; n->events = POLLIN; log_upload_active(intf,n); return NS_RUN; } FUNC_TYPE(recv_dccserv_nego_prepoll) { n->poll = poll_add(intf,n->sok,n->events); return NS_NOCHANGE; } static int recv_dccserv_init_upload(intf_t *intf, node_t *n); // pre-declaration FUNC_TYPE(recv_dccserv_nego_postpoll) { char buffer[514]; int ret; if(n->poll == NULL) return NS_NOCHANGE; if(n->poll->revents & POLL_ERRORS) { // continue } else if(n->complete == 0 && (n->poll->revents & POLLIN)) { char *ptr, **tokens = NULL; int no_tokens = 4; ret = recv(n->sok,buffer,512,0); if(ret == -1 && ret == EAGAIN) return NS_NOCHANGE; else if(ret == -1 || ret == 0) return NS_DELETE; buffer[ret] = '\0'; ptr = strchr(buffer,'\r'); if(ptr != NULL) *ptr = '\0'; else { ptr = strchr(buffer,'\n'); if(ptr != NULL) *ptr = '\0'; } tokens = split_opt(buffer,buffer,&no_tokens); ret = NS_DELETE; if(no_tokens == 4) { if(atoi(tokens[0]) == 120) { LOCK(intf->interface); n->file = calculate_fn(intf,tokens[3]); UNLOCK(intf->interface); n->size = strtoull(tokens[2],NULL,10); if(valid_file_size(intf,n->size)) { calculate_resume(intf,n); ret = NS_NOCHANGE; } } } else if(no_tokens == 2) { if(atoi(tokens[0]) == 100) { ret = NS_NOCHANGE; } } if(tokens != NULL) xfree(tokens); if(ret != NS_DELETE) { n->complete = 1; n->events = POLLOUT; return ret; } } else if(n->complete == 1) { struct sockaddr_in addr; socklen_t len = sizeof(addr); char buffer[64],**hosts; int i,ret,found = 0; getpeername(n->sok,(struct sockaddr *)&addr,&len); ret = aresolv_lookup_reverse(global->aresolver, &(addr.sin_addr),&hosts); if(ret == 0) return NS_NOCHANGE; inet_ntop(AF_INET,&(addr.sin_addr),buffer,sizeof(buffer)); if(n->file != NULL) { access_t *acc; LOCK(intf->interface); acc = intf->interface->recv.access; UNLOCK(intf->interface); if(ret > 0) { for(i = 0; i < ret && !found; ++i) { if(access_check(acc,NULL,hosts[i],NULL,NULL)) found = 1; } } if(!found) { if(access_check(acc,NULL,buffer,NULL,NULL)) found = 1; } } else { LOCK(global); for(i = 0; i < global->no_users && !found; ++i) { user_t *u = global->users[i]; int j,k; LOCK(u); for(j = 0; j < u->no_hosts && !found; ++j) { for(k = 0; k < ret && !found; ++k) { if(irc_hostmask_match(hosts[k],u->hosts[j])) found = 1; } if(!found) { if(irc_hostmask_match(buffer,u->hosts[j])) found = 1; } } UNLOCK(u); } UNLOCK(global); } if(found) { n->complete = 2; return NS_NOCHANGE; } } else if(n->complete == 2 && (n->poll->revents & POLLOUT)) { if(n->file != NULL) { ret = IMIN( snprintf(buffer,sizeof(buffer), "121 %lld",n->pos), sizeof(buffer)-1 ); if(recv_dccserv_init_upload(intf,n) == 0) { ret = send(n->sok,buffer,ret,0); if(ret != -1) return NS_NOCHANGE; } } else { xstrncpy(buffer,"101 ARISA\r\n",sizeof(buffer)); ret = send(n->sok,buffer,strlen(buffer),0); if(ret != -1) { admin_open(n->sok,NULL,NULL,NULL,NULL); n->sok = -1; } return NS_DELETE; } } else if(n->poll->revents == 0) return NS_NOCHANGE; xstrncpy(buffer,"150 Unavailable\r\n",sizeof(buffer)); send(n->sok,buffer,strlen(buffer),0); return NS_DELETE; } FUNC_TYPE(chat_nego_prepoll) { chat_t *c = (chat_t *)n->data; interface_t *i = intf->interface; LOCK(i); LOCK(c); if(c->type == CHAT_ADMIN) { PTRARR_DEL(&(i->chat.chats),&(i->chat.no_chats),c); UNLOCK(c); UNLOCK(i); admin_begin(n->sok,c); n->sok = -1; n->data = NULL; } else if(c->type == CHAT_LIST) { packlist_t *list = c->list.packlist; c->connected = xtime(); c->last_contact = c->connected; UNLOCK(c); UNLOCK(i); if(packlist_valid(list)) { LOCK(list); n->time_limit = list->time_limit; UNLOCK(list); n->events = POLLOUT; n->snd_buf = alloc_linebuf(512); n->rcv_buf = alloc_linebuf(512); n->state = NS_RUN; n->complete = 0; log_connected(intf,n); return NS_RUN; } } else { UNLOCK(c); UNLOCK(i); } return NS_DELETE; } FUNC_TYPE(chat_dccserv_nego_postpoll) { char buffer[514]; chat_t *c = (chat_t *)n->data; int ret = -1; if(n->poll->revents & POLL_ERRORS) { n->err = socket_error(n->sok); E(n->err); return NS_DELETE; } else if(n->complete == 0 && (n->poll->revents & POLLOUT)) { char nick[64] = "ARISA"; get_network_nick(NULL,c,nick,sizeof(nick)); ret = IMIN( snprintf(buffer,513,"100 %s",nick), 512 ); ret = send(n->sok,buffer,ret,0); if(ret == -1 && (errno == EAGAIN || errno == EINTR)) return NS_NOCHANGE; else if(ret == -1) { n->err = errno; E(n->err); return NS_DELETE; } n->complete = 1; } else if(n->complete == 1 && (n->poll->revents & POLLIN)) { ret = recv(n->sok,buffer,512,0); if(ret == -1 && (errno == EAGAIN || errno == EINTR)) return NS_NOCHANGE; else if(ret == -1) { n->err = errno; E(n->err); return NS_DELETE; } buffer[ret] = '\0'; ret = strtol(buffer,NULL,10); if(ret != 101) { n->err = EPROTONOSUPPORT; E(n->err); return NS_DELETE; } set_func(n,NEGO_PREPOLL,chat_nego_prepoll); set_func(n,NEGO_POSTPOLL,NULL); } return NS_NOCHANGE; } /** Bandwidth Functions **/ static int active_nodes(cl_t *cl) { node_t *n = CL_FRONT(cl); int count = 0; if(n == NULL) return 0; do { if(n->state == NS_RUN) count++; n = n->next; } while(n != CL_FRONT(cl)); return count; } static int active_subnodes(node_t *n) { return active_nodes(n->green) + active_nodes(n->yellow) + active_nodes(n->red) + active_nodes(n->white); } static int has_active(node_t *n); // pre-declaration static int has_active_cl(cl_t *cl) { node_t *n; if(cl == NULL) return 0; n = CL_FRONT(cl); if(n == NULL) return 0; do { if(n->state == NS_RUN && n->sok >= 0) return 1; else if(has_active(n)) return 1; n = n->next; } while(n != CL_FRONT(cl)); return 0; } static int has_active(node_t *n) { if(has_active_cl(n->green)) return 1; if(has_active_cl(n->yellow)) return 1; if(has_active_cl(n->red)) return 1; if(has_active_cl(n->white)) return 1; return 0; } static void assign_ac_rates(cl_t *cl, uint32_t a_rate, uint32_t c_rate) { node_t *n = CL_FRONT(cl); if(n == NULL) return; do { n->a_rate = a_rate; if(c_rate != 0 && (n->c_rate > c_rate || n->c_rate == 0)) n->c_rate = c_rate; n = n->next; } while(n != CL_FRONT(cl)); } static uint32_t sum_rates(cl_t *cl) { node_t *n = CL_FRONT(cl); uint32_t sum = 0; if(n == NULL) return 0; do { if(n->state == NS_RUN) { if(n->c_rate != 0) sum += n->c_rate; else return -1; } n = n->next; } while(n != CL_FRONT(cl)); return sum; } FUNC_TYPE(calculate_rates) { uint32_t tmp, rate; int sub_nodes = active_subnodes(n); if(n->a_rate == 0) { if(n->c_rate == 0) { rate = 0; rate = rate != -1 && (tmp = sum_rates(n->green)) != -1 ? rate + tmp : -1; rate = rate != -1 && (tmp = sum_rates(n->yellow)) != -1 ? rate + tmp : -1; rate = rate != -1 && (tmp = sum_rates(n->red)) != -1 ? rate + tmp : -1; rate = rate != -1 && (tmp = sum_rates(n->white)) != -1 ? rate + tmp : -1; if(rate < 0) rate = 0; } else rate = n->c_rate; n->a_rate = rate; } else rate = n->a_rate; if(rate != 0 && sub_nodes > 0) rate /= sub_nodes; assign_ac_rates(n->green,rate,n->c_rate); assign_ac_rates(n->yellow,rate,n->c_rate); assign_ac_rates(n->red,rate,n->c_rate); assign_ac_rates(n->white,rate,n->c_rate); return NS_NOCHANGE; } /** Delete Functions **/ FUNC_TYPE(recv_dccserv_delete) { if(n->sok != -1) log_dccserv_close(intf,n); return NS_DELETE; } static void send_delete_msg(char *buffer, size_t bufsize, node_t *n, state_t state, char *netnick, char *file, int hashed, char *crc, char *md5, off_t sent, off_t rate, time_t now, time_t elapsed, char *reason) { if(state == STATE_COMPLETE) { if(hashed) snprintf(buffer,bufsize, "Send of \"%s\" Completed, sent %lld bytes at %uKiB/sec. CRC: %s. MD5: %s.", file,sent, (uint32_t)(sent / (elapsed != 0 ? elapsed : 1)) / 1024, crc,md5); else snprintf(buffer,bufsize, "Send of \"%s\" Completed, sent %lld bytes at %uKiB/sec.", file,sent, (uint32_t)(sent / (elapsed != 0 ? elapsed : 1)) / 1024); LOGTP(L_INT,"Closed Send to [%s] of {\"%s\"}, Completed, %lld bytes sent at %uKiB/sec.", netnick,file,sent, (uint32_t)((sent / (elapsed != 0 ? elapsed : 1)) / 1024)); } else if(state == STATE_DELETED) { snprintf(buffer,bufsize, "Closed Send of \"%s\", %s.", file, reason != NULL ? reason : "Admin Requested Close"); LOGTP(L_INT, "Closed Send to [%s] of {\"%s\"}, Deleted (Reason: %s).", netnick,file, reason != NULL ? reason : "Admin Requested"); } else if(state == STATE_SLOW) { snprintf(buffer,bufsize, "Closed Send of \"%s\", Speed of %.1fKiB/sec is Below Minimum Requirement.", file,(float)(rate / AVGSPEED) / 1024.0); LOGTP(L_INT, "Closed Send to [%s] of {\"%s\"}, Below Minimum Speed Requirement.", netnick,file); } else if(state == STATE_TIMEOUT) { snprintf(buffer,bufsize, "Closed Send of \"%s\", Timed Out.", file); LOGTP(L_INT, "Closed Send to [%s] of {\"%s\"}, Timed Out.", netnick,file); } else { char errbuf[96]; lstrerror_r(n->err,errbuf,sizeof(errbuf)); snprintf(buffer,bufsize, "Closed Send of \"%s\", Error: %s.", file,errbuf); LOGTP(L_INT, "Closed Send to [%s] of {\"%s\"}, Error: %s.", netnick,file,errbuf); } } static void recv_delete_msg(char *buffer, size_t bufsize, node_t *n, state_t state, char *netnick, char *file, off_t sent, time_t now, time_t elapsed, char *reason) { send_t *send = (send_t *)n->data; dcc_aux_t *aux; char addrbuf[64]; LOCK(send); aux = (dcc_aux_t *)send->aux; if(aux != NULL) { char addrbuf2[64]; inet_ntop(AF_INET,&(aux->addr),addrbuf2,sizeof(addrbuf2)); snprintf(addrbuf,sizeof(addrbuf),"%s:%u",addrbuf2,aux->port); } UNLOCK(send); if(aux == NULL) peer_hostport_str(n->sok,addrbuf,sizeof(addrbuf)); if(state == STATE_COMPLETE) { snprintf(buffer,bufsize, "Upload of \"%s\" Completed, %lld bytes at %uKiB/sec.", file,sent, (uint32_t)(sent / (elapsed != 0 ? elapsed : 1)) / 1024); LOGTP(L_INT,"Closed Upload of {\"%s\"} from (%s) [%s], Completed %lld bytes at %uKiB/sec.", file,addrbuf,netnick,sent, (uint32_t)(sent / (elapsed != 0 ? elapsed : 1)) / 1024); } else if(state == STATE_DELETED) { snprintf(buffer,bufsize, "Closed Upload of \"%s\", %s.",file, reason != NULL ? reason : "Admin Requested Close"); LOGTP(L_INT,"Closed Upload of {\"%s\"} from (%s) [%s], Deleted (Reason: %s).", file,addrbuf,netnick, reason != NULL ? reason : "Admin Request"); } else { char errbuf[96]; lstrerror_r(n->err,errbuf,sizeof(errbuf)); snprintf(buffer,bufsize, "Closed Upload of \"%s\", Error: %s.", file, state == STATE_TIMEOUT ? "Timed Out" : errbuf); LOGTP(L_INT,"Closed Upload of {\"%s\"} from (%s) [%s], Error: %s.", file,addrbuf,netnick, state == STATE_TIMEOUT ? "Timed Out" : errbuf); } } FUNC_TYPE(send_recv_delete) { send_t *s = (send_t *)n->data; char buffer[512],nick[64],netnick[128],reason[256]; char file[512],crc[64],md5[64]; state_t state; network_t *net; time_t elapsed, now = xtime(); off_t sent = 0, rate = 0; int i,hashed = 0; netnick_str(intf,n,netnick,sizeof(netnick)); LOCK(s); net = s->network; xstrncpy(nick,s->nick != NULL ? s->nick : "",sizeof(nick)); state = s->state; elapsed = (now - s->connected); sent = s->bytes_sent; xstrncpy(reason,s->reason != NULL ? s->reason : "",sizeof(reason)); if(intf->type == INTERFACE_SEND) { se_notify_send(0,intf->interface,s,n->err); if(s->pack != NULL) { if(state == STATE_COMPLETE) WLOCK(s->pack); else RLOCK(s->pack); pathtofn(s->pack->file,file,sizeof(file)); if((hashed = s->pack->hashed) != 0) { xstrncpy(crc,s->pack->crc,sizeof(crc)); xstrncpy(md5,s->pack->md5,sizeof(md5)); } if(state == STATE_COMPLETE) { s->pack->no_gets++; WUNLOCK(s->pack); } else { RUNLOCK(s->pack); } } else file[0] = '\0'; for(i = 0, rate = 0; i < AVGSPEED; ++i) rate += (off_t) s->avgspeed[i]; if(state == STATE_ERROR || state == STATE_TIMEOUT) s->state = STATE_PURGEREQUEUE; else s->state = STATE_PURGING; } else { RLOCK(s->pack); pathtofn(s->pack->file,file,sizeof(file)); RUNLOCK(s->pack); se_notify_recv(0,intf->interface,s,n->err); } UNLOCK(s); if(intf->type == INTERFACE_SEND) { send_delete_msg(buffer,sizeof(buffer), n,state, netnick,file,hashed,crc,md5, sent,rate,now,elapsed, reason[0] != '\0' ? reason : NULL); if(state == STATE_COMPLETE && n->pool != NULL) { uint32_t avgrate = sent / elapsed; LOCK(n->pool); if(n->pool->record_send < avgrate) n->pool->record_send = avgrate; UNLOCK(n->pool); } } else if(intf->type == INTERFACE_RECV) { recv_delete_msg(buffer,sizeof(buffer), n,state, netnick,file,sent, now,elapsed, reason[0] != '\0' ? reason : NULL); } LOCK(s); if(s->aux != NULL) { xfree(s->aux); s->aux = NULL; } UNLOCK(s); if(intf->type == INTERFACE_RECV) { LOCK(intf->interface); PTRARR_DEL(&(intf->interface->recv.uploads), &(intf->interface->recv.no_uploads),s); UNLOCK(intf->interface); free_send(s); } if(!intf->end && net != NULL) irc_send_notice(net,nick,buffer); return NS_DELETE; } FUNC_TYPE(chat_delete) { chat_t *c = (chat_t *)n->data; char errbuf[96],netnick[128],why[256]; if(c == NULL) return NS_DELETE; netnick_str(intf,n,netnick,sizeof(netnick)); LOCK(c); switch(c->state) { case STATE_COMPLETE: strcpy(why,"Complete"); break; case STATE_DELETED: if(c->reason == NULL) strcpy(why,"Admin Request"); else xstrncpy(why,c->reason,sizeof(why)); break; case STATE_TIMEOUT: strcpy(why,"Timed Out"); break; case STATE_ERROR: snprintf(why,sizeof(why),"Error: %s", lstrerror_r(n->err,errbuf,sizeof(errbuf))); break; default: why[0] = '\0'; break; } UNLOCK(c); LOGTP(L_INT,"Closed chat with [%s], %s.",netnick,why); LOCK(intf->interface); PTRARR_DEL(&(intf->interface->chat.chats), &(intf->interface->chat.no_chats),c); UNLOCK(intf->interface); free_chat(c); return NS_DELETE; } /** Check Functions **/ FUNC_TYPE(send_recv_check_prepoll) { send_t *send = (send_t *)n->data; time_t now = xtime(); uint32_t speed; int i; //D("1: %p, colour: %d, events: %d",n,n->colour,n->events); LOCK(send); if(send->state == STATE_DELETED) { UNLOCK(send); return NS_DELETE; } if((n->state != NS_RUN || (n->state == NS_RUN && n->p_rate == 0)) && intf->dcc_timeout != 0 && (send->last_contact + intf->dcc_timeout) < now) { send->state = STATE_TIMEOUT; UNLOCK(send); return NS_DELETE; } if(n->state == NS_RUN) { if(n->colour == CL_RED && n->err != 0) { if(n->pos != n->size) { send->state = STATE_ERROR; } else { send->state = STATE_COMPLETE; send->bytes_sent += (off_t) n->p_rate; } UNLOCK(send); return NS_DELETE; } else if(n->pos == n->size && n->ack == n->size) { send->state = STATE_COMPLETE; send->bytes_sent += (off_t) n->p_rate; UNLOCK(send); return NS_DELETE; } else if(n->pos == n->size) send->state = STATE_COMPLETING; send->avgspeed[now % AVGSPEED] = n->p_rate; if((send->connected + 60) < now && n->min_rate > 0) { for(i = 0, speed = 0; i < AVGSPEED; ++i) speed += send->avgspeed[i]; if(speed < (n->min_rate * AVGSPEED)) { send->state = STATE_SLOW; UNLOCK(send); return NS_DELETE; } } send->bytes_sent += (off_t) n->p_rate; send->pos = n->pos; if(n->p_rate > 0) send->last_contact = now; if(send->state != STATE_COMPLETING) send->state = STATE_ACTIVE; UNLOCK(send); n->c_rate = n->max_rate; if(n->c_rate != 0 && n->p_rate > n->c_rate) { int32_t lc_rate = n->c_rate - (n->p_rate - n->c_rate); if(n->max_rate != 0 && lc_rate <= 0) lc_rate = n->c_rate / 4; if(lc_rate > 0) n->c_rate = lc_rate; } n->p_rate = 0; n->a_rate = 0; if(n->events == 0) { if(intf->type == INTERFACE_SEND) { if(!need_acks(intf,n) && n->pos < n->size) n->events = POLLOUT; else n->events = POLLIN; } else { n->events = POLLIN; } } if(n->colour != CL_WHITE) n->colour = CL_GREEN; else n->poll = poll_add(intf,n->sok,n->events); } else UNLOCK(send); //D("2: %p, colour: %d, events: %d",n,n->colour,n->events); return NS_NOCHANGE; } FUNC_TYPE(dcc_check_postpoll) { if(n->state == NS_RUN && n->colour == CL_WHITE) { if(n->poll->revents) n->colour = CL_GREEN; } return NS_NOCHANGE; } static void assign_max_min_rate(cl_t *cl, uint32_t max, uint32_t min) { node_t *n = CL_FRONT(cl); if(n == NULL) return; do { n->max_rate = max; n->min_rate = min; n = n->next; } while(n != CL_FRONT(cl)); } static void init_send(intf_t *intf, node_t *n, send_t *s); // pre-declaration FUNC_TYPE(pool_check_prepoll) { pool_t *p = (pool_t *) n->data; send_t *s; uint32_t send_max, send_min; time_t now = xtime(); int i; //D("%d %d %p %p",ct,ft,intf,n); //D("p: %p",p); pool_purge_sends(p); LOCK(p); if(p->deleted != 0) { UNLOCK(p); return NS_DELETE; } stat_update(&p->stat_mib,&p->stat_carry,n->p_rate); p->avgspeed[now % AVGSPEED] = n->p_rate; if(n->p_rate > p->record_rate) p->record_rate = n->p_rate; /* If the pool exceeded its ceiling rate last cycle, compensate with a lower ceiling this cycle. */ n->c_rate = (p->bandwidth * 1024U); if(n->c_rate != 0 && n->p_rate > n->c_rate) n->c_rate -= (n->p_rate - n->c_rate); if(p->bandwidth != 0 && n->c_rate <= 0) n->c_rate = (p->bandwidth * 256U); n->p_rate = 0; n->a_rate = 0; send_max = p->send_max_bandwidth * 1024U; send_min = p->send_min_bandwidth * 1024U; assign_max_min_rate(n->green,send_max,send_min); assign_max_min_rate(n->yellow,send_max,send_min); assign_max_min_rate(n->red,send_max,send_min); assign_max_min_rate(n->white,send_max,send_min); while(p->no_csends < p->sends) { UNLOCK(p); s = queue_dequeue(p); LOCK(p); if(s != NULL) { PTRARR_ADD(&p->csends,&p->no_csends,s); s->state = STATE_QUEUED; } else // here since we need lock on break; } for(i = 0; i < p->no_csends; ++i) { LOCK(p->csends[i]); if(p->csends[i]->state == STATE_QUEUED) { init_send(intf,n,p->csends[i]); se_notify_send(0,intf->interface,p->csends[i],0); } UNLOCK(p->csends[i]); } UNLOCK(p); return NS_NOCHANGE; } static void node_shuffle(node_t *n); // pre-declaration FUNC_TYPE(pool_check_postpoll) { node_shuffle(n); if(n->green->size > 0 || n->yellow->size > 0) n->colour = CL_GREEN; else if(n->white->size > 0) n->colour = CL_WHITE; else n->colour = CL_RED; /* D("green: %d, yellow: %d, white: %d, red: %d, n->colour: %d", n->green->size,n->yellow->size, n->white->size,n->red->size, n->colour); */ return NS_NOCHANGE; } FUNC_TYPE(recv_dccserv_check_prepoll) { time_t now = xtime(); if((n->connected + intf->dcc_timeout) < now) return NS_DELETE; else return NS_NOCHANGE; } FUNC_TYPE(chat_check_prepoll) { chat_t *chat = (chat_t *)n->data; time_t connected, now = xtime(); packlist_t *list; LOCK(chat); if(chat->state == STATE_DELETED) { UNLOCK(chat); return NS_DELETE; } if((n->state != NS_RUN || (n->state == NS_RUN && n->p_rate == 0)) && intf->dcc_timeout != 0 && (chat->last_contact + intf->dcc_timeout) < now) { chat->state = STATE_TIMEOUT; UNLOCK(chat); return NS_DELETE; } if(n->state == NS_RUN) { if(n->colour == CL_RED && (n->err != 0 || n->complete == 2)) { if(n->complete) chat->state = STATE_COMPLETE; else chat->state = STATE_ERROR; UNLOCK(chat); D(""); return NS_DELETE; } else if(chat->state == STATE_COMPLETING && (pqueue_length(&(chat->msgqueue)) == 0 || n->complete == 2)) { chat->state = STATE_COMPLETE; UNLOCK(chat); D(""); return NS_DELETE; } else if(chat->state == STATE_COMPLETING) n->events = POLLOUT; if(n->p_rate != 0) chat->last_contact = now; n->c_rate = 0; n->p_rate = 0; n->a_rate = 0; if(n->events == 0) n->events = POLLIN; list = chat->list.packlist; connected = chat->connected; UNLOCK(chat); if(!packlist_valid(list) || (connected + n->time_limit) < now) { char *msg; LOCK(chat); n->events = POLLOUT; n->complete = 1; chat->state = STATE_COMPLETING; shutdown(n->sok,SHUT_RD); n->events = POLLOUT; n->complete = 1; chat->state = STATE_COMPLETING; shutdown(n->sok,SHUT_RD); if((connected + n->time_limit) < now) msg = xstrdup("Timelimit Reached.\n"); else msg = xstrdup("Packlist Deleted.\n"); pqueue_push_back(&(chat->msgqueue),msg); UNLOCK(chat); } if(n->colour != CL_WHITE) n->colour = CL_GREEN; else n->poll = poll_add(intf,n->sok,n->events); } else UNLOCK(chat); return NS_NOCHANGE; } static void node_shuffle_by_colour(node_t *n, cl_t *cl) { node_t *pn; int i; if(cl == NULL) return; for(i = cl->size; i > 0; --i) { pn = CL_POP_FRONT(cl); //D("pn: %p, %d <=> %d",pn,pn->colour,cl->colour); if(pn->colour != cl->colour) { switch(pn->colour) { case CL_GREEN: CL_PUSH_BACK(n->green,pn); break; case CL_YELLOW: CL_PUSH_BACK(n->yellow,pn); break; case CL_RED: CL_PUSH_BACK(n->red,pn); break; case CL_WHITE: CL_PUSH_BACK(n->white,pn); break; } } else CL_PUSH_BACK(cl,pn); } } static void node_shuffle(node_t *n) { node_shuffle_by_colour(n,n->green); node_shuffle_by_colour(n,n->yellow); node_shuffle_by_colour(n,n->red); node_shuffle_by_colour(n,n->white); } static int interface_check_prepoll(intf_t *intf) { LOCK(intf->interface); if(intf->interface->deleted != 0) intf->end = 1; UNLOCK(intf->interface); if(intf->end == 1) return -1; update_bandwidth_stats(intf); load_interface_settings(intf); return 0; } static void setup_static_polls(intf_t *intf) { int i; if(intf->sok_unused != NULL) { for(i = pqueue_length(intf->sok_unused); i > 0; --i) { sok_t *s = pqueue_pop_front(intf->sok_unused); s->poll = poll_add(intf,s->sok,POLLIN); pqueue_push_back(intf->sok_unused,s); //D("i = %d, s = %p, s->port = %u",i,s,s->port); } } } static void run_static_polls(intf_t *intf) { int i,ret,sok; if(intf->sok_unused != NULL) { for(i = pqueue_length(intf->sok_unused); i > 0; --i) { sok_t *s = pqueue_pop_front(intf->sok_unused); if(s->poll->revents != 0) { do { ret = accept(s->sok,NULL,NULL); if(ret != -1) { sok = ret; close_sok(&sok); } } while(ret != -1); } pqueue_push_back(intf->sok_unused,s); //D("i = %d, s = %p, s->port = %u",i,s,s->port); } } } static void init_pool(intf_t *intf, pool_t *p); // pre-declaration FUNC_TYPE(send_interface_check_prepoll) { int i; if(interface_check_prepoll(intf) != 0) return NS_DELETE; pool_purge(intf->interface,xtime()); setup_static_polls(intf); LOCK(intf->interface); for(i = 0; i < intf->interface->send.no_pools; ++i) { pool_t *p = intf->interface->send.pools[i]; //D("p: %p",p); LOCK(p); if(p->deleted == 0 && p->state == STATE_UNKNOWN) init_pool(intf,p); UNLOCK(p); } UNLOCK(intf->interface); return NS_NOCHANGE; } FUNC_TYPE(send_interface_check_postpoll) { node_shuffle(n); run_static_polls(intf); return NS_NOCHANGE; } static void init_recv(intf_t *intf, send_t *r); // pre-declaration FUNC_TYPE(recv_interface_check_prepoll) { node_t *pn; int i; if(interface_check_prepoll(intf) != 0) return NS_DELETE; if(n->sok != -1) n->poll = poll_add(intf,n->sok,POLLIN); LOCK(intf->interface); for(i = 0; i < intf->interface->recv.no_uploads; ++i) { send_t *s = intf->interface->recv.uploads[i]; LOCK(s); if(s->state == STATE_QUEUED) { D("s: %p",s); init_recv(intf,s); se_notify_recv(0,intf->interface,s,0); } UNLOCK(s); } UNLOCK(intf->interface); for(i = n->white->size; i > 0; --i) { pn = CL_POP_FRONT(n->white); if(pn->pos == -1) { calculate_resume(intf,pn); if(pn->pos >= pn->size) { pn->err = ECANCELED; E(pn->err); free_node(intf,pn); } else CL_PUSH_BACK(n->white,pn); } else CL_PUSH_BACK(n->white,pn); } return NS_NOCHANGE; } static void init_dccserv(intf_t *intf, int sok); // pre-declaration FUNC_TYPE(recv_interface_check_postpoll) { node_shuffle(n); if(n->sok != -1) { if(n->poll->revents & POLLIN) { int ret, i = 0; do { ret = accept(n->sok,NULL,NULL); if(ret != -1) { init_dccserv(intf,ret); i++; } } while(ret != -1 && i < intf->accept_per_sec); } } return NS_NOCHANGE; } static void init_chat(intf_t *intf, chat_t *c); // pre-declaration FUNC_TYPE(chat_interface_check_prepoll) { int i; if(interface_check_prepoll(intf) != 0) return NS_DELETE; setup_static_polls(intf); LOCK(intf->interface); for(i = 0; i < intf->interface->chat.no_chats; ++i) { chat_t *c = intf->interface->chat.chats[i]; LOCK(c); if(c->state == STATE_QUEUED) init_chat(intf,c); UNLOCK(c); } UNLOCK(intf->interface); return NS_NOCHANGE; } FUNC_TYPE(chat_interface_check_postpoll) { node_shuffle(n); run_static_polls(intf); return NS_NOCHANGE; } /** Init Functions **/ static void init_send(intf_t *intf, node_t *p, send_t *s) { node_t *n = alloc_node(); RLOCK(s->pack); n->file = xstrdup(s->pack->file); n->size = s->pack->size; RUNLOCK(s->pack); s->state = STATE_LISTENING; s->started = xtime(); s->last_contact = s->started; n->state = NS_CONNECT; n->data = s; n->pool = (pool_t *)p->data; set_func(n,CHECK_PREPOLL,send_recv_check_prepoll); set_func(n,CHECK_POSTPOLL,dcc_check_postpoll); set_func(n,RUN,send_run); set_func(n,DELETE,send_recv_delete); switch(intf->mode) { case IMODE_NORMAL: set_func(n,CONN_PREPOLL, dcc_listen_prepoll); set_func(n,CONN_POSTPOLL, dcc_listen_postpoll); set_func(n,NEGO_PREPOLL, send_nego_prepoll); break; case IMODE_DCCSERVER: set_func(n,CONN_PREPOLL, dccserv_conn_prepoll); set_func(n,CONN_POSTPOLL, dcc_outbound_postpoll); set_func(n,NEGO_PREPOLL, send_chat_dccserv_nego_prepoll); set_func(n,NEGO_POSTPOLL, send_dccserv_nego_postpoll); break; case IMODE_REVERSE: set_func(n,CONN_PREPOLL, dcc_outbound_prepoll); set_func(n,CONN_POSTPOLL, dcc_outbound_postpoll); set_func(n,NEGO_PREPOLL, send_nego_prepoll); break; } CL_PUSH_BACK(p->white,n); } static void init_pool(intf_t *intf, pool_t *p) { node_t *n = alloc_node(); //D("p: %p",p); p->state = STATE_ACTIVE; n->state = NS_RUN; n->data = p; set_func(n,CHECK_PREPOLL,pool_check_prepoll); set_func(n,CHECK_POSTPOLL,pool_check_postpoll); set_func(n,RUN_PREPOLL,common_run_prepoll); set_func(n,RUN_POSTPOLL,common_run_postpoll); set_func(n,RUN,colour_run); set_func(n,BANDWIDTH,calculate_rates); CL_PUSH_BACK(intf->root->white,n); } static void init_dccserv(intf_t *intf, int sok) { node_t *n = alloc_node(); n->state = NS_NEGOTIATE; n->sok = sok; n->connected = xtime(); n->events = POLLIN; log_dccserv_incoming(intf,n); set_func(n,CHECK_PREPOLL,recv_dccserv_check_prepoll); set_func(n,NEGO_PREPOLL,recv_dccserv_nego_prepoll); set_func(n,NEGO_POSTPOLL,recv_dccserv_nego_postpoll); set_func(n,DELETE,recv_dccserv_delete); CL_PUSH_BACK(intf->root->white,n); } static void recv_set_common_funcs(node_t *n) { set_func(n,CHECK_PREPOLL,send_recv_check_prepoll); set_func(n,CHECK_POSTPOLL,dcc_check_postpoll); set_func(n,RUN,recv_run); set_func(n,DELETE,send_recv_delete); set_func(n,NEGO_PREPOLL,recv_nego_prepoll); } static void init_recv(intf_t *intf, send_t *r) { node_t *n = alloc_node(); dcc_aux_t *aux; RLOCK(r->pack); n->file = calculate_fn(intf,r->pack->file); n->size = r->pack->size; RUNLOCK(r->pack); r->state = STATE_LISTENING; r->started = xtime(); r->last_contact = r->started; n->state = NS_CONNECT; n->data = r; aux = (dcc_aux_t *)r->aux; n->pos = -1; // This is a flag to the upper level check function // that we need to calculate the resume. recv_set_common_funcs(n); if(aux->reverse == 0) { set_func(n,CONN_PREPOLL,dcc_outbound_prepoll); set_func(n,CONN_POSTPOLL,dcc_outbound_postpoll); } else { set_func(n,CONN_PREPOLL,dcc_listen_prepoll); set_func(n,CONN_POSTPOLL,dcc_listen_postpoll); } CL_PUSH_BACK(intf->root->white,n); } static int recv_dccserv_init_upload(intf_t *intf, node_t *n) { struct sockaddr_in addr; socklen_t len = sizeof(addr); dcc_aux_t *aux; send_t *r = NULL; char buffer[512],addrbuf[64]; LOCK(intf->interface); if(intf->interface->recv.no_uploads < intf->interface->recv.max_no_uploads) { r = alloc_send(); LOCK(r); PTRARR_ADD(&(intf->interface->recv.uploads), &(intf->interface->recv.no_uploads),r); } UNLOCK(intf->interface); if(r == NULL) return -1; recv_set_common_funcs(n); set_func(n,NEGO_POSTPOLL,NULL); getpeername(n->sok,(struct sockaddr *)&addr,&len); inet_ntop(AF_INET,&(addr.sin_addr),addrbuf,sizeof(addrbuf)); snprintf(buffer,sizeof(buffer),"%s:%u",addrbuf,ntohs(addr.sin_port)); r->started = n->connected; r->last_contact = xtime(); r->nick = xstrdup(buffer); r->host = xstrdup(addrbuf); pathtofn(n->file,buffer,sizeof(buffer)); r->pack = alloc_pack(); r->pack->file = xstrdup(buffer); r->pack->size = n->size; r->pos = n->pos; aux = xalloc(sizeof(dcc_aux_t)); r->aux = aux; memcpy(&(aux->addr),&(addr.sin_addr),sizeof(struct in_addr)); aux->port = ntohs(addr.sin_port); se_notify_recv(0,intf->interface,r,0); UNLOCK(r); log_dccserv_upload(intf,n); n->data = r; return 0; } static void init_chat(intf_t *intf, chat_t *c) { node_t *n = alloc_node(); D("c: %p",c); n->state = NS_CONNECT; n->data = c; c->state = STATE_LISTENING; c->started = xtime(); c->last_contact = c->started; set_func(n,CHECK_PREPOLL,chat_check_prepoll); set_func(n,CHECK_POSTPOLL,dcc_check_postpoll); set_func(n,RUN,chat_run); set_func(n,DELETE,chat_delete); switch(intf->mode) { case IMODE_NORMAL: set_func(n,CONN_PREPOLL, dcc_listen_prepoll); set_func(n,CONN_POSTPOLL, dcc_listen_postpoll); set_func(n,NEGO_PREPOLL, chat_nego_prepoll); break; case IMODE_DCCSERVER: set_func(n,CONN_PREPOLL, dccserv_conn_prepoll); set_func(n,CONN_POSTPOLL, dcc_outbound_postpoll); set_func(n,NEGO_PREPOLL, send_chat_dccserv_nego_prepoll); set_func(n,NEGO_POSTPOLL, chat_dccserv_nego_postpoll); break; case IMODE_REVERSE: set_func(n,CONN_PREPOLL, dcc_outbound_prepoll); set_func(n,CONN_POSTPOLL, dcc_outbound_postpoll); set_func(n,NEGO_PREPOLL, chat_nego_prepoll); break; } CL_PUSH_BACK(intf->root->white,n); } /** Interface Running Functions **/ static uint32_t generate_tokens(node_t *n, size_t token_size, uint32_t off_usec) { uint32_t tokens_total,tokens_used; int ret; if(n->c_rate == 0) return 16374; /* "infinite" tokens */ if(n->p_rate >= n->c_rate) return 0; if(token_size <= 0) token_size = 512; tokens_total = ceil((float)n->c_rate / (float)token_size); tokens_used = ceil((float)n->p_rate / (float)token_size); if(tokens_total < 1) tokens_total = 1; else if(tokens_total > 1000000) tokens_total = 1000000; ret = (uint32_t)(((off_usec / (1000000 / tokens_total)) - tokens_used)); ret = ret > 0 ? ret : 0; return ret; } static void interface_started(interface_t *i) { LOCK(i); LOG_TITLE("Interface %s",i->name); UNLOCK(i); LOGTP(L_INF,"Started"); } static void interface_stopped(interface_t *i) { LOCK(i); UNLOCK(i); LOGTP(L_INF,"Shutdown"); } // like diff_tv_usec, but inline static inline uint32_t diff_usec(struct timeval *ti, struct timeval *tj) { if(ti->tv_sec == tj->tv_sec) return IMAX(ti->tv_usec,tj->tv_usec) - IMIN(ti->tv_usec,tj->tv_usec); else { if(IMAX(ti->tv_sec,tj->tv_sec) == ti->tv_sec) { return ((ti->tv_sec - tj->tv_sec) * 1000000) + (ti->tv_usec - tj->tv_usec); } else { return ((tj->tv_sec - ti->tv_sec) * 1000000) + (tj->tv_usec - ti->tv_usec); } } } static int master_tick(intf_t *intf, uint32_t tick_length) { poll_wipe(intf); if(run_func(UPWARD,CHECK_PREPOLL,intf,intf->root) == NS_DELETE) return -1; run_func(UPWARD,CONN_PREPOLL,intf,intf->root); run_func(UPWARD,NEGO_PREPOLL,intf,intf->root); poll(intf->fds,intf->no_fds,tick_length / 1000); if(run_func(UPWARD,CHECK_POSTPOLL,intf,intf->root) == NS_DELETE) return -1; run_func(UPWARD,CONN_POSTPOLL,intf,intf->root); run_func(UPWARD,NEGO_POSTPOLL,intf,intf->root); run_func(DOWNWARD,BANDWIDTH,intf,intf->root); return 0; } static void minor_tick(intf_t *intf, uint32_t tick_length) { poll_wipe(intf); run_func(UPWARD,RUN_PREPOLL,intf,intf->root); poll(intf->fds,intf->no_fds,tick_length / 1000); run_func(UPWARD,RUN_POSTPOLL,intf,intf->root); /* D("n: %p, green: %d, yellow: %d, red: %d, white: %d", intf->root, intf->root->green->size, intf->root->yellow->size, intf->root->red->size, intf->root->white->size); */ } static void run_tokens(intf_t *intf, uint32_t tokens) { node_t *n = intf->root; if(n->c_rate == 0) { while((tokens--) > 0) { if(N_RUN(intf,n) != 1) break; } } else { while((tokens--) > 0 && n->p_rate < n->c_rate) { if(N_RUN(intf,n) != 1) break; } } } static void interface_run(intf_t *intf) { uint32_t tick_length = 20000; uint32_t el_usec,ticks; struct timeval tvs,tve; node_t *root; uint32_t tokens; /* run */ while((!thread_should_end()) && intf->end == 0) { gettimeofday(&tvs,NULL); if(master_tick(intf,tick_length) != 0) break; root = intf->root; if(root->c_rate != 0) { ticks = (root->c_rate*2) / IMAX(intf->snd_bufsize,intf->rcv_bufsize); if(ticks < intf->ticks_low) ticks = intf->ticks_low; else if(ticks > intf->ticks_high) ticks = intf->ticks_high; } else ticks = intf->ticks_high; tick_length = 1000000 / ticks; do { if(has_active(intf->root)) { minor_tick(intf,tick_length); gettimeofday(&tve,NULL); el_usec = diff_usec(&tvs,&tve); tokens = generate_tokens(root, intf->token_size,el_usec); if(el_usec < 1000000 && tokens == 0) { usleep(IMIN(tick_length, 1000000 - el_usec)); gettimeofday(&tve,NULL); el_usec = diff_usec(&tvs,&tve); tokens = generate_tokens(root, intf->token_size, el_usec < 1000000 ? el_usec : 1000000); } run_tokens(intf,tokens); } else { gettimeofday(&tve,NULL); el_usec = diff_usec(&tvs,&tve); if(el_usec < 1000000) usleep(1000000 - diff_usec(&tvs,&tve)); } gettimeofday(&tve,NULL); } while(diff_usec(&tvs,&tve) < 1000000); } intf->end = 1; } void *interface_thread(void *interface_ptr) { interface_t *interface = (interface_t *)interface_ptr; intf_t *intf; thread_signal_started(&(interface->thread)); LOCK(interface); intf = alloc_intf(interface->type,interface); UNLOCK(interface); load_interface_settings(intf); interface_started(interface); if(intf->type == INTERFACE_SEND) { int i,j; LOGTP(L_INF,"Using %s method.",sendfile_method); LOCK(interface); for(i = 0; i < interface->send.no_pools; ++i) { pool_t *p = interface->send.pools[i]; LOCK(p); for(j = 0; j < p->no_csends; ++j) { LOCK(p->csends[j]); p->csends[j]->state = STATE_QUEUED; UNLOCK(p->csends[j]); } UNLOCK(p); } UNLOCK(interface); } switch (intf->type) { case INTERFACE_SEND: intf->ticks_high = 64; set_func(intf->root,CHECK_PREPOLL, send_interface_check_prepoll); set_func(intf->root,CHECK_POSTPOLL, send_interface_check_postpoll); break; case INTERFACE_RECV: intf->ticks_high = 32; set_func(intf->root,CHECK_PREPOLL, recv_interface_check_prepoll); set_func(intf->root,CHECK_POSTPOLL, recv_interface_check_postpoll); break; case INTERFACE_CHAT: intf->ticks_high = 16; set_func(intf->root,CHECK_PREPOLL, chat_interface_check_prepoll); set_func(intf->root,CHECK_POSTPOLL, chat_interface_check_postpoll); break; default: assert(0); break; } intf->ticks_low = 4; intf->root->state = NS_RUN; set_func(intf->root,RUN_PREPOLL,common_run_prepoll); set_func(intf->root,RUN_POSTPOLL,common_run_postpoll); set_func(intf->root,RUN,colour_run); set_func(intf->root,BANDWIDTH,calculate_rates); interface_run(intf); free_intf(intf); interface_stopped(interface); thread_signal_finished(); return NULL; }