]>
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.2.1 2006/03/14 03:45:52 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", AF_INET
);
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"));
149 memcpy(&from
, &VirtualHost_v6
, sizeof(from
));
150 from
.port
= atoi(UDP_PORT
);
152 fd
= os_socket(&from
, SOCK_DGRAM
, "IPv6 uping listener", AF_INET6
);
155 if (!socket_add(&upingSock_v6
, uping_echo_callback
, 0, SS_DATAGRAM
,
156 SOCK_EVENT_READABLE
, fd
)) {
157 Debug((DEBUG_ERROR
, "UPING: Unable to queue fd to event system"));
167 /** Callback for socket activity on an outbound uping socket.
168 * @param[in] ev I/O event for socket.
170 static void uping_read_callback(struct Event
* ev
)
174 assert(0 != ev_socket(ev
));
175 assert(0 != s_data(ev_socket(ev
)));
177 pptr
= (struct UPing
*) s_data(ev_socket(ev
));
179 Debug((DEBUG_SEND
, "uping_read_callback called, %p (%d)", pptr
,
182 if (ev_type(ev
) == ET_DESTROY
) { /* being destroyed */
183 pptr
->freeable
&= ~UPING_PENDING_SOCKET
;
186 MyFree(pptr
); /* done with it, finally */
188 assert(ev_type(ev
) == ET_READ
|| ev_type(ev
) == ET_ERROR
);
190 uping_read(pptr
); /* read uping response */
194 /** Timer callback to send another outbound uping.
195 * @param[in] ev Event for uping timer.
197 static void uping_sender_callback(struct Event
* ev
)
201 assert(0 != ev_timer(ev
));
202 assert(0 != t_data(ev_timer(ev
)));
204 pptr
= (struct UPing
*) t_data(ev_timer(ev
));
206 Debug((DEBUG_SEND
, "uping_sender_callback called, %p (%d)", pptr
,
209 if (ev_type(ev
) == ET_DESTROY
) { /* being destroyed */
210 pptr
->freeable
&= ~UPING_PENDING_SENDER
;
213 MyFree(pptr
); /* done with it, finally */
215 assert(ev_type(ev
) == ET_EXPIRE
);
217 pptr
->lastsent
= CurrentTime
; /* store last ping time */
218 uping_send(pptr
); /* send a ping */
220 if (pptr
->sent
== pptr
->count
) /* done sending pings, don't send more */
221 timer_del(ev_timer(ev
));
225 /** Timer callback to stop upings.
226 * @param[in] ev Event for uping expiration.
228 static void uping_killer_callback(struct Event
* ev
)
232 assert(0 != ev_timer(ev
));
233 assert(0 != t_data(ev_timer(ev
)));
235 pptr
= (struct UPing
*) t_data(ev_timer(ev
));
237 Debug((DEBUG_SEND
, "uping_killer_callback called, %p (%d)", pptr
,
240 if (ev_type(ev
) == ET_DESTROY
) { /* being destroyed */
241 pptr
->freeable
&= ~UPING_PENDING_KILLER
;
244 MyFree(pptr
); /* done with it, finally */
246 assert(ev_type(ev
) == ET_EXPIRE
);
248 uping_end(pptr
); /* <FUDD>kill the uping, kill the uping!</FUDD> */
253 * This sets up the timers, UPing flags, and sends a notice to the
256 static void uping_start(struct UPing
* pptr
)
260 timer_add(timer_init(&pptr
->sender
), uping_sender_callback
, (void*) pptr
,
262 timer_add(timer_init(&pptr
->killer
), uping_killer_callback
, (void*) pptr
,
263 TT_RELATIVE
, UPINGTIMEOUT
);
264 pptr
->freeable
|= UPING_PENDING_SENDER
| UPING_PENDING_KILLER
;
266 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :Sending %d ping%s to %s",
267 pptr
->client
, pptr
->count
, (pptr
->count
== 1) ? "" : "s",
272 /** Send a uping to another server.
273 * @param[in] pptr Descriptor for uping.
275 void uping_send(struct UPing
* pptr
)
278 char buf
[BUFSIZE
+ 1];
281 if (pptr
->sent
== pptr
->count
)
283 memset(buf
, 0, sizeof(buf
));
285 gettimeofday(&tv
, NULL
);
286 sprintf(buf
, " %10lu%c%6lu", (unsigned long)tv
.tv_sec
, '\0', (unsigned long)tv
.tv_usec
);
288 Debug((DEBUG_SEND
, "send_ping: sending [%s %s] to %s.%d on %d",
290 ircd_ntoa(&pptr
->addr
.addr
), pptr
->addr
.port
,
293 if (os_sendto_nonb(pptr
->fd
, buf
, BUFSIZE
, NULL
, 0, &pptr
->addr
) != IO_SUCCESS
)
295 const char* msg
= strerror(errno
);
297 msg
= "Unknown error";
299 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :UPING: send failed: "
300 "%s", pptr
->client
, msg
);
301 Debug((DEBUG_DEBUG
, "UPING: send_ping: sendto failed on %d: %s", pptr
->fd
, msg
));
308 /** Read the response from an outbound uping.
309 * @param[in] pptr UPing to check.
311 void uping_read(struct UPing
* pptr
)
313 struct irc_sockaddr sin
;
316 unsigned int pingtime
;
318 char buf
[BUFSIZE
+ 1];
323 gettimeofday(&tv
, NULL
);
325 ior
= os_recvfrom_nonb(pptr
->fd
, buf
, BUFSIZE
, &len
, &sin
);
326 if (IO_BLOCKED
== ior
)
328 else if (IO_FAILURE
== ior
) {
329 const char* msg
= strerror(errno
);
331 msg
= "Unknown error";
332 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :UPING: receive error: "
333 "%s", pptr
->client
, msg
);
339 return; /* Broken packet */
344 pingtime
= (tv
.tv_sec
- atol(&buf
[1])) * 1000
345 + (tv
.tv_usec
- atol(buf
+ strlen(buf
) + 1)) / 1000;
347 pptr
->ms_ave
+= pingtime
;
348 if (!pptr
->ms_min
|| pptr
->ms_min
> pingtime
)
349 pptr
->ms_min
= pingtime
;
350 if (pingtime
> pptr
->ms_max
)
351 pptr
->ms_max
= pingtime
;
353 timer_chg(&pptr
->killer
, TT_RELATIVE
, UPINGTIMEOUT
);
355 s
= pptr
->buf
+ strlen(pptr
->buf
);
356 sprintf(s
, " %u", pingtime
);
358 if (pptr
->received
== pptr
->count
)
363 /** Start sending upings to a server.
364 * @param[in] sptr Client requesting the upings.
365 * @param[in] aconf ConfItem containing the address to ping.
366 * @param[in] port Port number to ping.
367 * @param[in] count Number of times to ping (should be at least 20).
370 int uping_server(struct Client
* sptr
, struct ConfItem
* aconf
, int port
, int count
)
375 struct irc_sockaddr
*local
;
380 if (!irc_in_addr_valid(&aconf
->address
.addr
)) {
381 sendcmdto_one(&me
, CMD_NOTICE
, sptr
, "%C :UPING: Host lookup failed for "
382 "%s", sptr
, aconf
->name
);
387 uping_cancel(sptr
, sptr
); /* Cancel previous ping request */
389 if (irc_in_addr_is_ipv4(&aconf
->address
.addr
)) {
390 local
= &VirtualHost_v4
;
393 local
= &VirtualHost_v6
;
395 fd
= os_socket(local
, SOCK_DGRAM
, "Outbound uping socket", family
);
399 pptr
= (struct UPing
*) MyMalloc(sizeof(struct UPing
));
401 memset(pptr
, 0, sizeof(struct UPing
));
403 if (!socket_add(&pptr
->socket
, uping_read_callback
, (void*) pptr
,
404 SS_DATAGRAM
, SOCK_EVENT_READABLE
, fd
)) {
405 sendcmdto_one(&me
, CMD_NOTICE
, sptr
, "%C :UPING: Can't queue fd for "
413 memcpy(&pptr
->addr
.addr
, &aconf
->address
.addr
, sizeof(pptr
->addr
.addr
));
414 pptr
->addr
.port
= port
;
415 pptr
->count
= IRCD_MIN(20, count
);
417 pptr
->freeable
= UPING_PENDING_SOCKET
;
418 strcpy(pptr
->name
, aconf
->name
);
420 pptr
->next
= pingList
;
428 /** Clean up a UPing structure, reporting results to the requester.
429 * @param[in,out] pptr UPing results.
431 void uping_end(struct UPing
* pptr
)
433 Debug((DEBUG_DEBUG
, "uping_end: %p", pptr
));
436 if (pptr
->lastsent
) {
437 if (0 < pptr
->received
) {
438 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :UPING %s%s",
439 pptr
->client
, pptr
->name
, pptr
->buf
);
440 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :UPING Stats: "
441 "sent %d recvd %d ; min/avg/max = %u/%u/%u ms",
442 pptr
->client
, pptr
->sent
, pptr
->received
, pptr
->ms_min
,
443 (2 * pptr
->ms_ave
) / (2 * pptr
->received
), pptr
->ms_max
);
445 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :UPING: no response "
446 "from %s within %d seconds", pptr
->client
, pptr
->name
,
449 sendcmdto_one(&me
, CMD_NOTICE
, pptr
->client
, "%C :UPING: Could not "
450 "start ping to %s", pptr
->client
, pptr
->name
);
457 ClearUPing(pptr
->client
);
458 if (pptr
->freeable
& UPING_PENDING_SOCKET
)
459 socket_del(&pptr
->socket
);
460 if (pptr
->freeable
& UPING_PENDING_SENDER
)
461 timer_del(&pptr
->sender
);
462 if (pptr
->freeable
& UPING_PENDING_KILLER
)
463 timer_del(&pptr
->killer
);
466 /** Change notifications for any upings by \a sptr.
467 * @param[in] sptr Client to stop notifying.
468 * @param[in] acptr New client to notify (or NULL).
470 void uping_cancel(struct Client
*sptr
, struct Client
* acptr
)
473 struct UPing
* ping_next
;
475 Debug((DEBUG_DEBUG
, "UPING: canceling uping for %s", cli_name(sptr
)));
476 for (ping
= pingList
; ping
; ping
= ping_next
) {
477 ping_next
= ping
->next
;
478 if (sptr
== ping
->client
) {
479 ping
->client
= acptr
;