Improve support for IPv4 vs IPv6 virtual hosts (fixes SF bugs #1087699, #1087668).
[ircu2.10.12-pk.git] / ircd / uping.c
1 /*
2  * IRC - Internet Relay Chat, ircd/uping.c
3  * Copyright (C) 1994 Carlo Wood ( Run @ undernet.org )
4  *
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)
8  * any later version.
9  *
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.
14  *
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.
18  */
19 /** @file
20  * @brief UDP ping implementation.
21  * @version $Id$
22  */
23 #include "config.h"
24
25 #include "uping.h"
26 #include "client.h"
27 #include "ircd.h"
28 #include "ircd_alloc.h"
29 #include "ircd_events.h"
30 #include "ircd_log.h"
31 #include "ircd_osdep.h"
32 #include "ircd_string.h"
33 #include "match.h"
34 #include "msg.h"
35 #include "numeric.h"
36 #include "numnicks.h"
37 #include "s_bsd.h"    /* VirtualHost */
38 #include "s_conf.h"
39 #include "s_debug.h"
40 #include "s_misc.h"
41 #include "s_user.h"
42 #include "send.h"
43 #include "sys.h"
44
45 /* #include <assert.h> -- Now using assert in ircd_log.h */
46 #include <errno.h>
47 #include <netdb.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <sys/socket.h>
52 #include <sys/time.h>
53 #include <unistd.h>
54
55 #define UPINGTIMEOUT 60   /**< Timeout waiting for ping responses */
56
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 */
60
61 /** Start iteration of uping list.
62  * @return Start of uping list.
63  */
64 struct UPing* uping_begin(void)
65 {
66   return pingList;
67 }
68
69 /** Removes \a p from uping list.
70  * @param[in,out] p UPing to remove from list.
71  */
72 static void uping_erase(struct UPing* p)
73 {
74   struct UPing* it;
75   struct UPing* last = 0;
76
77   assert(0 != p);
78
79   for (it = pingList; it; last = it, it = it->next) {
80     if (p == it) {
81       if (last)
82         last->next = p->next;
83       else
84         pingList = p->next;
85       break;
86     }
87   }
88 }
89
90 /** Callback for uping listener socket.
91  * Reads a uping from the socket and respond, but not more than 10
92  * times per second.
93  * @param[in] ev I/O event for uping socket.
94  */
95 static void uping_echo_callback(struct Event* ev)
96 {
97   struct Socket      *sock;
98   struct irc_sockaddr from;
99   unsigned int       len = 0;
100   static time_t      last = 0;
101   static int         counter = 0;
102   char               buf[BUFSIZE + 1];
103
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);
107
108   Debug((DEBUG_DEBUG, "UPING: uping_echo"));
109
110   if (IO_SUCCESS != os_recvfrom_nonb(s_fd(sock), buf, BUFSIZE, &len, &from))
111     return;
112   /*
113    * count em even if we're getting flooded so we can tell we're getting
114    * flooded.
115    */
116   ++ServerStats->uping_recv;
117   if (len < 19)
118     return;
119   else if (CurrentTime != last) {
120     counter = 0;
121     last = CurrentTime;
122   } else if (++counter > 10)
123     return;
124   os_sendto_nonb(s_fd(sock), buf, len, NULL, 0, &from);
125 }
126
127 /** Initialize a UDP socket for upings.
128  * @returns 0 on success, -1 on error.
129  */
130 int uping_init(void)
131 {
132   struct irc_sockaddr from;
133   int fd;
134
135   memcpy(&from, &VirtualHost_v4, sizeof(from));
136   from.port = atoi(UDP_PORT);
137
138   fd = os_socket(&from, SOCK_DGRAM, "IPv4 uping listener");
139   if (fd < 0)
140     return -1;
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"));
144     close(fd);
145     return -1;
146   }
147
148   memcpy(&from, &VirtualHost_v6, sizeof(from));
149   from.port = atoi(UDP_PORT);
150
151   fd = os_socket(&from, SOCK_DGRAM, "IPv6 uping listener");
152   if (fd < 0)
153     return -1;
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"));
157     close(fd);
158     return -1;
159   }
160
161   return 0;
162 }
163
164
165 /** Callback for socket activity on an outbound uping socket.
166  * @param[in] ev I/O event for socket.
167  */
168 static void uping_read_callback(struct Event* ev)
169 {
170   struct UPing *pptr;
171
172   assert(0 != ev_socket(ev));
173   assert(0 != s_data(ev_socket(ev)));
174
175   pptr = (struct UPing*) s_data(ev_socket(ev));
176
177   Debug((DEBUG_SEND, "uping_read_callback called, %p (%d)", pptr,
178          ev_type(ev)));
179
180   if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
181     pptr->freeable &= ~UPING_PENDING_SOCKET;
182
183     if (!pptr->freeable)
184       MyFree(pptr); /* done with it, finally */
185   } else {
186     assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
187
188     uping_read(pptr); /* read uping response */
189   }
190 }
191
192 /** Timer callback to send another outbound uping.
193  * @param[in] ev Event for uping timer.
194  */
195 static void uping_sender_callback(struct Event* ev)
196 {
197   struct UPing *pptr;
198
199   assert(0 != ev_timer(ev));
200   assert(0 != t_data(ev_timer(ev)));
201
202   pptr = (struct UPing*) t_data(ev_timer(ev));
203
204   Debug((DEBUG_SEND, "uping_sender_callback called, %p (%d)", pptr,
205          ev_type(ev)));
206
207   if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
208     pptr->freeable &= ~UPING_PENDING_SENDER;
209
210     if (!pptr->freeable)
211       MyFree(pptr); /* done with it, finally */
212   } else {
213     assert(ev_type(ev) == ET_EXPIRE);
214
215     pptr->lastsent = CurrentTime; /* store last ping time */
216     uping_send(pptr); /* send a ping */
217
218     if (pptr->sent == pptr->count) /* done sending pings, don't send more */
219       timer_del(ev_timer(ev));
220   }
221 }
222
223 /** Timer callback to stop upings.
224  * @param[in] ev Event for uping expiration.
225  */
226 static void uping_killer_callback(struct Event* ev)
227 {
228   struct UPing *pptr;
229
230   assert(0 != ev_timer(ev));
231   assert(0 != t_data(ev_timer(ev)));
232
233   pptr = (struct UPing*) t_data(ev_timer(ev));
234
235   Debug((DEBUG_SEND, "uping_killer_callback called, %p (%d)", pptr,
236          ev_type(ev)));
237
238   if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
239     pptr->freeable &= ~UPING_PENDING_KILLER;
240
241     if (!pptr->freeable)
242       MyFree(pptr); /* done with it, finally */
243   } else {
244     assert(ev_type(ev) == ET_EXPIRE);
245
246     uping_end(pptr); /* <FUDD>kill the uping, kill the uping!</FUDD> */
247   }
248 }
249
250 /** Start a uping.
251  * This sets up the timers, UPing flags, and sends a notice to the
252  * requesting client.
253  */
254 static void uping_start(struct UPing* pptr)
255 {
256   assert(0 != pptr);
257
258   timer_add(timer_init(&pptr->sender), uping_sender_callback, (void*) pptr,
259             TT_PERIODIC, 1);
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;
263
264   sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :Sending %d ping%s to %s",
265                 pptr->client, pptr->count, (pptr->count == 1) ? "" : "s",
266                 pptr->name);
267   pptr->active = 1;
268 }
269
270 /** Send a uping to another server.
271  * @param[in] pptr Descriptor for uping.
272  */
273 void uping_send(struct UPing* pptr)
274 {
275   struct timeval tv;
276   char buf[BUFSIZE + 1];
277
278   assert(0 != pptr);
279   if (pptr->sent == pptr->count)
280     return;
281   memset(buf, 0, sizeof(buf));
282
283   gettimeofday(&tv, NULL);
284   sprintf(buf, " %10lu%c%6lu", (unsigned long)tv.tv_sec, '\0', (unsigned long)tv.tv_usec);
285
286   Debug((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d",
287           buf, &buf[12],
288           ircd_ntoa(&pptr->addr.addr), pptr->addr.port,
289           pptr->fd));
290
291   if (os_sendto_nonb(pptr->fd, buf, BUFSIZE, NULL, 0, &pptr->addr) != IO_SUCCESS)
292   {
293     const char* msg = strerror(errno);
294     if (!msg)
295       msg = "Unknown error";
296     if (pptr->client)
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));
300     uping_end(pptr);
301     return;
302   }
303   ++pptr->sent;
304 }
305
306 /** Read the response from an outbound uping.
307  * @param[in] pptr UPing to check.
308  */
309 void uping_read(struct UPing* pptr)
310 {
311   struct irc_sockaddr sin;
312   struct timeval     tv;
313   unsigned int       len;
314   unsigned int       pingtime;
315   char*              s;
316   char               buf[BUFSIZE + 1];
317   IOResult           ior;
318
319   assert(0 != pptr);
320
321   gettimeofday(&tv, NULL);
322
323   ior = os_recvfrom_nonb(pptr->fd, buf, BUFSIZE, &len, &sin);
324   if (IO_BLOCKED == ior)
325     return;
326   else if (IO_FAILURE == ior) {
327     const char* msg = strerror(errno);
328     if (!msg)
329       msg = "Unknown error";
330     sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: receive error: "
331                   "%s", pptr->client, msg);
332     uping_end(pptr);
333     return;
334   }
335
336   if (len < 19)
337     return;                     /* Broken packet */
338
339   ++pptr->received;
340
341   buf[len] = 0;
342   pingtime = (tv.tv_sec - atol(&buf[1])) * 1000
343              + (tv.tv_usec - atol(buf + strlen(buf) + 1)) / 1000;
344
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;
350
351   timer_chg(&pptr->killer, TT_RELATIVE, UPINGTIMEOUT);
352
353   s = pptr->buf + strlen(pptr->buf);
354   sprintf(s, " %u", pingtime);
355
356   if (pptr->received == pptr->count)
357     uping_end(pptr);
358   return;
359 }
360
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).
366  * @return Zero.
367  */
368 int uping_server(struct Client* sptr, struct ConfItem* aconf, int port, int count)
369 {
370   int fd;
371   struct UPing* pptr;
372   struct irc_sockaddr *local;
373
374   assert(0 != sptr);
375   assert(0 != aconf);
376
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);
380     return 0;
381   }
382
383   if (IsUPing(sptr))
384     uping_cancel(sptr, sptr);  /* Cancel previous ping request */
385
386   local = irc_in_addr_is_ipv4(&aconf->address.addr) ? &VirtualHost_v4 : &VirtualHost_v6;
387   fd = os_socket(local, SOCK_DGRAM, "Outbound uping socket");
388   if (fd < 0)
389     return 0;
390
391   pptr = (struct UPing*) MyMalloc(sizeof(struct UPing));
392   assert(0 != pptr);
393   memset(pptr, 0, sizeof(struct UPing));
394
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 "
398                   "reading", sptr);
399     close(fd);
400     MyFree(pptr);
401     return 0;
402   }
403
404   pptr->fd                  = fd;
405   memcpy(&pptr->addr.addr, &aconf->address.addr, sizeof(pptr->addr.addr));
406   pptr->addr.port           = port;
407   pptr->count               = IRCD_MIN(20, count);
408   pptr->client              = sptr;
409   pptr->freeable            = UPING_PENDING_SOCKET;
410   strcpy(pptr->name, aconf->name);
411
412   pptr->next = pingList;
413   pingList   = pptr;
414
415   SetUPing(sptr);
416   uping_start(pptr);
417   return 0;
418 }
419
420 /** Clean up a UPing structure, reporting results to the requester.
421  * @param[in,out] pptr UPing results.
422  */
423 void uping_end(struct UPing* pptr)
424 {
425   Debug((DEBUG_DEBUG, "uping_end: %p", pptr));
426
427   if (pptr->client) {
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);
436       } else
437         sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: no response "
438                       "from %s within %d seconds", pptr->client, pptr->name,
439                       UPINGTIMEOUT);
440     } else
441       sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: Could not "
442                     "start ping to %s", pptr->client, pptr->name);
443   }
444
445   close(pptr->fd);
446   pptr->fd = -1;
447   uping_erase(pptr);
448   if (pptr->client)
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);
456 }
457
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).
461  */
462 void uping_cancel(struct Client *sptr, struct Client* acptr)
463 {
464   struct UPing* ping;
465   struct UPing* ping_next;
466
467   Debug((DEBUG_DEBUG, "UPING: cancelling 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;
472       uping_end(ping);
473     }
474   }
475   ClearUPing(sptr);
476 }
477
478