📄 server-core.c
字号:
int nr, n, ret = 0; svz_vector_t *accepted = NULL; /* Check connect frequency. */ if (port->accepted) accepted = (svz_vector_t *) svz_hash_get (port->accepted, ip); else port->accepted = svz_hash_create (4); current = time (NULL); if (accepted != NULL) { /* Delete older entries and count valid entries. */ nr = 0; svz_vector_foreach (accepted, t, n) { if (*t < current - 4) { svz_vector_del (accepted, n); n--; } else nr++; } /* Check the connection frequency. */ if ((nr /= 4) > port->connect_freq) { svz_log (LOG_NOTICE, "connect frequency reached: %s: %d/%d\n", ip, nr, port->connect_freq); ret = -1; } } /* Not yet connected. */ else { accepted = svz_vector_create (sizeof (time_t)); } svz_vector_add (accepted, ¤t); svz_hash_put (port->accepted, ip, accepted); return ret;}/* * This function returns zero if the @var{child} socket is allowed to * connect to the port configuration of the @var{parent} socket structure * which needs to be a listener therefore. */intsvz_sock_check_access (svz_socket_t *parent, svz_socket_t *child){ svz_portcfg_t *port; char *ip; int n, ret; char *remote; /* Check arguments and return if this function cannot work. */ if (parent == NULL || child == NULL || parent->port == NULL) return 0; /* Get port configuration and remote address. */ port = parent->port; remote = svz_inet_ntoa (child->remote_addr); /* Check the deny IP addresses. */ if (port->deny) { svz_array_foreach (port->deny, ip, n) { if (!strcmp (ip, remote)) { svz_log (LOG_NOTICE, "denying access from %s\n", ip); return -1; } } } /* Check allow IP addresses. */ if (port->allow) { ret = -1; svz_array_foreach (port->allow, ip, n) { if (!strcmp (ip, remote)) { svz_log (LOG_NOTICE, "allowing access from %s\n", ip); ret = 0; } } if (ret) { svz_log (LOG_NOTICE, "denying unallowed access from %s\n", remote); return ret; } } return 0;}/* * Return the parents port configuration of the socket structure @var{sock} * or @code{NULL} if the given socket has no parent, i.e. is a listener. */svz_portcfg_t *svz_sock_portcfg (svz_socket_t *sock){ svz_portcfg_t *port = NULL; svz_socket_t *parent; if ((parent = svz_sock_getparent (sock)) != NULL) port = parent->port; return port;}/* * Set the sockets @var{child} parent to the given socket structure * @var{parent}. This should be called whenever a listener accepts a * connection and creates a new child socket. */voidsvz_sock_setparent (svz_socket_t *child, svz_socket_t *parent){ if (parent != NULL && child != NULL) { child->parent_id = parent->id; child->parent_version = parent->version; }}/* * Return the sockets @var{child} parent socket structure or @code{NULL} * if this socket does not exist anymore. This might happen if a listener * dies for some reason. */svz_socket_t *svz_sock_getparent (svz_socket_t *child){ if (!child) return NULL; return svz_sock_find (child->parent_id, child->parent_version);}/* * Set the referring socket structure of @var{sock} to the given socket * @var{referrer}. This can be used to create some relationship between * two socket structures. If @var{referrer} is @code{NULL} the reference * will be invalidated. */voidsvz_sock_setreferrer (svz_socket_t *sock, svz_socket_t *referrer){ if (referrer == NULL) { sock->referrer_version = sock->referrer_id = -1; } else { sock->referrer_id = referrer->id; sock->referrer_version = referrer->version; }}/* * Get the referrer of the socket structure @var{sock}. Return @code{NULL} * if there is no such socket. */svz_socket_t *svz_sock_getreferrer (svz_socket_t *sock){ if (!sock) return NULL; return svz_sock_find (sock->referrer_id, sock->referrer_version);}/* * Return the socket structure for the socket id @var{id} and the version * @var{version} or @code{NULL} if no such socket exists. If @var{version} * is -1 it is not checked. */svz_socket_t *svz_sock_find (int id, int version){ svz_socket_t *sock; if (id & ~(svz_sock_limit - 1)) { svz_log (LOG_FATAL, "socket id %d is invalid\n", id); return NULL; } sock = svz_sock_lookup_table[id]; if (version != -1 && sock && sock->version != version) { svz_log (LOG_WARNING, "socket version %d (id %d) is invalid\n", version, id); return NULL; } return svz_sock_lookup_table[id];}/* * Create the socket lookup table initially. Must be called from * @code{svz_boot()}. */voidsvz_sock_table_create (void){ svz_sock_lookup_table = svz_calloc (svz_sock_limit * sizeof (svz_socket_t *));}/* * Destroy the socket lookup table finally. Must be called from * @code{svz_halt()}. */voidsvz_sock_table_destroy (void){ svz_free_and_zero (svz_sock_lookup_table);}/* * Calculate unique socket structure id and assign a version for a * given @var{sock}. The version is for validating socket structures. It is * currently used in the coserver callbacks. */intsvz_sock_unique_id (svz_socket_t *sock){ int i; for (i = 0; i < svz_sock_limit; i++) { svz_sock_id++; svz_sock_id &= (svz_sock_limit - 1); if (NULL == svz_sock_lookup_table[svz_sock_id]) break; } /* ensure global limit, resize the lookup table if necessary */ if (i == svz_sock_limit) { svz_sock_lookup_table = svz_realloc (svz_sock_lookup_table, svz_sock_limit * 2 * sizeof (svz_socket_t *)); memset (&svz_sock_lookup_table[svz_sock_limit], 0, svz_sock_limit * sizeof (svz_socket_t *)); svz_sock_id = svz_sock_limit; svz_sock_limit *= 2; svz_log (LOG_NOTICE, "lookup table enlarged to %d\n", svz_sock_limit); } sock->id = svz_sock_id; sock->version = svz_sock_version++; return svz_sock_id;}/* * This gets called when the server receives a SIGHUP, which means * that the server should be reset. */static intsvz_reset (void){ /* FIXME: Maybe `server_t' reset callback ? */ return 0;}/* * Do everything to shut down the socket @var{sock}. The socket structure * gets removed from the socket queue, the file descriptor is closed * and all memory used by the socket gets freed. Note that this * function calls the @var{sock}'s disconnect handler if defined. */static intsvz_sock_shutdown (svz_socket_t *sock){#if ENABLE_DEBUG svz_log (LOG_DEBUG, "shutting down socket id %d\n", sock->id);#endif if (sock->disconnected_socket) sock->disconnected_socket (sock); svz_sock_dequeue (sock); if (sock->flags & SOCK_FLAG_SOCK) svz_sock_disconnect (sock); if (sock->flags & SOCK_FLAG_PIPE) svz_pipe_disconnect (sock); svz_sock_free (sock); return 0;}/* * Shutdown all sockets within the socket list no matter if it was * scheduled for shutdown or not. */voidsvz_sock_shutdown_all (void){ svz_socket_t *sock; sock = svz_sock_root; while (sock) { svz_sock_shutdown (sock); sock = svz_sock_root; } svz_sock_root = svz_sock_last = NULL;}/* * Mark socket @var{sock} as killed. That means that no operations except * disconnecting and freeing are allowed anymore. All marked sockets * will be deleted once the server loop is through. */intsvz_sock_schedule_for_shutdown (svz_socket_t *sock){ if (!(sock->flags & SOCK_FLAG_KILLED)) {#if ENABLE_DEBUG svz_log (LOG_DEBUG, "scheduling socket id %d for shutdown\n", sock->id);#endif /* ENABLE_DEBUG */ sock->flags |= SOCK_FLAG_KILLED; /* Shutdown each child for listeners. */ if (sock->flags & SOCK_FLAG_LISTENING) { svz_socket_t *child; svz_sock_foreach (child) if (svz_sock_getparent (child) == sock) svz_sock_schedule_for_shutdown (child); } } return 0;}/* * This routine gets called once a second and is supposed to perform any * task that has to get scheduled periodically. It checks all sockets' * timers and calls their timer functions when necessary. */intsvz_periodic_tasks (void){ svz_socket_t *sock; svz_notify += 1; sock = svz_sock_root; while (sock) {#if ENABLE_FLOOD_PROTECTION if (sock->flood_points > 0) { sock->flood_points--; }#endif /* ENABLE_FLOOD_PROTECTION */ if (sock->idle_func && sock->idle_counter > 0) { if (--sock->idle_counter <= 0) { if (sock->idle_func (sock)) { svz_log (LOG_ERROR, "idle function for socket id %d " "returned error\n", sock->id); svz_sock_schedule_for_shutdown (sock); } } } sock = sock->next; } /* check regularly for internal coserver responses and keep coservers alive */ svz_coserver_check (); /* run the server instance timer routines */ svz_server_notifiers (); return 0;}/* * Goes through all socket and shuts invalid ones down. */voidsvz_sock_check_bogus (void){#ifdef __MINGW32__ unsigned long readBytes;#endif svz_socket_t *sock;#if ENABLE_DEBUG svz_log (LOG_DEBUG, "checking for bogus sockets\n");#endif /* ENABLE_DEBUG */ svz_sock_foreach (sock) { if (sock->flags & SOCK_FLAG_SOCK) {#ifdef __MINGW32__ if (ioctlsocket (sock->sock_desc, FIONREAD, &readBytes) == SOCKET_ERROR) {#else /* not __MINGW32__ */ if (fcntl (sock->sock_desc, F_GETFL) < 0) {#endif /* not __MINGW32__ */ svz_log (LOG_ERROR, "socket %d has gone\n", sock->sock_desc); svz_sock_schedule_for_shutdown (sock); } }#ifndef __MINGW32__ if (sock->flags & SOCK_FLAG_RECV_PIPE) { if (fcntl (sock->pipe_desc[READ], F_GETFL) < 0) { svz_log (LOG_ERROR, "pipe %d has gone\n", sock->pipe_desc[READ]); svz_sock_schedule_for_shutdown (sock); } } if (sock->flags & SOCK_FLAG_SEND_PIPE) { if (fcntl (sock->pipe_desc[WRITE], F_GETFL) < 0) { svz_log (LOG_ERROR, "pipe %d has gone\n", sock->pipe_desc[WRITE]); svz_sock_schedule_for_shutdown (sock); } }#endif /* not __MINGW32__ */ }} /* * Setup signaling for the core library. */voidsvz_signal_up (void){#ifdef SIGTERM signal (SIGTERM, svz_signal_handler);#endif#ifdef SIGQUIT signal (SIGQUIT, svz_signal_handler);#endif#ifdef SIGINT signal (SIGINT, svz_signal_handler);#endif#ifdef SIGBREAK signal (SIGBREAK, svz_signal_handler);#endif#ifdef SIGCHLD signal (SIGCHLD, svz_signal_handler);#endif#ifdef SIGHUP signal (SIGHUP, svz_signal_handler);#endif#ifdef SIGPIPE signal (SIGPIPE, svz_signal_handler);#endif#ifdef SIGSEGV signal (SIGSEGV, svz_segfault_exception);#endif}/* * Deinstall signaling for the core library. */voidsvz_signal_dn (void){#ifdef SIGTERM signal (SIGTERM, SIG_DFL);#endif#ifdef SIGQUIT signal (SIGQUIT, SIG_DFL);#endif#ifdef SIGINT signal (SIGINT, SIG_DFL);#endif#ifdef SIGBREAK signal (SIGBREAK, SIG_DFL);#endif#ifdef SIGCHLD signal (SIGCHLD, SIG_DFL);#endif#ifdef SIGHUP signal (SIGHUP, SIG_DFL);#endif#ifdef SIGPIPE signal (SIGPIPE, SIG_DFL);#endif#ifdef SIGSEGV signal (SIGSEGV, SIG_DFL);#endif}/* * This routine handles all things once and is called regularly in the * below @code{svz_loop()} routine. */voidsvz_loop_one (void){ svz_socket_t *sock, *next; static int rechain = 0; /* * FIXME: Remove this once the server is stable. */#if ENABLE_DEBUG svz_sock_validate_list ();#endif /* ENABLE_DEBUG */ if (svz_reset_happened) { /* SIGHUP received. */ svz_log (LOG_NOTICE, "resetting server\n"); svz_reset (); svz_reset_happened = 0; } if (svz_pipe_broke) { /* SIGPIPE received. */ svz_log (LOG_ERROR, "broken pipe, continuing\n"); svz_pipe_broke = 0; } if (svz_child_died) { /* SIGCHLD received. */ svz_log (LOG_ERROR, "child pid %d died\n", (int) svz_child_died); svz_child_died = 0; } /* * Check for new connections on server port, incoming data from * clients and process queued output data. */ svz_check_sockets (); /* * Reorder the socket chain every 16 select loops. We do not do it * every time for performance reasons. */ if (rechain++ & 16) svz_sock_rechain_list (); /* * Shut down all sockets that have been scheduled for closing. */ sock = svz_sock_root; while (sock) { next = sock->next; if (sock->flags & SOCK_FLAG_KILLED) svz_sock_shutdown (sock); sock = next; }}/* * Call this function once before using @code{svz_loop_one()}. */voidsvz_loop_pre (void){ /* * Setting up control variables. These get set either in the signal * handler or from a command processing routine. */ svz_nuke_happened = 0; svz_reset_happened = 0; svz_child_died = 0; svz_pipe_broke = 0; svz_notify = time (NULL); /* Run the server loop. */ svz_log (LOG_NOTICE, "entering server loop\n");}/* * Call this function once after using @code{svz_loop_one()}. */voidsvz_loop_post (void){ svz_log (LOG_NOTICE, "leaving server loop\n"); /* Shutdown all socket structures. */ svz_sock_shutdown_all ();}/* * Main server loop. Handle all signals, incoming and outgoing connections * and listening server sockets. */voidsvz_loop (void){ svz_loop_pre (); while (!svz_nuke_happened) { svz_loop_one (); } svz_loop_post ();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -