]>
jfr.im git - irc/quakenet/snircd.git/blob - ircd/uping.c
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.
20 * @brief UDP ping implementation.
21 * @version $Id: uping.c,v 1.23 2005/03/20 16:06:30 entrope Exp $
28 #include "ircd_alloc.h"
29 #include "ircd_events.h"
31 #include "ircd_osdep.h"
32 #include "ircd_string.h"
37 #include "s_bsd.h" /* VirtualHost */
45 /* #include <assert.h> -- Now using assert in ircd_log.h */
51 #include <sys/socket.h>
55 #define UPINGTIMEOUT 60 /**< Timeout waiting for ping responses */
57 static struct UPing
* pingList
= 0; /**< Linked list of UPing structs */
58 static struct Socket upingSock_v4
; /**< Socket struct for IPv4 upings */
59 static struct Socket upingSock_v6
; /**< Socket struct for IPv6 upings */
61 /** Start iteration of uping list.
62 * @return Start of uping list.
64 struct UPing
* uping_begin(void)
69 /** Removes \a p from uping list.
70 * @param[in,out] p UPing to remove from list.
72 static void uping_erase(struct UPing
* p
)
75 struct UPing
* last
= 0;
79 for (it
= pingList
; it
; last
= it
, it
= it
->next
) {
90 /** Callback for uping listener socket.
91 * Reads a uping from the socket and respond, but not more than 10
93 * @param[in] ev I/O event for uping socket.
95 static void uping_echo_callback(struct Event
* ev
)
98 struct irc_sockaddr from
;
100 static time_t last
= 0;
101 static int counter
= 0;
102 char buf
[BUFSIZE
+ 1];
104 assert(ev_type(ev
) == ET_READ
|| ev_type(ev
) == ET_ERROR
);
105 sock
= ev_socket(ev
);
106 assert(sock
== &upingSock_v4
|| sock
== &upingSock_v6
);
108 Debug((DEBUG_DEBUG
, "UPING: uping_echo"));
110 if (IO_SUCCESS
!= os_recvfrom_nonb(s_fd(sock
), buf
, BUFSIZE
, &len
, &from
))
113 * count em even if we're getting flooded so we can tell we're getting
116 ++ServerStats
->uping_recv
;
119 else if (CurrentTime
!= last
) {
122 } else if (++counter
> 10)
124 os_sendto_nonb(s_fd(sock
), buf
, len
, NULL
, 0, &from
);
127 /** Initialize a UDP socket for upings.
128 * @returns 0 on success, -1 on error.
132 struct irc_sockaddr from
;
135 memcpy(&from
, &VirtualHost_v4
, sizeof(from
));
136 from
.port
= atoi(UDP_PORT
);
138 fd
= os_socket(&from
, SOCK_DGRAM
, "IPv4 uping listener");
141 if (!socket_add(&upingSock_v4
, uping_echo_callback
, 0, SS_DATAGRAM
,
142 SOCK_EVENT_READABLE
, fd
)) {
143 Debug((DEBUG_ERROR
, "UPING: Unable to queue fd to event system"));
148 memcpy(&from
, &VirtualHost_v6
, sizeof(from
));
149 from
.port
= atoi(UDP_PORT
);
151 fd
= os_socket(&from
, SOCK_DGRAM
, "IPv6 uping listener");
154 if (!socket_add(&upingSock_v6
, uping_echo_callback
, 0, SS_DATAGRAM
,
155 SOCK_EVENT_READABLE
, fd
)) {
156 Debug((DEBUG_ERROR
, "UPING: Unable to queue fd to event system"));
165 /** Callback for socket activity on an outbound uping socket.
166 * @param[in] ev I/O event for socket.
168 static void uping_read_callback(struct Event
* ev
)
172 assert(0 != ev_socket(ev
));
173 assert(0 != s_data(ev_socket(ev
)));
175 pptr
= (struct UPing
*) s_data(ev_socket(ev
));
177 Debug((DEBUG_SEND
, "uping_read_callback called, %p (%d)", pptr
,
180 if (ev_type(ev
) == ET_DESTROY
) { /* being destroyed */
181 pptr
->freeable
&= ~UPING_PENDING_SOCKET
;
184 MyFree(pptr
); /* done with it, finally */
186 assert(ev_type(ev
) == ET_READ
|| ev_type(ev
) == ET_ERROR
);
188 uping_read(pptr
); /* read uping response */
192 /** Timer callback to send another outbound uping.
193 * @param[in] ev Event for uping timer.
195 static void uping_sender_callback(struct Event
* ev
)
199 assert(0 != ev_timer(ev
));
200 assert(0 != t_data(ev_timer(ev
)));
202 pptr
= (struct UPing
*) t_data(ev_timer(ev
));
204 Debug((DEBUG_SEND
, "uping_sender_callback called, %p (%d)", pptr
,
207 if (ev_type(ev
) == ET_DESTROY
) { /* being destroyed */
208 pptr
->freeable
&= ~UPING_PENDING_SENDER
;
211 MyFree(pptr
); /* done with it, finally */
213 assert(ev_type(ev
) == ET_EXPIRE
);
215 pptr
->lastsent
= CurrentTime
; /* store last ping time */
216 uping_send(pptr
); /* send a ping */
218 if (pptr
->sent
== pptr
->count
) /* done sending pings, don't send more */
219 timer_del(ev_timer(ev
));
223 /** Timer callback to stop upings.
224 * @param[in] ev Event for uping expiration.
226 static void uping_killer_callback(struct Event
* ev
)
230 assert(0 != ev_timer(ev
));
231 assert(0 != t_data(ev_timer(ev
)));
233 pptr
= (struct UPing
*) t_data(ev_timer(ev
));
235 Debug((DEBUG_SEND
, "uping_killer_callback called, %p (%d)", pptr
,
238 if (ev_type(ev
) == ET_DESTROY
) { /* being destroyed */
239 pptr
->freeable
&= ~UPING_PENDING_KILLER
;
242 MyFree(pptr
); /* done with it, finally */
244 assert(ev_type(ev
) == ET_EXPIRE
);
246 uping_end(pptr
); /* <FUDD>kill the uping, kill the uping!</FUDD> */
251 * This sets up the timers, UPing flags, and sends a notice to the
254 static void uping_start(struct UPing
* pptr
)
258 timer_add(timer_init(&pptr
->sender
), uping_sender_callback
, (void*) pptr
,
260 timer_add(timer_init(&pptr
->killer
), uping_killer_callback
, (void*) pptr
,
261 TT_RELATIVE
, UPINGTIMEOUT
);
262 pptr
->freeable
|= UPING_PENDING_SENDER
| UPING_PENDING_KILLER
;
264 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :Sending %d ping%s to %s",
265 pptr
->client
, pptr
->count
, (pptr
->count
== 1) ? "" : "s",
270 /** Send a uping to another server.
271 * @param[in] pptr Descriptor for uping.
273 void uping_send(struct UPing
* pptr
)
276 char buf
[BUFSIZE
+ 1];
279 if (pptr
->sent
== pptr
->count
)
281 memset(buf
, 0, sizeof(buf
));
283 gettimeofday(&tv
, NULL
);
284 sprintf(buf
, " %10lu%c%6lu", (unsigned long)tv
.tv_sec
, '\0', (unsigned long)tv
.tv_usec
);
286 Debug((DEBUG_SEND
, "send_ping: sending [%s %s] to %s.%d on %d",
288 ircd_ntoa(&pptr
->addr
.addr
), pptr
->addr
.port
,
291 if (os_sendto_nonb(pptr
->fd
, buf
, BUFSIZE
, NULL
, 0, &pptr
->addr
) != IO_SUCCESS
)
293 const char* msg
= strerror(errno
);
295 msg
= "Unknown error";
297 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :UPING: send failed: "
298 "%s", pptr
->client
, msg
);
299 Debug((DEBUG_DEBUG
, "UPING: send_ping: sendto failed on %d: %s", pptr
->fd
, msg
));
306 /** Read the response from an outbound uping.
307 * @param[in] pptr UPing to check.
309 void uping_read(struct UPing
* pptr
)
311 struct irc_sockaddr sin
;
314 unsigned int pingtime
;
316 char buf
[BUFSIZE
+ 1];
321 gettimeofday(&tv
, NULL
);
323 ior
= os_recvfrom_nonb(pptr
->fd
, buf
, BUFSIZE
, &len
, &sin
);
324 if (IO_BLOCKED
== ior
)
326 else if (IO_FAILURE
== ior
) {
327 const char* msg
= strerror(errno
);
329 msg
= "Unknown error";
330 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :UPING: receive error: "
331 "%s", pptr
->client
, msg
);
337 return; /* Broken packet */
342 pingtime
= (tv
.tv_sec
- atol(&buf
[1])) * 1000
343 + (tv
.tv_usec
- atol(buf
+ strlen(buf
) + 1)) / 1000;
345 pptr
->ms_ave
+= pingtime
;
346 if (!pptr
->ms_min
|| pptr
->ms_min
> pingtime
)
347 pptr
->ms_min
= pingtime
;
348 if (pingtime
> pptr
->ms_max
)
349 pptr
->ms_max
= pingtime
;
351 timer_chg(&pptr
->killer
, TT_RELATIVE
, UPINGTIMEOUT
);
353 s
= pptr
->buf
+ strlen(pptr
->buf
);
354 sprintf(s
, " %u", pingtime
);
356 if (pptr
->received
== pptr
->count
)
361 /** Start sending upings to a server.
362 * @param[in] sptr Client requesting the upings.
363 * @param[in] aconf ConfItem containing the address to ping.
364 * @param[in] port Port number to ping.
365 * @param[in] count Number of times to ping (should be at least 20).
368 int uping_server(struct Client
* sptr
, struct ConfItem
* aconf
, int port
, int count
)
372 struct irc_sockaddr
*local
;
377 if (!irc_in_addr_valid(&aconf
->address
.addr
)) {
378 sendcmdto_one(&me
, CMD_NOTICE
, sptr
, "%C :UPING: Host lookup failed for "
379 "%s", sptr
, aconf
->name
);
384 uping_cancel(sptr
, sptr
); /* Cancel previous ping request */
386 local
= irc_in_addr_is_ipv4(&aconf
->address
.addr
) ? &VirtualHost_v4
: &VirtualHost_v6
;
387 fd
= os_socket(local
, SOCK_DGRAM
, "Outbound uping socket");
391 pptr
= (struct UPing
*) MyMalloc(sizeof(struct UPing
));
393 memset(pptr
, 0, sizeof(struct UPing
));
395 if (!socket_add(&pptr
->socket
, uping_read_callback
, (void*) pptr
,
396 SS_DATAGRAM
, SOCK_EVENT_READABLE
, fd
)) {
397 sendcmdto_one(&me
, CMD_NOTICE
, sptr
, "%C :UPING: Can't queue fd for "
405 memcpy(&pptr
->addr
.addr
, &aconf
->address
.addr
, sizeof(pptr
->addr
.addr
));
406 pptr
->addr
.port
= port
;
407 pptr
->count
= IRCD_MIN(20, count
);
409 pptr
->freeable
= UPING_PENDING_SOCKET
;
410 strcpy(pptr
->name
, aconf
->name
);
412 pptr
->next
= pingList
;
420 /** Clean up a UPing structure, reporting results to the requester.
421 * @param[in,out] pptr UPing results.
423 void uping_end(struct UPing
* pptr
)
425 Debug((DEBUG_DEBUG
, "uping_end: %p", pptr
));
428 if (pptr
->lastsent
) {
429 if (0 < pptr
->received
) {
430 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :UPING %s%s",
431 pptr
->client
, pptr
->name
, pptr
->buf
);
432 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :UPING Stats: "
433 "sent %d recvd %d ; min/avg/max = %1lu/%1lu/%1lu ms",
434 pptr
->client
, pptr
->sent
, pptr
->received
, pptr
->ms_min
,
435 (2 * pptr
->ms_ave
) / (2 * pptr
->received
), pptr
->ms_max
);
437 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :UPING: no response "
438 "from %s within %d seconds", pptr
->client
, pptr
->name
,
441 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :UPING: Could not "
442 "start ping to %s", pptr
->client
, pptr
->name
);
449 ClearUPing(pptr
->client
);
450 if (pptr
->freeable
& UPING_PENDING_SOCKET
)
451 socket_del(&pptr
->socket
);
452 if (pptr
->freeable
& UPING_PENDING_SENDER
)
453 timer_del(&pptr
->sender
);
454 if (pptr
->freeable
& UPING_PENDING_KILLER
)
455 timer_del(&pptr
->killer
);
458 /** Change notifications for any upings by \a sptr.
459 * @param[in] sptr Client to stop notifying.
460 * @param[in] acptr New client to notify (or NULL).
462 void uping_cancel(struct Client
*sptr
, struct Client
* acptr
)
465 struct UPing
* ping_next
;
467 Debug((DEBUG_DEBUG
, "UPING: canceling uping for %s", cli_name(sptr
)));
468 for (ping
= pingList
; ping
; ping
= ping_next
) {
469 ping_next
= ping
->next
;
470 if (sptr
== ping
->client
) {
471 ping
->client
= acptr
;