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", 
133            (strerror(errno)) ? strerror(errno) : "Unknown error"));
134     return -1;
135   }
136   if (!os_set_reuseaddr(fd)) {
137     ircd_log(L_ERROR, "UPING: setsockopt UDP listener: fd %d", fd);
138     Debug((DEBUG_ERROR, "UPING: set reuseaddr on UDP listener failed: %s",
139            (strerror(errno)) ? strerror(errno) : "Unknown error"));
140     close(fd);
141     return -1;
142   }
143   if (bind(fd, (struct sockaddr*) &from, sizeof(from)) == -1) {
144     ircd_log(L_ERROR, "UPING: bind UDP listener %d fd %d", htons(from.sin_port), fd);
145     Debug((DEBUG_ERROR, "UPING: bind on UDP listener failed : %s",
146            (strerror(errno)) ? strerror(errno) : "Unknown error"));
147     close(fd);
148     return -1;
149   }
150   if (!os_set_nonblocking(fd)) {
151     Debug((DEBUG_ERROR, "UPING: set non-blocking: %s",
152            (strerror(errno)) ? strerror(errno) : "Unknown error"));
153     close(fd);
154     return -1;
155   }
156   return fd;
157 }
158
159
160 /*
161  * max # of pings set to 15/sec.
162  */
163 void polludp(int udpfd)
164 {
165   struct sockaddr_in from;
166   unsigned int       len = 0;
167   static time_t      last = 0;
168   static int         counter = 0;
169   char               buf[BUFSIZE + 1];
170
171   Debug((DEBUG_DEBUG, "UPING: poll"));
172
173   if (IO_SUCCESS != os_recvfrom_nonb(udpfd, buf, BUFSIZE, &len, &from))
174     return;
175   /*
176    * count em even if we're getting flooded so we can tell we're getting
177    * flooded.
178    */
179   ++ServerStats->uping_recv;
180   if (CurrentTime == last) {
181     if (++counter > 10)
182       return;
183   }
184   else {
185     counter = 0;
186     last    = CurrentTime;
187   }
188   if (len < 19)
189     return;
190   sendto(udpfd, buf, len, 0, (struct sockaddr *)&from, sizeof(from));
191 }
192
193
194 /*
195  * start_ping
196  */
197 static void start_ping(struct UPing* pptr)
198 {
199   assert(valid_ptr((void*) pptr, sizeof(struct UPing)));
200
201   if (MyUser(pptr->client) || Protocol(pptr->client->from) < 10) {
202     sendto_one(pptr->client,
203         ":%s NOTICE %s :Sending %d ping%s to %s[%s] port %d",
204         me.name, pptr->client->name, pptr->count,
205         (pptr->count == 1) ? "" : "s", pptr->name,
206         ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port));
207   }
208   else
209   {
210     sendto_one(pptr->client,
211         "%s NOTICE %s%s :Sending %d ping%s to %s[%s] port %d",
212         NumServ(&me), NumNick(pptr->client), pptr->count,
213         (pptr->count == 1) ? "" : "s", pptr->name,
214         ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port));
215   }
216   pptr->timeout = CurrentTime + UPINGTIMEOUT;
217   pptr->active = 1;
218 }
219
220 /*
221  * ping_server - get the server host address if not valid
222  * then call start_ping
223  */
224 static void ping_server(struct UPing* pptr)
225 {
226   if (INADDR_NONE == pptr->sin.sin_addr.s_addr) {
227     char *s;
228
229     if ((s = strchr(pptr->name, '@')))
230       ++s;                      
231     else
232       s = pptr->name;
233
234     if (INADDR_NONE == (pptr->sin.sin_addr.s_addr = inet_addr(s))) {
235       struct DNSQuery query;
236       struct DNSReply* rpl;
237
238       query.vptr = (void*) pptr;
239       query.callback = uping_dns_callback;
240       if (0 == (rpl = gethost_byname(s, &query)))
241         return;
242       memcpy(&pptr->sin.sin_addr, rpl->hp->h_addr, sizeof(struct in_addr));
243     }
244   }
245   start_ping(pptr);
246 }
247
248
249 /*
250  * send_ping
251  *
252  */
253 void send_ping(struct UPing* pptr)
254 {
255   struct timeval tv;
256   char buf[BUFSIZE + 1];
257
258   assert(0 != pptr);
259   memset(buf, 0, sizeof(buf));
260
261   gettimeofday(&tv, NULL);
262   sprintf(buf, " %10lu%c%6lu", tv.tv_sec, '\0', tv.tv_usec);
263
264   Debug((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d",
265           buf, &buf[12],
266           ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port),
267           pptr->fd));
268
269   if (sendto(pptr->fd, buf, BUFSIZE, 0, (struct sockaddr*) &pptr->sin,
270              sizeof(struct sockaddr_in)) != BUFSIZE)
271   {
272     int err = errno;
273     if (pptr->client)
274     {
275       if (MyUser(pptr->client)
276 #ifndef NO_PROTOCOL9
277           || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10)
278 #endif
279           )
280         sendto_one(pptr->client, ":%s NOTICE %s :UPING: sendto() failed: %s",
281                    me.name, pptr->client->name,
282                    (strerror(err)) ? strerror(err) : "Unknown error");
283       else
284         sendto_one(pptr->client, "%s NOTICE %s%s :UPING: sendto() failed: %s",
285                    NumServ(&me), NumNick(pptr->client),
286                    (strerror(err)) ? strerror(err) : "Unknown error");
287     }
288     Debug((DEBUG_DEBUG, "UPING: send_ping: sendto failed on %d: %s", pptr->fd,
289            (strerror(err)) ? strerror(err) : "Unknown error"));
290     end_ping(pptr);
291     return;
292   }
293   ++pptr->sent;
294 }
295
296 /*
297  * read_ping
298  */
299 void read_ping(struct UPing* pptr)
300 {
301   struct sockaddr_in sin;
302   struct timeval     tv;
303   unsigned int       len;
304   unsigned int       pingtime;
305   char*              s;
306   char               buf[BUFSIZE + 1];
307   IOResult           ior;
308
309   assert(0 != pptr);
310
311   gettimeofday(&tv, NULL);
312
313   ior = os_recvfrom_nonb(pptr->fd, buf, BUFSIZE, &len, &sin);
314   if (IO_BLOCKED == ior)
315     return;
316   else if (IO_FAILURE == ior) {
317     int err = errno;
318     if (MyUser(pptr->client)
319 #ifndef NO_PROTOCOL9
320         || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10)
321 #endif
322         )
323       sendto_one(pptr->client, ":%s NOTICE %s :UPING: recvfrom: %s",
324                  me.name, pptr->client->name, 
325                  (strerror(err)) ? strerror(err) : "Unknown error");
326     else
327       sendto_one(pptr->client, "%s NOTICE %s%s :UPING: recvfrom: %s",
328                  NumServ(&me), NumNick(pptr->client),
329                  (strerror(err)) ? strerror(err) : "Unknown error");
330     Debug((DEBUG_SEND, "UPING: read_ping: recvfrom: %d", err));
331     end_ping(pptr);
332     return;
333   }    
334
335   if (len < 19)
336     return;                     /* Broken packet */
337
338   ++pptr->received;
339   pingtime = (tv.tv_sec - atol(&buf[1])) * 1000
340              + (tv.tv_usec - atol(buf + strlen(buf) + 1)) / 1000;
341
342   pptr->ms_ave += pingtime;
343   if (!(pptr->ms_min) || (pptr->ms_min > pingtime))
344     pptr->ms_min = pingtime;
345   if (pingtime > pptr->ms_max)
346     pptr->ms_max = pingtime;
347   
348   pptr->timeout = CurrentTime + UPINGTIMEOUT;
349
350   Debug((DEBUG_SEND, "read_ping: %d bytes, ti %lu: [%s %s] %lu ms",
351       len, pptr->timeout, buf, (buf + strlen(buf) + 1), pingtime));
352
353   s = pptr->buf + strlen(pptr->buf);
354   sprintf(s, " %u", pingtime);
355
356   if (pptr->received == pptr->count)
357     end_ping(pptr);
358   return;
359 }
360
361
362 /*
363  * m_uping  -- by Run
364  *
365  * parv[0] = sender prefix
366  * parv[1] = pinged server
367  * parv[2] = port
368  * parv[3] = hunted server
369  * parv[4] = number of requested pings
370  */
371 int m_uping(struct Client* cptr, struct Client *sptr, int parc, char *parv[])
372 {
373   struct ConfItem *aconf;
374   int port;
375   int fd;
376   struct UPing* pptr = 0;
377
378   if (!IsPrivileged(sptr))
379   {
380     sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
381     return -1;
382   }
383
384   if (parc < 2)
385   {
386     sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UPING");
387     return 0;
388   }
389
390   if (MyUser(sptr))
391   {
392     if (parc == 2)
393     {
394       parv[parc++] = UDP_PORT;
395       parv[parc++] = me.name;
396       parv[parc++] = "5";
397     }
398     else if (parc == 3)
399     {
400       if (IsDigit(*parv[2]))
401         parv[parc++] = me.name;
402       else
403       {
404         parv[parc++] = parv[2];
405         parv[2] = UDP_PORT;
406       }
407       parv[parc++] = "5";
408     }
409     else if (parc == 4)
410     {
411       if (IsDigit(*parv[2]))
412       {
413         if (IsDigit(*parv[3]))
414         {
415           parv[parc++] = parv[3];
416           parv[3] = me.name;
417         }
418         else
419           parv[parc++] = "5";
420       }
421       else
422       {
423         parv[parc++] = parv[3];
424         parv[3] = parv[2];
425         parv[2] = UDP_PORT;
426       }
427     }
428   }
429   if (hunt_server(1, cptr, sptr,
430       ":%s UPING %s %s %s %s", 3, parc, parv) != HUNTED_ISME)
431     return 0;
432
433   if (BadPtr(parv[4]) || atoi(parv[4]) <= 0)
434   {
435     if (MyUser(sptr) || Protocol(cptr) < 10)
436       sendto_one(sptr, ":%s NOTICE %s :UPING: Illegal number of packets: %s",
437           me.name, parv[0], parv[4]);
438     else
439       sendto_one(sptr, "%s NOTICE %s%s :UPING: Illegal number of packets: %s",
440           NumServ(&me), NumNick(sptr), parv[4]);
441     return 0;
442   }
443
444   /* Check if a CONNECT would be possible at all (adapted from m_connect) */
445   for (aconf = GlobalConfList; aconf; aconf = aconf->next)
446   {
447     if (aconf->status == CONF_SERVER &&
448         match(parv[1], aconf->name) == 0)
449       break;
450   }
451   if (!aconf)
452   {
453     for (aconf = GlobalConfList; aconf; aconf = aconf->next)
454     {
455       if (aconf->status == CONF_SERVER &&
456           (match(parv[1], aconf->host) == 0 ||
457            match(parv[1], strchr(aconf->host, '@') + 1) == 0))
458         break;
459     }
460   }
461   if (!aconf)
462   {
463     if (MyUser(sptr) || Protocol(cptr) < 10)
464       sendto_one(sptr, ":%s NOTICE %s :UPING: Host %s not listed in ircd.conf",
465           me.name, parv[0], parv[1]);
466     else
467       sendto_one(sptr,
468           "%s NOTICE %s%s :UPING: Host %s not listed in ircd.conf",
469           NumServ(&me), NumNick(sptr), parv[1]);
470     return 0;
471   }
472
473   if (IsUPing(sptr))
474     cancel_ping(sptr, sptr);  /* Cancel previous ping request */
475
476   /*
477    * Determine port: First user supplied, then default : 7007
478    */
479   if (BadPtr(parv[2]) || (port = atoi(parv[2])) <= 0)
480     port = atoi(UDP_PORT);
481
482   if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
483     int err = errno;
484     sendto_ops("m_uping: socket: %s", (err != EMFILE) 
485                 ? ((strerror(err)) ? strerror(err) : "Unknown error") : "No more sockets");
486     if (MyUser(sptr) || Protocol(cptr) < 10)
487       sendto_one(sptr, 
488                  ":%s NOTICE %s :UPING: Unable to create udp ping socket",
489                  me.name, parv[0]);
490     else
491       sendto_one(sptr,
492                  "%s NOTICE %s%s :UPING: Unable to create udp ping socket",
493                  NumServ(&me), NumNick(sptr));
494     ircd_log(L_ERROR, "UPING: Unable to create UDP socket");
495     return 0;
496   }
497
498   if (!os_set_nonblocking(fd)) {
499     if (MyUser(sptr) || Protocol(cptr) < 10)
500       sendto_one(sptr, ":%s NOTICE %s :UPING: Can't set fd non-blocking",
501             me.name, parv[0]);
502     else
503       sendto_one(sptr, "%s NOTICE %s%s :UPING: Can't set fd non-blocking",
504             NumServ(&me), NumNick(sptr));
505     close(fd);
506     return 0;
507   }
508   pptr = (struct UPing*) MyMalloc(sizeof(struct UPing));
509   assert(0 != pptr);
510   memset(pptr, 0, sizeof(struct UPing));
511
512   pptr->fd = fd;
513   pptr->sin.sin_port = htons(port);
514   pptr->sin.sin_addr.s_addr = aconf->ipnum.s_addr;
515   pptr->sin.sin_family = AF_INET;
516   pptr->count = IRCD_MIN(20, atoi(parv[4]));
517   strcpy(pptr->name, aconf->host);
518   pptr->client = sptr;
519   pptr->index = -1;
520
521   pptr->next = pingList;
522   pingList = pptr;
523
524   SetUPing(sptr);
525   ping_server(pptr);
526   return 0;
527 }
528
529 void end_ping(struct UPing* pptr)
530 {
531   Debug((DEBUG_DEBUG, "end_ping: %p", pptr));
532   delete_resolver_queries((void*) pptr);
533   if (pptr->client)
534   {
535     if (MyUser(pptr->client)
536         || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10))
537     {
538       if (pptr->lastsent)       /* Started at all ? */
539       {
540         if (0 < pptr->received) /* Received any pings at all? */
541         {
542           sendto_one(pptr->client, ":%s NOTICE %s :UPING %s%s",
543               me.name, pptr->client->name, pptr->name, pptr->buf);
544           /* XXX - warning long unsigned int format, unsigned int arg (7, 8, 9) */
545           sendto_one(pptr->client,
546               ":%s NOTICE %s :UPING Stats: sent %d recvd %d ; "
547               "min/avg/max = %1lu/%1lu/%1lu ms",
548               me.name, pptr->client->name, pptr->sent,
549               pptr->received, pptr->ms_min,
550               (2 * pptr->ms_ave) / (2 * pptr->received), 
551               pptr->ms_max);
552         }
553         else
554           sendto_one(pptr->client,
555               ":%s NOTICE %s :UPING: no response from %s within %d seconds",
556               me.name, pptr->client->name, pptr->name,
557               UPINGTIMEOUT);
558       }
559       else
560         sendto_one(pptr->client,
561             ":%s NOTICE %s :UPING: Could not start ping to %s %d",
562             me.name, pptr->client->name, pptr->name, ntohs(pptr->sin.sin_port));
563     }
564     else
565     {
566       if (pptr->lastsent)       /* Started at all ? */
567       {
568         if (0 < pptr->received) /* Received any pings at all? */
569         {
570           sendto_one(pptr->client, "%s NOTICE %s%s :UPING %s%s",
571               NumServ(&me), NumNick(pptr->client), pptr->name, pptr->buf);
572           /* XXX - warning: long unsigned int format, unsigned int arg(9, 10, 11) */
573           sendto_one(pptr->client,
574               "%s NOTICE %s%s :UPING Stats: sent %d recvd %d ; "
575               "min/avg/max = %1lu/%1lu/%1lu ms",
576               NumServ(&me), NumNick(pptr->client), pptr->sent,
577               pptr->received, pptr->ms_min,
578               (2 * pptr->ms_ave) / (2 * pptr->received), 
579               pptr->ms_max);
580         }
581         else
582           sendto_one(pptr->client,
583               "%s NOTICE %s%s :UPING: no response from %s within %d seconds",
584               NumServ(&me), NumNick(pptr->client), pptr->name,
585               UPINGTIMEOUT);
586       }
587       else
588         sendto_one(pptr->client,
589             "%s NOTICE %s%s :UPING: Could not start ping to %s %d",
590             NumServ(&me), NumNick(pptr->client), pptr->name, 
591             ntohs(pptr->sin.sin_port));
592     }
593   }
594   close(pptr->fd);
595   pptr->fd = -1;
596   uping_erase(pptr);
597   if (pptr->client)
598     ClearUPing(pptr->client);
599   MyFree(pptr);
600 }
601
602 void cancel_ping(struct Client *sptr, struct Client* acptr)
603 {
604   struct UPing* ping;
605   struct UPing* ping_next;
606
607   Debug((DEBUG_DEBUG, "UPING: cancelling uping for %s", sptr->name));
608   for (ping = pingList; ping; ping = ping_next) {
609     ping_next = ping->next;
610     if (sptr == ping->client) {
611       ping->client = acptr;
612       end_ping(ping);
613     }
614   }
615   ClearUPing(sptr);
616 }
617