Author: Bleep <tomh@inxpress.net>
[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  * $Id$
20  */
21 #include "uping.h"
22 #include "client.h"
23 #include "ircd.h"
24 #include "ircd_alloc.h"
25 #include "ircd_log.h"
26 #include "ircd_osdep.h"
27 #include "ircd_string.h"
28 #include "match.h"
29 #include "numeric.h"
30 #include "numnicks.h"
31 #include "res.h"
32 #include "s_bsd.h"    /* vserv */
33 #include "s_conf.h"
34 #include "s_debug.h"
35 #include "s_misc.h"
36 #include "s_user.h"
37 #include "send.h"
38 #include "sys.h"
39
40 #include <arpa/inet.h>
41 #include <assert.h>
42 #include <errno.h>
43 #include <netdb.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <sys/socket.h>
48 #include <sys/time.h>
49 #include <unistd.h>
50
51 #define UPINGTIMEOUT 60   /* Timeout waiting for ping responses */
52
53 #ifndef INADDR_NONE
54 #define INADDR_NONE 0xffffffff
55 #endif
56
57 static void ping_server(struct UPing*);
58
59 static struct UPing* pingList = 0;
60 int UPingListener = -1; /* UDP listener socket for upings */
61
62 /*
63  * pings_begin - iterator function for ping list 
64  */
65 struct UPing* uping_begin(void)
66 {
67   return pingList;
68 }
69
70 /*
71  * pings_erase - removes ping struct from ping list
72  */
73 static void uping_erase(struct UPing* p)
74 {
75   struct UPing* it;
76   struct UPing* last = 0;
77
78   assert(0 != p);
79   
80   for (it = pingList; it; last = it = it->next) {
81     if (p == it) {
82       if (last)
83         last->next = p->next;
84       else
85         pingList = p->next;
86       break;
87     }
88   }
89 }
90
91 /*
92  * uping_dns_callback - this gets called when the resolver either
93  * succeeds or fails to locate the servers address.
94  * If the dns query failed hp will be 0, otherwise it
95  * will contain the stuff a hostent normally contains.
96  */
97 static void uping_dns_callback(void* v, struct DNSReply* r)
98 {
99   struct UPing* ping = (struct UPing*) v;
100   assert(valid_ptr((void*)ping, sizeof(struct UPing)));
101
102   if (r) {
103     memcpy(&ping->sin.sin_addr, r->hp->h_addr, sizeof(struct in_addr));
104     ping_server(ping);
105   }
106   else
107   {
108     sendto_ops("UDP ping to %s failed: host lookup", ping->name);
109     end_ping(ping);
110   }
111 }
112
113
114 /*
115  * Setup a UDP socket and listen for incoming packets
116  */
117 int uping_init(void)
118 {
119   struct sockaddr_in from;
120   int fd;
121
122   memset(&from, 0, sizeof(from));
123 #ifdef VIRTUAL_HOST
124   from.sin_addr = vserv.sin_addr;
125 #else
126   from.sin_addr.s_addr = htonl(INADDR_ANY);
127 #endif
128   from.sin_port = htons(atoi(UDP_PORT));
129   from.sin_family = AF_INET;
130
131   if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
132     Debug((DEBUG_ERROR, "UPING: UDP listener socket call failed: %s", strerror(errno)));
133     return -1;
134   }
135   if (!os_set_reuseaddr(fd)) {
136     ircd_log(L_ERROR, "UPING: setsockopt UDP listener: fd %d", fd);
137     Debug((DEBUG_ERROR, "UPING: set reuseaddr on UDP listener failed: %s", strerror(errno)));
138     close(fd);
139     return -1;
140   }
141   if (bind(fd, (struct sockaddr*) &from, sizeof(from)) == -1) {
142     ircd_log(L_ERROR, "UPING: bind UDP listener %d fd %d", htons(from.sin_port), fd);
143     Debug((DEBUG_ERROR, "UPING: bind on UDP listener failed : %s", strerror(errno)));
144     close(fd);
145     return -1;
146   }
147   if (!os_set_nonblocking(fd)) {
148     Debug((DEBUG_ERROR, "UPING: set non-blocking: %s", strerror(errno)));
149     close(fd);
150     return -1;
151   }
152   return fd;
153 }
154
155
156 /*
157  * max # of pings set to 15/sec.
158  */
159 void polludp(int udpfd)
160 {
161   struct sockaddr_in from;
162   size_t             len = 0;
163   static time_t      last = 0;
164   static int         counter = 0;
165   char               buf[BUFSIZE + 1];
166
167   Debug((DEBUG_DEBUG, "UPING: poll"));
168
169   if (IO_SUCCESS != os_recvfrom_nonb(udpfd, buf, BUFSIZE, &len, &from))
170     return;
171   /*
172    * count em even if we're getting flooded so we can tell we're getting
173    * flooded.
174    */
175   ++ServerStats->uping_recv;
176   if (CurrentTime == last) {
177     if (++counter > 10)
178       return;
179   }
180   else {
181     counter = 0;
182     last    = CurrentTime;
183   }
184   if (len < 19)
185     return;
186   sendto(udpfd, buf, len, 0, (struct sockaddr *)&from, sizeof(from));
187 }
188
189
190 /*
191  * start_ping
192  */
193 static void start_ping(struct UPing* pptr)
194 {
195   assert(valid_ptr((void*) pptr, sizeof(struct UPing)));
196
197   if (MyUser(pptr->client) || Protocol(pptr->client->from) < 10) {
198     sendto_one(pptr->client,
199         ":%s NOTICE %s :Sending %d ping%s to %s[%s] port %d",
200         me.name, pptr->client->name, pptr->count,
201         (pptr->count == 1) ? "" : "s", pptr->name,
202         ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port));
203   }
204   else
205   {
206     sendto_one(pptr->client,
207         "%s NOTICE %s%s :Sending %d ping%s to %s[%s] port %d",
208         NumServ(&me), NumNick(pptr->client), pptr->count,
209         (pptr->count == 1) ? "" : "s", pptr->name,
210         ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port));
211   }
212   pptr->timeout = CurrentTime + UPINGTIMEOUT;
213   pptr->active = 1;
214 }
215
216 /*
217  * ping_server - get the server host address if not valid
218  * then call start_ping
219  */
220 static void ping_server(struct UPing* pptr)
221 {
222   if (INADDR_NONE == pptr->sin.sin_addr.s_addr) {
223     char *s;
224
225     if ((s = strchr(pptr->name, '@')))
226       ++s;                      
227     else
228       s = pptr->name;
229
230     if (INADDR_NONE == (pptr->sin.sin_addr.s_addr = inet_addr(s))) {
231       struct DNSQuery query;
232       struct DNSReply* rpl;
233
234       query.vptr = (void*) pptr;
235       query.callback = uping_dns_callback;
236       if (0 == (rpl = gethost_byname(s, &query)))
237         return;
238       memcpy(&pptr->sin.sin_addr, rpl->hp->h_addr, sizeof(struct in_addr));
239     }
240   }
241   start_ping(pptr);
242 }
243
244
245 /*
246  * send_ping
247  *
248  */
249 void send_ping(struct UPing* pptr)
250 {
251   struct timeval tv;
252   char buf[BUFSIZE + 1];
253
254   assert(0 != pptr);
255   memset(buf, 0, sizeof(buf));
256
257   gettimeofday(&tv, NULL);
258   sprintf(buf, " %10lu%c%6lu", tv.tv_sec, '\0', tv.tv_usec);
259
260   Debug((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d",
261           buf, &buf[12],
262           ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port),
263           pptr->fd));
264
265   if (sendto(pptr->fd, buf, BUFSIZE, 0, (struct sockaddr*) &pptr->sin,
266              sizeof(struct sockaddr_in)) != BUFSIZE)
267   {
268     int err = errno;
269     if (pptr->client)
270     {
271       if (MyUser(pptr->client)
272 #ifndef NO_PROTOCOL9
273           || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10)
274 #endif
275           )
276         sendto_one(pptr->client, ":%s NOTICE %s :UPING: sendto() failed: %s",
277                    me.name, pptr->client->name, strerror(errno));
278       else
279         sendto_one(pptr->client, "%s NOTICE %s%s :UPING: sendto() failed: %s",
280                    NumServ(&me), NumNick(pptr->client), strerror(errno));
281     }
282     Debug((DEBUG_DEBUG, "UPING: send_ping: sendto failed on %d: %s", pptr->fd, strerror(err)));
283     end_ping(pptr);
284     return;
285   }
286   ++pptr->sent;
287 }
288
289 /*
290  * read_ping
291  */
292 void read_ping(struct UPing* pptr)
293 {
294   struct sockaddr_in sin;
295   struct timeval     tv;
296   unsigned int       len;
297   unsigned int       pingtime;
298   char*              s;
299   char               buf[BUFSIZE + 1];
300   IOResult           ior;
301
302   assert(0 != pptr);
303
304   gettimeofday(&tv, NULL);
305
306   ior = os_recvfrom_nonb(pptr->fd, buf, BUFSIZE, &len, &sin);
307   if (IO_BLOCKED == ior)
308     return;
309   else if (IO_FAILURE == ior) {
310     int err = errno;
311     if (MyUser(pptr->client)
312 #ifndef NO_PROTOCOL9
313         || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10)
314 #endif
315         )
316       sendto_one(pptr->client, ":%s NOTICE %s :UPING: recvfrom: %s",
317                  me.name, pptr->client->name, strerror(err));
318     else
319       sendto_one(pptr->client, "%s NOTICE %s%s :UPING: recvfrom: %s",
320                  NumServ(&me), NumNick(pptr->client), strerror(err));
321     Debug((DEBUG_SEND, "UPING: read_ping: recvfrom: %d", err));
322     end_ping(pptr);
323     return;
324   }    
325
326   if (len < 19)
327     return;                     /* Broken packet */
328
329   ++pptr->received;
330   pingtime = (tv.tv_sec - atol(&buf[1])) * 1000
331              + (tv.tv_usec - atol(buf + strlen(buf) + 1)) / 1000;
332
333   pptr->ms_ave += pingtime;
334   if (!(pptr->ms_min) || (pptr->ms_min > pingtime))
335     pptr->ms_min = pingtime;
336   if (pingtime > pptr->ms_max)
337     pptr->ms_max = pingtime;
338   
339   pptr->timeout = CurrentTime + UPINGTIMEOUT;
340
341   Debug((DEBUG_SEND, "read_ping: %d bytes, ti %lu: [%s %s] %lu ms",
342       len, pptr->timeout, buf, (buf + strlen(buf) + 1), pingtime));
343
344   s = pptr->buf + strlen(pptr->buf);
345   sprintf(s, " %u", pingtime);
346
347   if (pptr->received == pptr->count)
348     end_ping(pptr);
349   return;
350 }
351
352
353 /*
354  * m_uping  -- by Run
355  *
356  * parv[0] = sender prefix
357  * parv[1] = pinged server
358  * parv[2] = port
359  * parv[3] = hunted server
360  * parv[4] = number of requested pings
361  */
362 int m_uping(struct Client* cptr, struct Client *sptr, int parc, char *parv[])
363 {
364   struct ConfItem *aconf;
365   int port;
366   int fd;
367   struct UPing* pptr = 0;
368
369   if (!IsPrivileged(sptr))
370   {
371     sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
372     return -1;
373   }
374
375   if (parc < 2)
376   {
377     sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UPING");
378     return 0;
379   }
380
381   if (MyUser(sptr))
382   {
383     if (parc == 2)
384     {
385       parv[parc++] = UDP_PORT;
386       parv[parc++] = me.name;
387       parv[parc++] = "5";
388     }
389     else if (parc == 3)
390     {
391       if (IsDigit(*parv[2]))
392         parv[parc++] = me.name;
393       else
394       {
395         parv[parc++] = parv[2];
396         parv[2] = UDP_PORT;
397       }
398       parv[parc++] = "5";
399     }
400     else if (parc == 4)
401     {
402       if (IsDigit(*parv[2]))
403       {
404         if (IsDigit(*parv[3]))
405         {
406           parv[parc++] = parv[3];
407           parv[3] = me.name;
408         }
409         else
410           parv[parc++] = "5";
411       }
412       else
413       {
414         parv[parc++] = parv[3];
415         parv[3] = parv[2];
416         parv[2] = UDP_PORT;
417       }
418     }
419   }
420   if (hunt_server(1, cptr, sptr,
421       ":%s UPING %s %s %s %s", 3, parc, parv) != HUNTED_ISME)
422     return 0;
423
424   if (BadPtr(parv[4]) || atoi(parv[4]) <= 0)
425   {
426     if (MyUser(sptr) || Protocol(cptr) < 10)
427       sendto_one(sptr, ":%s NOTICE %s :UPING: Illegal number of packets: %s",
428           me.name, parv[0], parv[4]);
429     else
430       sendto_one(sptr, "%s NOTICE %s%s :UPING: Illegal number of packets: %s",
431           NumServ(&me), NumNick(sptr), parv[4]);
432     return 0;
433   }
434
435   /* Check if a CONNECT would be possible at all (adapted from m_connect) */
436   for (aconf = GlobalConfList; aconf; aconf = aconf->next)
437   {
438     if (aconf->status == CONF_SERVER &&
439         match(parv[1], aconf->name) == 0)
440       break;
441   }
442   if (!aconf)
443   {
444     for (aconf = GlobalConfList; aconf; aconf = aconf->next)
445     {
446       if (aconf->status == CONF_SERVER &&
447           (match(parv[1], aconf->host) == 0 ||
448            match(parv[1], strchr(aconf->host, '@') + 1) == 0))
449         break;
450     }
451   }
452   if (!aconf)
453   {
454     if (MyUser(sptr) || Protocol(cptr) < 10)
455       sendto_one(sptr, ":%s NOTICE %s :UPING: Host %s not listed in ircd.conf",
456           me.name, parv[0], parv[1]);
457     else
458       sendto_one(sptr,
459           "%s NOTICE %s%s :UPING: Host %s not listed in ircd.conf",
460           NumServ(&me), NumNick(sptr), parv[1]);
461     return 0;
462   }
463
464   if (IsUPing(sptr))
465     cancel_ping(sptr, sptr);  /* Cancel previous ping request */
466
467   /*
468    * Determine port: First user supplied, then default : 7007
469    */
470   if (BadPtr(parv[2]) || (port = atoi(parv[2])) <= 0)
471     port = atoi(UDP_PORT);
472
473   if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
474     int err = errno;
475     sendto_ops("m_uping: socket: %s", (err != EMFILE) 
476                 ? strerror(err) : "No more sockets");
477     if (MyUser(sptr) || Protocol(cptr) < 10)
478       sendto_one(sptr, 
479                  ":%s NOTICE %s :UPING: Unable to create udp ping socket",
480                  me.name, parv[0]);
481     else
482       sendto_one(sptr,
483                  "%s NOTICE %s%s :UPING: Unable to create udp ping socket",
484                  NumServ(&me), NumNick(sptr));
485     ircd_log(L_ERROR, "UPING: Unable to create UDP socket");
486     return 0;
487   }
488
489   if (!os_set_nonblocking(fd)) {
490     if (MyUser(sptr) || Protocol(cptr) < 10)
491       sendto_one(sptr, ":%s NOTICE %s :UPING: Can't set fd non-blocking",
492             me.name, parv[0]);
493     else
494       sendto_one(sptr, "%s NOTICE %s%s :UPING: Can't set fd non-blocking",
495             NumServ(&me), NumNick(sptr));
496     close(fd);
497     return 0;
498   }
499   pptr = (struct UPing*) MyMalloc(sizeof(struct UPing));
500   assert(0 != pptr);
501   memset(pptr, 0, sizeof(struct UPing));
502
503   pptr->fd = fd;
504   pptr->sin.sin_port = htons(port);
505   pptr->sin.sin_addr.s_addr = aconf->ipnum.s_addr;
506   pptr->sin.sin_family = AF_INET;
507   pptr->count = IRCD_MIN(20, atoi(parv[4]));
508   strcpy(pptr->name, aconf->host);
509   pptr->client = sptr;
510   pptr->index = -1;
511
512   pptr->next = pingList;
513   pingList = pptr;
514
515   SetUPing(sptr);
516   ping_server(pptr);
517   return 0;
518 }
519
520 void end_ping(struct UPing* pptr)
521 {
522   Debug((DEBUG_DEBUG, "end_ping: %p", pptr));
523   delete_resolver_queries((void*) pptr);
524   if (pptr->client)
525   {
526     if (MyUser(pptr->client)
527         || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10))
528     {
529       if (pptr->lastsent)       /* Started at all ? */
530       {
531         if (0 < pptr->received) /* Received any pings at all? */
532         {
533           sendto_one(pptr->client, ":%s NOTICE %s :UPING %s%s",
534               me.name, pptr->client->name, pptr->name, pptr->buf);
535           /* XXX - warning long unsigned int format, unsigned int arg (7, 8, 9) */
536           sendto_one(pptr->client,
537               ":%s NOTICE %s :UPING Stats: sent %d recvd %d ; "
538               "min/avg/max = %1lu/%1lu/%1lu ms",
539               me.name, pptr->client->name, pptr->sent,
540               pptr->received, pptr->ms_min,
541               (2 * pptr->ms_ave) / (2 * pptr->received), 
542               pptr->ms_max);
543         }
544         else
545           sendto_one(pptr->client,
546               ":%s NOTICE %s :UPING: no response from %s within %d seconds",
547               me.name, pptr->client->name, pptr->name,
548               UPINGTIMEOUT);
549       }
550       else
551         sendto_one(pptr->client,
552             ":%s NOTICE %s :UPING: Could not start ping to %s %d",
553             me.name, pptr->client->name, pptr->name, ntohs(pptr->sin.sin_port));
554     }
555     else
556     {
557       if (pptr->lastsent)       /* Started at all ? */
558       {
559         if (0 < pptr->received) /* Received any pings at all? */
560         {
561           sendto_one(pptr->client, "%s NOTICE %s%s :UPING %s%s",
562               NumServ(&me), NumNick(pptr->client), pptr->name, pptr->buf);
563           /* XXX - warning: long unsigned int format, unsigned int arg(9, 10, 11) */
564           sendto_one(pptr->client,
565               "%s NOTICE %s%s :UPING Stats: sent %d recvd %d ; "
566               "min/avg/max = %1lu/%1lu/%1lu ms",
567               NumServ(&me), NumNick(pptr->client), pptr->sent,
568               pptr->received, pptr->ms_min,
569               (2 * pptr->ms_ave) / (2 * pptr->received), 
570               pptr->ms_max);
571         }
572         else
573           sendto_one(pptr->client,
574               "%s NOTICE %s%s :UPING: no response from %s within %d seconds",
575               NumServ(&me), NumNick(pptr->client), pptr->name,
576               UPINGTIMEOUT);
577       }
578       else
579         sendto_one(pptr->client,
580             "%s NOTICE %s%s :UPING: Could not start ping to %s %d",
581             NumServ(&me), NumNick(pptr->client), pptr->name, 
582             ntohs(pptr->sin.sin_port));
583     }
584   }
585   close(pptr->fd);
586   pptr->fd = -1;
587   uping_erase(pptr);
588   if (pptr->client)
589     ClearUPing(pptr->client);
590   MyFree(pptr);
591 }
592
593 void cancel_ping(struct Client *sptr, struct Client* acptr)
594 {
595   struct UPing* ping;
596   struct UPing* ping_next;
597
598   Debug((DEBUG_DEBUG, "UPING: cancelling uping for %s", sptr->name));
599   for (ping = pingList; ping; ping = ping_next) {
600     ping_next = ping->next;
601     if (sptr == ping->client) {
602       ping->client = acptr;
603       end_ping(ping);
604     }
605   }
606   ClearUPing(sptr);
607 }
608