X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fuping.c;h=62b69560db348c636e148f2f967d99e487746c6f;hb=refs%2Fheads%2Fupstream-ssl;hp=c84401b2da744d3b579eaf526babc4b25fbc7996;hpb=56ac07961060874ee62cc83a4af85be97efd6c27;p=ircu2.10.12-pk.git diff --git a/ircd/uping.c b/ircd/uping.c index c84401b..62b6956 100644 --- a/ircd/uping.c +++ b/ircd/uping.c @@ -15,13 +15,18 @@ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ */ +/** @file + * @brief UDP ping implementation. + * @version $Id$ + */ +#include "config.h" + #include "uping.h" #include "client.h" #include "ircd.h" #include "ircd_alloc.h" +#include "ircd_events.h" #include "ircd_log.h" #include "ircd_osdep.h" #include "ircd_string.h" @@ -37,8 +42,7 @@ #include "send.h" #include "sys.h" -#include -#include +/* #include -- Now using assert in ircd_log.h */ #include #include #include @@ -48,25 +52,22 @@ #include #include -#define UPINGTIMEOUT 60 /* Timeout waiting for ping responses */ +#define UPINGTIMEOUT 60 /**< Timeout waiting for ping responses */ -#ifndef INADDR_NONE -#define INADDR_NONE 0xffffffff -#endif +static struct UPing* pingList = 0; /**< Linked list of UPing structs */ +static struct Socket upingSock_v4; /**< Socket struct for IPv4 upings */ +static struct Socket upingSock_v6; /**< Socket struct for IPv6 upings */ -static struct UPing* pingList = 0; -int UPingFileDescriptor = -1; /* UDP listener socket for upings */ - -/* - * pings_begin - iterator function for ping list +/** Start iteration of uping list. + * @return Start of uping list. */ struct UPing* uping_begin(void) { return pingList; } -/* - * pings_erase - removes ping struct from ping list +/** Removes \a p from uping list. + * @param[in,out] p UPing to remove from list. */ static void uping_erase(struct UPing* p) { @@ -74,7 +75,7 @@ static void uping_erase(struct UPing* p) struct UPing* last = 0; assert(0 != p); - + for (it = pingList; it; last = it, it = it->next) { if (p == it) { if (last) @@ -86,109 +87,190 @@ static void uping_erase(struct UPing* p) } } -/* - * Setup a UDP socket and listen for incoming packets +/** Callback for uping listener socket. + * Reads a uping from the socket and respond, but not more than 10 + * times per second. + * @param[in] ev I/O event for uping socket. + */ +static void uping_echo_callback(struct Event* ev) +{ + struct Socket *sock; + struct irc_sockaddr from; + unsigned int len = 0; + static time_t last = 0; + static int counter = 0; + char buf[BUFSIZE + 1]; + + assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR); + sock = ev_socket(ev); + assert(sock == &upingSock_v4 || sock == &upingSock_v6); + + Debug((DEBUG_DEBUG, "UPING: uping_echo")); + + if (IO_SUCCESS != os_recvfrom_nonb(s_fd(sock), buf, BUFSIZE, &len, &from)) + return; + /* + * count em even if we're getting flooded so we can tell we're getting + * flooded. + */ + ++ServerStats->uping_recv; + if (len < 19) + return; + else if (CurrentTime != last) { + counter = 0; + last = CurrentTime; + } else if (++counter > 10) + return; + os_sendto_nonb(s_fd(sock), buf, len, NULL, 0, &from); +} + +/** Initialize a UDP socket for upings. + * @returns 0 on success, -1 on error. */ int uping_init(void) { - struct sockaddr_in from = { 0 }; + struct irc_sockaddr from; int fd; - memset(&from, 0, sizeof(from)); - from.sin_addr = VirtualHost.sin_addr; - from.sin_port = htons(atoi(UDP_PORT)); - from.sin_family = AF_INET; + memcpy(&from, &VirtualHost_v4, sizeof(from)); + from.port = atoi(UDP_PORT); - if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { - Debug((DEBUG_ERROR, "UPING: UDP listener socket call failed: %s", - (strerror(errno)) ? strerror(errno) : "Unknown error")); + fd = os_socket(&from, SOCK_DGRAM, "IPv4 uping listener", AF_INET); + if (fd < 0) return -1; - } - if (!os_set_reuseaddr(fd)) { - ircd_log(L_ERROR, "UPING: setsockopt UDP listener: fd %d", fd); - Debug((DEBUG_ERROR, "UPING: set reuseaddr on UDP listener failed: %s", - (strerror(errno)) ? strerror(errno) : "Unknown error")); + if (!socket_add(&upingSock_v4, uping_echo_callback, 0, SS_DATAGRAM, + SOCK_EVENT_READABLE, fd)) { + Debug((DEBUG_ERROR, "UPING: Unable to queue fd to event system")); close(fd); return -1; } - if (bind(fd, (struct sockaddr*) &from, sizeof(from)) == -1) { - ircd_log(L_ERROR, "UPING: bind UDP listener %d fd %d", htons(from.sin_port), fd); - Debug((DEBUG_ERROR, "UPING: bind on UDP listener failed : %s", - (strerror(errno)) ? strerror(errno) : "Unknown error")); - close(fd); + +#ifdef AF_INET6 + memcpy(&from, &VirtualHost_v6, sizeof(from)); + from.port = atoi(UDP_PORT); + + fd = os_socket(&from, SOCK_DGRAM, "IPv6 uping listener", AF_INET6); + if (fd < 0) return -1; - } - if (!os_set_nonblocking(fd)) { - Debug((DEBUG_ERROR, "UPING: set non-blocking: %s", - (strerror(errno)) ? strerror(errno) : "Unknown error")); + if (!socket_add(&upingSock_v6, uping_echo_callback, 0, SS_DATAGRAM, + SOCK_EVENT_READABLE, fd)) { + Debug((DEBUG_ERROR, "UPING: Unable to queue fd to event system")); close(fd); return -1; } - UPingFileDescriptor = fd; - return fd; +#endif + + return 0; } -/* - * max # of pings set to 15/sec. +/** Callback for socket activity on an outbound uping socket. + * @param[in] ev I/O event for socket. */ -void uping_echo() +static void uping_read_callback(struct Event* ev) { - struct sockaddr_in from = { 0 }; - unsigned int len = 0; - static time_t last = 0; - static int counter = 0; - char buf[BUFSIZE + 1]; + struct UPing *pptr; - Debug((DEBUG_DEBUG, "UPING: uping_echo")); + assert(0 != ev_socket(ev)); + assert(0 != s_data(ev_socket(ev))); - if (IO_SUCCESS != os_recvfrom_nonb(UPingFileDescriptor, buf, BUFSIZE, &len, &from)) - return; - /* - * count em even if we're getting flooded so we can tell we're getting - * flooded. - */ - ++ServerStats->uping_recv; - if (CurrentTime == last) { - if (++counter > 10) - return; + pptr = (struct UPing*) s_data(ev_socket(ev)); + + Debug((DEBUG_SEND, "uping_read_callback called, %p (%d)", pptr, + ev_type(ev))); + + if (ev_type(ev) == ET_DESTROY) { /* being destroyed */ + pptr->freeable &= ~UPING_PENDING_SOCKET; + + if (!pptr->freeable) + MyFree(pptr); /* done with it, finally */ + } else { + assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR); + + uping_read(pptr); /* read uping response */ } - else { - counter = 0; - last = CurrentTime; +} + +/** Timer callback to send another outbound uping. + * @param[in] ev Event for uping timer. + */ +static void uping_sender_callback(struct Event* ev) +{ + struct UPing *pptr; + + assert(0 != ev_timer(ev)); + assert(0 != t_data(ev_timer(ev))); + + pptr = (struct UPing*) t_data(ev_timer(ev)); + + Debug((DEBUG_SEND, "uping_sender_callback called, %p (%d)", pptr, + ev_type(ev))); + + if (ev_type(ev) == ET_DESTROY) { /* being destroyed */ + pptr->freeable &= ~UPING_PENDING_SENDER; + + if (!pptr->freeable) + MyFree(pptr); /* done with it, finally */ + } else { + assert(ev_type(ev) == ET_EXPIRE); + + pptr->lastsent = CurrentTime; /* store last ping time */ + uping_send(pptr); /* send a ping */ + + if (pptr->sent == pptr->count) /* done sending pings, don't send more */ + timer_del(ev_timer(ev)); } - if (len < 19) - return; - sendto(UPingFileDescriptor, buf, len, 0, (struct sockaddr*) &from, sizeof(from)); } +/** Timer callback to stop upings. + * @param[in] ev Event for uping expiration. + */ +static void uping_killer_callback(struct Event* ev) +{ + struct UPing *pptr; + + assert(0 != ev_timer(ev)); + assert(0 != t_data(ev_timer(ev))); -/* - * start_ping + pptr = (struct UPing*) t_data(ev_timer(ev)); + + Debug((DEBUG_SEND, "uping_killer_callback called, %p (%d)", pptr, + ev_type(ev))); + + if (ev_type(ev) == ET_DESTROY) { /* being destroyed */ + pptr->freeable &= ~UPING_PENDING_KILLER; + + if (!pptr->freeable) + MyFree(pptr); /* done with it, finally */ + } else { + assert(ev_type(ev) == ET_EXPIRE); + + uping_end(pptr); /* kill the uping, kill the uping! */ + } +} + +/** Start a uping. + * This sets up the timers, UPing flags, and sends a notice to the + * requesting client. */ static void uping_start(struct UPing* pptr) { assert(0 != pptr); - if (MyUser(pptr->client)) { - sendto_one(pptr->client, - ":%s NOTICE %s :Sending %d ping%s to %s", - me.name, pptr->client->name, pptr->count, - (pptr->count == 1) ? "" : "s", pptr->name); - } - else { - sendto_one(pptr->client, - "%s NOTICE %s%s :Sending %d ping%s to %s", - NumServ(&me), NumNick(pptr->client), pptr->count, - (pptr->count == 1) ? "" : "s", pptr->name); - } - pptr->timeout = CurrentTime + UPINGTIMEOUT; + timer_add(timer_init(&pptr->sender), uping_sender_callback, (void*) pptr, + TT_PERIODIC, 1); + timer_add(timer_init(&pptr->killer), uping_killer_callback, (void*) pptr, + TT_RELATIVE, UPINGTIMEOUT); + pptr->freeable |= UPING_PENDING_SENDER | UPING_PENDING_KILLER; + + sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :Sending %d ping%s to %s", + pptr->client, pptr->count, (pptr->count == 1) ? "" : "s", + pptr->name); pptr->active = 1; } -/* - * uping_send - * +/** Send a uping to another server. + * @param[in] pptr Descriptor for uping. */ void uping_send(struct UPing* pptr) { @@ -201,27 +283,21 @@ void uping_send(struct UPing* pptr) memset(buf, 0, sizeof(buf)); gettimeofday(&tv, NULL); - sprintf(buf, " %10lu%c%6lu", tv.tv_sec, '\0', tv.tv_usec); + sprintf(buf, " %10lu%c%6lu", (unsigned long)tv.tv_sec, '\0', (unsigned long)tv.tv_usec); Debug((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d", buf, &buf[12], - ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port), + ircd_ntoa(&pptr->addr.addr), pptr->addr.port, pptr->fd)); - if (sendto(pptr->fd, buf, BUFSIZE, 0, (struct sockaddr*) &pptr->sin, - sizeof(struct sockaddr_in)) != BUFSIZE) + if (os_sendto_nonb(pptr->fd, buf, BUFSIZE, NULL, 0, &pptr->addr) != IO_SUCCESS) { const char* msg = strerror(errno); if (!msg) msg = "Unknown error"; - if (pptr->client) { - if (MyUser(pptr->client)) - sendto_one(pptr->client, ":%s NOTICE %s :UPING: send failed: %s", - me.name, pptr->client->name, msg); - else - sendto_one(pptr->client, "%s NOTICE %s%s :UPING: sendto() failed: %s", - NumServ(&me), NumNick(pptr->client), msg); - } + if (pptr->client) + sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: send failed: " + "%s", pptr->client, msg); Debug((DEBUG_DEBUG, "UPING: send_ping: sendto failed on %d: %s", pptr->fd, msg)); uping_end(pptr); return; @@ -229,12 +305,12 @@ void uping_send(struct UPing* pptr) ++pptr->sent; } -/* - * read_ping +/** Read the response from an outbound uping. + * @param[in] pptr UPing to check. */ void uping_read(struct UPing* pptr) { - struct sockaddr_in sin; + struct irc_sockaddr sin; struct timeval tv; unsigned int len; unsigned int pingtime; @@ -253,19 +329,15 @@ void uping_read(struct UPing* pptr) const char* msg = strerror(errno); if (!msg) msg = "Unknown error"; - if (MyUser(pptr->client)) - sendto_one(pptr->client, ":%s NOTICE %s :UPING: receive error: %s", - me.name, pptr->client->name, msg); - else - sendto_one(pptr->client, "%s NOTICE %s%s :UPING: receive error: %s", - NumServ(&me), NumNick(pptr->client), msg); + sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: receive error: " + "%s", pptr->client, msg); uping_end(pptr); return; - } + } if (len < 19) return; /* Broken packet */ - + ++pptr->received; buf[len] = 0; @@ -277,11 +349,8 @@ void uping_read(struct UPing* pptr) pptr->ms_min = pingtime; if (pingtime > pptr->ms_max) pptr->ms_max = pingtime; - - pptr->timeout = CurrentTime + UPINGTIMEOUT; - Debug((DEBUG_SEND, "read_ping: %d bytes, ti %lu: [%s %s] %lu ms", - len, pptr->timeout, buf, (buf + strlen(buf) + 1), pingtime)); + timer_chg(&pptr->killer, TT_RELATIVE, UPINGTIMEOUT); s = pptr->buf + strlen(pptr->buf); sprintf(s, " %u", pingtime); @@ -291,57 +360,61 @@ void uping_read(struct UPing* pptr) return; } +/** Start sending upings to a server. + * @param[in] sptr Client requesting the upings. + * @param[in] aconf ConfItem containing the address to ping. + * @param[in] port Port number to ping. + * @param[in] count Number of times to ping (should be at least 20). + * @return Zero. + */ int uping_server(struct Client* sptr, struct ConfItem* aconf, int port, int count) { int fd; + int family = 0; struct UPing* pptr; + struct irc_sockaddr *local; assert(0 != sptr); assert(0 != aconf); - if (INADDR_NONE == aconf->ipnum.s_addr) { - if (MyUser(sptr)) - sendto_one(sptr, ":%s NOTICE %s :UPING: Host lookup failed for %s", - me.name, sptr->name, aconf->name); - else - sendto_one(sptr, "%s " TOK_NOTICE " %s%s :UPING: Host lookup failed for %s", - NumServ(&me), NumNick(sptr), aconf->name); + if (!irc_in_addr_valid(&aconf->address.addr)) { + sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Host lookup failed for " + "%s", sptr, aconf->name); + return 0; } if (IsUPing(sptr)) uping_cancel(sptr, sptr); /* Cancel previous ping request */ - if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { - if (MyUser(sptr)) - sendto_one(sptr, ":%s NOTICE %s :UPING: Unable to create udp ping socket", - me.name, sptr->name); - else - sendto_one(sptr, "%s " TOK_NOTICE " %s%s :UPING: Unable to create udp ping socket", - NumServ(&me), NumNick(sptr)); - return 0; + if (irc_in_addr_is_ipv4(&aconf->address.addr)) { + local = &VirtualHost_v4; + family = AF_INET; + } else { + local = &VirtualHost_v6; } - - if (!os_set_nonblocking(fd)) { - if (MyUser(sptr)) - sendto_one(sptr, ":%s NOTICE %s :UPING: Can't set fd non-blocking", - me.name, sptr->name); - else - sendto_one(sptr, "%s " TOK_NOTICE " %s%s :UPING: Can't set fd non-blocking", - NumServ(&me), NumNick(sptr)); - close(fd); + fd = os_socket(local, SOCK_DGRAM, "Outbound uping socket", family); + if (fd < 0) return 0; - } + pptr = (struct UPing*) MyMalloc(sizeof(struct UPing)); assert(0 != pptr); memset(pptr, 0, sizeof(struct UPing)); + if (!socket_add(&pptr->socket, uping_read_callback, (void*) pptr, + SS_DATAGRAM, SOCK_EVENT_READABLE, fd)) { + sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Can't queue fd for " + "reading", sptr); + close(fd); + MyFree(pptr); + return 0; + } + pptr->fd = fd; - pptr->sin.sin_port = htons(port); - pptr->sin.sin_addr.s_addr = aconf->ipnum.s_addr; - pptr->sin.sin_family = AF_INET; + memcpy(&pptr->addr.addr, &aconf->address.addr, sizeof(pptr->addr.addr)); + pptr->addr.port = port; pptr->count = IRCD_MIN(20, count); pptr->client = sptr; - pptr->index = -1; + pptr->freeable = UPING_PENDING_SOCKET; strcpy(pptr->name, aconf->name); pptr->next = pingList; @@ -352,78 +425,54 @@ int uping_server(struct Client* sptr, struct ConfItem* aconf, int port, int coun return 0; } - +/** Clean up a UPing structure, reporting results to the requester. + * @param[in,out] pptr UPing results. + */ void uping_end(struct UPing* pptr) { Debug((DEBUG_DEBUG, "uping_end: %p", pptr)); if (pptr->client) { - if (MyUser(pptr->client)) { - if (pptr->lastsent) { - if (0 < pptr->received) { - sendto_one(pptr->client, ":%s NOTICE %s :UPING %s%s", - me.name, pptr->client->name, pptr->name, pptr->buf); - /* - * XXX - warning long unsigned int format, unsigned int arg (7, 8, 9) - */ - sendto_one(pptr->client, - ":%s NOTICE %s :UPING Stats: sent %d recvd %d ; " - "min/avg/max = %1lu/%1lu/%1lu ms", - me.name, pptr->client->name, pptr->sent, - pptr->received, pptr->ms_min, - (2 * pptr->ms_ave) / (2 * pptr->received), - pptr->ms_max); - } - else - sendto_one(pptr->client, - ":%s NOTICE %s :UPING: no response from %s within %d seconds", - me.name, pptr->client->name, pptr->name, - UPINGTIMEOUT); - } - else - sendto_one(pptr->client, - ":%s NOTICE %s :UPING: Could not start ping to %s", - me.name, pptr->client->name, pptr->name); - } - else { - if (pptr->lastsent) { - if (0 < pptr->received) { - sendto_one(pptr->client, "%s NOTICE %s%s :UPING %s%s", - NumServ(&me), NumNick(pptr->client), pptr->name, pptr->buf); - /* XXX - warning: long unsigned int format, unsigned int arg(9, 10, 11) */ - sendto_one(pptr->client, - "%s " TOK_NOTICE " %s%s :UPING Stats: sent %d recvd %d ; " - "min/avg/max = %1lu/%1lu/%1lu ms", - NumServ(&me), NumNick(pptr->client), pptr->sent, - pptr->received, pptr->ms_min, - (2 * pptr->ms_ave) / (2 * pptr->received), - pptr->ms_max); - } - else - sendto_one(pptr->client, - "%s " TOK_NOTICE " %s%s :UPING: no response from %s within %d seconds", - NumServ(&me), NumNick(pptr->client), pptr->name, UPINGTIMEOUT); - } - else - sendto_one(pptr->client, - "%s " TOK_NOTICE " %s%s :UPING: Could not start ping to %s", - NumServ(&me), NumNick(pptr->client), pptr->name); - } + if (pptr->lastsent) { + if (0 < pptr->received) { + sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING %s%s", + pptr->client, pptr->name, pptr->buf); + sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING Stats: " + "sent %d recvd %d ; min/avg/max = %u/%u/%u ms", + pptr->client, pptr->sent, pptr->received, pptr->ms_min, + (2 * pptr->ms_ave) / (2 * pptr->received), pptr->ms_max); + } else + sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: no response " + "from %s within %d seconds", pptr->client, pptr->name, + UPINGTIMEOUT); + } else + sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: Could not " + "start ping to %s", pptr->client, pptr->name); } + close(pptr->fd); pptr->fd = -1; uping_erase(pptr); if (pptr->client) ClearUPing(pptr->client); - MyFree(pptr); + if (pptr->freeable & UPING_PENDING_SOCKET) + socket_del(&pptr->socket); + if (pptr->freeable & UPING_PENDING_SENDER) + timer_del(&pptr->sender); + if (pptr->freeable & UPING_PENDING_KILLER) + timer_del(&pptr->killer); } +/** Change notifications for any upings by \a sptr. + * @param[in] sptr Client to stop notifying. + * @param[in] acptr New client to notify (or NULL). + */ void uping_cancel(struct Client *sptr, struct Client* acptr) { struct UPing* ping; struct UPing* ping_next; - Debug((DEBUG_DEBUG, "UPING: cancelling uping for %s", sptr->name)); + Debug((DEBUG_DEBUG, "UPING: canceling uping for %s", cli_name(sptr))); for (ping = pingList; ping; ping = ping_next) { ping_next = ping->next; if (sptr == ping->client) { @@ -433,5 +482,3 @@ void uping_cancel(struct Client *sptr, struct Client* acptr) } ClearUPing(sptr); } - -