2 * IRC - Internet Relay Chat, ircd/uping.c
3 * Copyright (C) 1994 Carlo Wood ( Run @ undernet.org )
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "ircd_alloc.h"
27 #include "ircd_events.h"
29 #include "ircd_osdep.h"
30 #include "ircd_string.h"
35 #include "s_bsd.h" /* VirtualHost */
43 #include <arpa/inet.h>
50 #include <sys/socket.h>
54 #define UPINGTIMEOUT 60 /* Timeout waiting for ping responses */
57 #define INADDR_NONE 0xffffffff
60 static struct UPing* pingList = 0;
61 int UPingFileDescriptor = -1; /* UDP listener socket for upings */
63 static struct Socket upingSock;
66 * pings_begin - iterator function for ping list
68 struct UPing* uping_begin(void)
74 * pings_erase - removes ping struct from ping list
76 static void uping_erase(struct UPing* p)
79 struct UPing* last = 0;
83 for (it = pingList; it; last = it, it = it->next) {
94 /* Called when the event engine detects activity on the UPing socket */
95 static void uping_echo_callback(struct Event* ev)
97 assert(ev_type(ev) == ET_READ);
103 * Setup a UDP socket and listen for incoming packets
107 struct sockaddr_in from = { 0 };
110 memset(&from, 0, sizeof(from));
111 from.sin_addr = VirtualHost.sin_addr;
112 from.sin_port = htons(atoi(UDP_PORT));
113 from.sin_family = AF_INET;
115 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
116 Debug((DEBUG_ERROR, "UPING: UDP listener socket call failed: %s",
117 (strerror(errno)) ? strerror(errno) : "Unknown error"));
120 if (!os_set_reuseaddr(fd)) {
121 log_write(LS_SOCKET, L_ERROR, 0,
122 "UPING: set reuseaddr on UDP listener failed: %m (fd %d)", fd);
123 Debug((DEBUG_ERROR, "UPING: set reuseaddr on UDP listener failed: %s",
124 (strerror(errno)) ? strerror(errno) : "Unknown error"));
128 if (bind(fd, (struct sockaddr*) &from, sizeof(from)) == -1) {
129 log_write(LS_SOCKET, L_ERROR, 0,
130 "UPING: bind on UDP listener (%d fd %d) failed: %m",
131 htons(from.sin_port), fd);
132 Debug((DEBUG_ERROR, "UPING: bind on UDP listener failed : %s",
133 (strerror(errno)) ? strerror(errno) : "Unknown error"));
137 if (!os_set_nonblocking(fd)) {
138 Debug((DEBUG_ERROR, "UPING: set non-blocking: %s",
139 (strerror(errno)) ? strerror(errno) : "Unknown error"));
143 if (!socket_add(&upingSock, uping_echo_callback, 0, SS_DATAGRAM,
144 SOCK_EVENT_READABLE, fd)) {
145 Debug((DEBUG_ERROR, "UPING: Unable to queue fd to event system"));
149 UPingFileDescriptor = fd;
155 * max # of pings set to 15/sec.
159 struct sockaddr_in from = { 0 };
160 unsigned int len = 0;
161 static time_t last = 0;
162 static int counter = 0;
163 char buf[BUFSIZE + 1];
165 Debug((DEBUG_DEBUG, "UPING: uping_echo"));
167 if (IO_SUCCESS != os_recvfrom_nonb(UPingFileDescriptor, buf, BUFSIZE, &len, &from))
170 * count em even if we're getting flooded so we can tell we're getting
173 ++ServerStats->uping_recv;
174 if (CurrentTime == last) {
184 sendto(UPingFileDescriptor, buf, len, 0, (struct sockaddr*) &from, sizeof(from));
188 /* Callback when socket has data to read */
189 static void uping_read_callback(struct Event* ev)
193 assert(0 != ev_socket(ev));
194 assert(0 != s_data(ev_socket(ev)));
196 pptr = s_data(ev_socket(ev));
198 Debug((DEBUG_SEND, "uping_read_callback called, %p (%d)", pptr,
201 if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
202 pptr->freeable &= ~UPING_PENDING_SOCKET;
205 MyFree(pptr); /* done with it, finally */
207 assert(ev_type(ev) == ET_READ);
209 uping_read(pptr); /* read uping response */
213 /* Callback to send another ping */
214 static void uping_sender_callback(struct Event* ev)
218 assert(0 != ev_timer(ev));
219 assert(0 != t_data(ev_timer(ev)));
221 pptr = t_data(ev_timer(ev));
223 Debug((DEBUG_SEND, "uping_sender_callback called, %p (%d)", pptr,
226 if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
227 pptr->freeable &= ~UPING_PENDING_SENDER;
230 MyFree(pptr); /* done with it, finally */
232 assert(ev_type(ev) == ET_EXPIRE);
234 pptr->lastsent = CurrentTime; /* store last ping time */
235 uping_send(pptr); /* send a ping */
237 if (pptr->sent == pptr->count) /* done sending pings, don't send more */
238 timer_del(ev_timer(ev));
242 /* Callback to kill a ping */
243 static void uping_killer_callback(struct Event* ev)
247 assert(0 != ev_timer(ev));
248 assert(0 != t_data(ev_timer(ev)));
250 pptr = t_data(ev_timer(ev));
252 Debug((DEBUG_SEND, "uping_killer_callback called, %p (%d)", pptr,
255 if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
256 pptr->freeable &= ~UPING_PENDING_KILLER;
259 MyFree(pptr); /* done with it, finally */
261 assert(ev_type(ev) == ET_EXPIRE);
263 uping_end(pptr); /* <FUDD>kill the uping, kill the uping!</FUDD> */
270 static void uping_start(struct UPing* pptr)
274 timer_add(&pptr->sender, uping_sender_callback, (void*) pptr,
276 timer_add(&pptr->killer, uping_killer_callback, (void*) pptr,
277 TT_RELATIVE, UPINGTIMEOUT);
278 pptr->freeable |= UPING_PENDING_SENDER | UPING_PENDING_KILLER;
280 sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :Sending %d ping%s to %s",
281 pptr->client, pptr->count, (pptr->count == 1) ? "" : "s",
290 void uping_send(struct UPing* pptr)
293 char buf[BUFSIZE + 1];
296 if (pptr->sent == pptr->count)
298 memset(buf, 0, sizeof(buf));
300 gettimeofday(&tv, NULL);
301 sprintf(buf, " %10lu%c%6lu", tv.tv_sec, '\0', tv.tv_usec);
303 Debug((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d",
305 ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port),
308 if (sendto(pptr->fd, buf, BUFSIZE, 0, (struct sockaddr*) &pptr->sin,
309 sizeof(struct sockaddr_in)) != BUFSIZE)
311 const char* msg = strerror(errno);
313 msg = "Unknown error";
315 sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: send failed: "
316 "%s", pptr->client, msg);
317 Debug((DEBUG_DEBUG, "UPING: send_ping: sendto failed on %d: %s", pptr->fd, msg));
327 void uping_read(struct UPing* pptr)
329 struct sockaddr_in sin;
332 unsigned int pingtime;
334 char buf[BUFSIZE + 1];
339 gettimeofday(&tv, NULL);
341 ior = os_recvfrom_nonb(pptr->fd, buf, BUFSIZE, &len, &sin);
342 if (IO_BLOCKED == ior)
344 else if (IO_FAILURE == ior) {
345 const char* msg = strerror(errno);
347 msg = "Unknown error";
348 sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: receive error: "
349 "%s", pptr->client, msg);
355 return; /* Broken packet */
360 pingtime = (tv.tv_sec - atol(&buf[1])) * 1000
361 + (tv.tv_usec - atol(buf + strlen(buf) + 1)) / 1000;
363 pptr->ms_ave += pingtime;
364 if (!pptr->ms_min || pptr->ms_min > pingtime)
365 pptr->ms_min = pingtime;
366 if (pingtime > pptr->ms_max)
367 pptr->ms_max = pingtime;
369 timer_chg(&pptr->killer, TT_RELATIVE, UPINGTIMEOUT);
371 s = pptr->buf + strlen(pptr->buf);
372 sprintf(s, " %u", pingtime);
374 if (pptr->received == pptr->count)
379 int uping_server(struct Client* sptr, struct ConfItem* aconf, int port, int count)
387 if (INADDR_NONE == aconf->ipnum.s_addr) {
388 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Host lookup failed for "
389 "%s", sptr, aconf->name);
394 uping_cancel(sptr, sptr); /* Cancel previous ping request */
396 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
397 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Unable to create udp "
398 "ping socket", sptr);
402 if (!os_set_nonblocking(fd)) {
403 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Can't set fd non-"
408 pptr = (struct UPing*) MyMalloc(sizeof(struct UPing));
410 memset(pptr, 0, sizeof(struct UPing));
412 if (!socket_add(&pptr->socket, uping_read_callback, (void*) pptr,
413 SS_DATAGRAM, SOCK_EVENT_READABLE, fd)) {
414 sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Can't queue fd for "
422 pptr->sin.sin_port = htons(port);
423 pptr->sin.sin_addr.s_addr = aconf->ipnum.s_addr;
424 pptr->sin.sin_family = AF_INET;
425 pptr->count = IRCD_MIN(20, count);
428 pptr->freeable = UPING_PENDING_SOCKET;
429 strcpy(pptr->name, aconf->name);
431 pptr->next = pingList;
440 void uping_end(struct UPing* pptr)
442 Debug((DEBUG_DEBUG, "uping_end: %p", pptr));
445 if (pptr->lastsent) {
446 if (0 < pptr->received) {
447 sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING %s%s",
448 pptr->client, pptr->name, pptr->buf);
449 sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING Stats: "
450 "sent %d recvd %d ; min/avg/max = %1lu/%1lu/%1lu ms",
451 pptr->client, pptr->sent, pptr->received, pptr->ms_min,
452 (2 * pptr->ms_ave) / (2 * pptr->received), pptr->ms_max);
454 sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: no response "
455 "from %s within %d seconds", pptr->client, pptr->name,
458 sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: Could not "
459 "start ping to %s", pptr->client, pptr->name);
466 ClearUPing(pptr->client);
467 if (pptr->freeable & UPING_PENDING_SOCKET)
468 socket_del(&pptr->socket);
469 if (pptr->freeable & UPING_PENDING_SENDER)
470 timer_del(&pptr->sender);
471 if (pptr->freeable & UPING_PENDING_KILLER)
472 timer_del(&pptr->killer);
475 void uping_cancel(struct Client *sptr, struct Client* acptr)
478 struct UPing* ping_next;
480 Debug((DEBUG_DEBUG, "UPING: cancelling uping for %s", cli_name(sptr)));
481 for (ping = pingList; ping; ping = ping_next) {
482 ping_next = ping->next;
483 if (sptr == ping->client) {
484 ping->client = acptr;