/* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */ /* See LICENSE for licensing information */ /* $Id$ */ #include "or.h" /********* PROTOTYPES **********/ static void dumpstats(int severity); /* log stats */ static int init_from_config(int argc, char **argv); /********* START VARIABLES **********/ extern char *conn_state_to_string[][_CONN_TYPE_MAX+1]; or_options_t options; /* command-line and config-file options */ int global_read_bucket; /* max number of bytes I can read this second */ static int stats_prev_global_read_bucket; static uint64_t stats_n_bytes_read = 0; static long stats_n_seconds_reading = 0; static connection_t *connection_array[MAXCONNECTIONS] = { NULL }; static struct pollfd poll_array[MAXCONNECTIONS]; static int nfds=0; /* number of connections currently active */ #ifndef MS_WINDOWS /* do signal stuff only on unix */ static int please_dumpstats=0; /* whether we should dump stats during the loop */ static int please_reset=0; /* whether we just got a sighup */ static int please_reap_children=0; /* whether we should waitpid for exited children */ #endif /* signal stuff */ int has_fetched_directory=0; /* we set this to 1 when we've fetched a dir, to know whether to complain * yet about unrecognized nicknames in entrynodes, exitnodes, etc. * Also, we don't try building circuits unless this is 1. */ int has_completed_circuit=0; /* we set this to 1 when we've opened a circuit, so we can print a log * entry to inform the user that Tor is working. */ /********* END VARIABLES ************/ /**************************************************************************** * * This section contains accessors and other methods on the connection_array * and poll_array variables (which are global within this file and unavailable * outside it). * ****************************************************************************/ int connection_add(connection_t *conn) { if(nfds >= options.MaxConn-1) { log_fn(LOG_WARN,"failing because nfds is too high."); return -1; } conn->poll_index = nfds; connection_set_poll_socket(conn); connection_array[nfds] = conn; /* zero these out here, because otherwise we'll inherit values from the previously freed one */ poll_array[nfds].events = 0; poll_array[nfds].revents = 0; nfds++; log_fn(LOG_INFO,"new conn type %s, socket %d, nfds %d.", CONN_TYPE_TO_STRING(conn->type), conn->s, nfds); return 0; } void connection_set_poll_socket(connection_t *conn) { poll_array[conn->poll_index].fd = conn->s; } /* Remove the connection from the global list, and remove the * corresponding poll entry. Calling this function will shift the last * connection (if any) into the position occupied by conn. */ int connection_remove(connection_t *conn) { int current_index; assert(conn); assert(nfds>0); log_fn(LOG_INFO,"removing socket %d (type %s), nfds now %d", conn->s, CONN_TYPE_TO_STRING(conn->type), nfds-1); /* if it's an edge conn, remove it from the list * of conn's on this circuit. If it's not on an edge, * flush and send destroys for all circuits on this conn */ circuit_about_to_close_connection(conn); current_index = conn->poll_index; if(current_index == nfds-1) { /* this is the end */ nfds--; return 0; } /* replace this one with the one at the end */ nfds--; poll_array[current_index].fd = poll_array[nfds].fd; poll_array[current_index].events = poll_array[nfds].events; poll_array[current_index].revents = poll_array[nfds].revents; connection_array[current_index] = connection_array[nfds]; connection_array[current_index]->poll_index = current_index; return 0; } void get_connection_array(connection_t ***array, int *n) { *array = connection_array; *n = nfds; } void connection_watch_events(connection_t *conn, short events) { assert(conn && conn->poll_index < nfds); poll_array[conn->poll_index].events = events; } int connection_is_reading(connection_t *conn) { return poll_array[conn->poll_index].events & POLLIN; } void connection_stop_reading(connection_t *conn) { assert(conn && conn->poll_index < nfds); log(LOG_DEBUG,"connection_stop_reading() called."); if(poll_array[conn->poll_index].events & POLLIN) poll_array[conn->poll_index].events -= POLLIN; } void connection_start_reading(connection_t *conn) { assert(conn && conn->poll_index < nfds); poll_array[conn->poll_index].events |= POLLIN; } int connection_is_writing(connection_t *conn) { return poll_array[conn->poll_index].events & POLLOUT; } void connection_stop_writing(connection_t *conn) { assert(conn && conn->poll_index < nfds); if(poll_array[conn->poll_index].events & POLLOUT) poll_array[conn->poll_index].events -= POLLOUT; } void connection_start_writing(connection_t *conn) { assert(conn && conn->poll_index < nfds); poll_array[conn->poll_index].events |= POLLOUT; } static void conn_read(int i) { connection_t *conn = connection_array[i]; if (conn->marked_for_close) return; /* see http://www.greenend.org.uk/rjk/2001/06/poll.html for * discussion of POLLIN vs POLLHUP */ if(!(poll_array[i].revents & (POLLIN|POLLHUP|POLLERR))) if(!connection_is_reading(conn) || !connection_has_pending_tls_data(conn)) return; /* this conn should not read */ log_fn(LOG_DEBUG,"socket %d wants to read.",conn->s); assert_connection_ok(conn, time(NULL)); if( /* XXX does POLLHUP also mean it's definitely broken? */ #ifdef MS_WINDOWS (poll_array[i].revents & POLLERR) || #endif connection_handle_read(conn) < 0) { if (!conn->marked_for_close) { /* this connection is broken. remove it */ /* XXX This shouldn't ever happen anymore. */ /* XXX but it'll clearly happen on MS_WINDOWS from POLLERR, right? */ log_fn(LOG_ERR,"Unhandled error on read for %s connection (fd %d); removing", CONN_TYPE_TO_STRING(conn->type), conn->s); connection_mark_for_close(conn,0); } } assert_connection_ok(conn, time(NULL)); } static void conn_write(int i) { connection_t *conn; if(!(poll_array[i].revents & POLLOUT)) return; /* this conn doesn't want to write */ conn = connection_array[i]; log_fn(LOG_DEBUG,"socket %d wants to write.",conn->s); if (conn->marked_for_close) return; assert_connection_ok(conn, time(NULL)); if (connection_handle_write(conn) < 0) { if (!conn->marked_for_close) { /* this connection is broken. remove it. */ log_fn(LOG_WARN,"Unhandled error on read for %s connection (fd %d); removing", CONN_TYPE_TO_STRING(conn->type), conn->s); conn->has_sent_end = 1; /* otherwise we cry wolf about duplicate close */ connection_mark_for_close(conn,0); } } assert_connection_ok(conn, time(NULL)); } static void conn_close_if_marked(int i) { connection_t *conn; int retval; conn = connection_array[i]; assert_connection_ok(conn, time(NULL)); if(!conn->marked_for_close) return; /* nothing to see here, move along */ log_fn(LOG_INFO,"Cleaning up connection (fd %d).",conn->s); if(conn->s >= 0 && connection_wants_to_flush(conn)) { /* -1 means it's an incomplete edge connection, or that the socket * has already been closed as unflushable. */ if(!conn->hold_open_until_flushed) log_fn(LOG_WARN, "Conn (fd %d, type %s, state %d) marked, but wants to flush %d bytes. " "(Marked at %s:%d)", conn->s, CONN_TYPE_TO_STRING(conn->type), conn->state, conn->outbuf_flushlen, conn->marked_for_close_file, conn->marked_for_close); if(connection_speaks_cells(conn)) { if(conn->state == OR_CONN_STATE_OPEN) { retval = flush_buf_tls(conn->tls, conn->outbuf, &conn->outbuf_flushlen); /* XXX actually, some non-zero results are maybe ok. which ones? */ } else retval = -1; /* never flush non-open broken tls connections */ } else { retval = flush_buf(conn->s, conn->outbuf, &conn->outbuf_flushlen); } if(retval >= 0 && conn->hold_open_until_flushed && connection_wants_to_flush(conn)) { log_fn(LOG_INFO,"Holding conn (fd %d) open for more flushing.",conn->s); /* XXX should we reset timestamp_lastwritten here? */ return; } if(connection_wants_to_flush(conn)) { log_fn(LOG_WARN,"Conn (fd %d, type %s, state %d) still wants to flush. Losing %d bytes! (Marked at %s:%d)", conn->s, CONN_TYPE_TO_STRING(conn->type), conn->state, (int)buf_datalen(conn->outbuf), conn->marked_for_close_file, conn->marked_for_close); } } connection_remove(conn); if(conn->type == CONN_TYPE_EXIT) { assert_connection_edge_not_dns_pending(conn); } connection_free(conn); if(i