Author: Run <carlo@alinoe.com>
[ircu2.10.12-pk.git] / ircd / s_ping.c
1 /*
2  * IRC - Internet Relay Chat, ircd/s_ping.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
20 #include "sys.h"
21 #include <sys/socket.h>
22 #if HAVE_SYS_FILE_H
23 #include <sys/file.h>
24 #endif
25 #if HAVE_SYS_IOCTL_H
26 #include <sys/ioctl.h>
27 #endif
28 #ifdef  UNIXPORT
29 #include <sys/un.h>
30 #endif
31 #if HAVE_FCNTL_H
32 #include <fcntl.h>
33 #endif
34 #include <stdlib.h>
35 #if HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #ifdef USE_SYSLOG
39 #include <syslog.h>
40 #endif
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include "h.h"
44 #include "struct.h"
45 #include "send.h"
46 #include "s_conf.h"
47 #include "match.h"
48 #include "res.h"
49 #include "s_bsd.h"
50 #include "s_serv.h"
51 #include "ircd.h"
52 #include "s_ping.h"
53 #include "support.h"
54 #include "numeric.h"
55 #include "s_user.h"
56 #include "s_err.h"
57 #include "common.h"
58 #include "s_user.h"
59 #include "numnicks.h"
60
61 RCSTAG_CC("$Id$");
62
63 #define UPINGBUFSIZE 2000       /* Lot bigger then 1024,
64                                    bit smaller then 2048 */
65 #define UPINGTIMEOUT 120        /* Timeout waitting for first ping response */
66
67 /*
68  * start_ping
69  *
70  * As for now, I am abusing the client structure for a ping connection.
71  *                 .... And I really don't like this solution --Nemesi
72  * Used members are:
73  * These are used by existing routines as well, and do have their own meaning:
74  *   fd       : The socket file descriptor.
75  *   status   : To flag that this IS one of these abused ping structures
76  *   sockhost : Name of requested server to ping (aconf->host).
77  *   name     : aconf->name
78  *   ip       : ip#
79  * These have more or less their own meaning,
80  * but are not used by existing routines:
81  *   flags    : To flag that a next ping is requested.
82  *   port     : Requested remote port.
83  * These are only used by the 'uping' routines
84  * and have totally different meanings:
85  *   buffer   : buffer hold pingtimes of received packets
86  *   confs    : recv/send (char *) buffer.
87  *   hopcount : Total number of requested pings
88  *   sendB    : Number of pings left to send.
89  *   receiveB : Number of pings left to be received.
90  *   acpt     : client asking for this ping
91  *   lasttime : last time a ping was sent
92  *   firsttime: recvfrom timeout
93  *   since    : timeout in seconds to next recvfrom
94  *   receiveK : minimum in ms
95  *   sendM    : average in ms
96  *   receiveM : maximum in ms
97  */
98 int start_ping(aClient *cptr)
99 {
100   struct sockaddr_in remote_addr;
101
102   Debug((DEBUG_NOTICE, "start_ping(%p) status %d", cptr, cptr->status));
103
104   if (!(cptr->acpt))
105     return -1;
106
107   memcpy(&remote_addr.sin_addr, &cptr->ip, sizeof(struct in_addr));
108 #ifdef TESTNET
109   remote_addr.sin_port = htons(cptr->port + 10000);
110 #else
111   remote_addr.sin_port = htons(cptr->port);
112 #endif
113   remote_addr.sin_family = AF_INET;
114
115   if (MyUser(cptr->acpt) || Protocol(cptr->acpt->from) < 10)
116   {
117     sendto_one(cptr->acpt,
118         ":%s NOTICE %s :Sending %d ping%s to %s[%s] port %u",
119         me.name, cptr->acpt->name, cptr->hopcount,
120         (cptr->hopcount == 1) ? "" : "s", cptr->name,
121 #ifdef TESTNET
122         inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port) - 10000);
123 #else
124         inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
125 #endif
126   }
127   else
128   {
129     sendto_one(cptr->acpt,
130         "%s NOTICE %s%s :Sending %d ping%s to %s[%s] port %u",
131         NumServ(&me), NumNick(cptr->acpt), cptr->hopcount,
132         (cptr->hopcount == 1) ? "" : "s", cptr->name,
133 #ifdef TESTNET
134         inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port) - 10000);
135 #else
136         inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
137 #endif
138   }
139
140   cptr->firsttime = now + UPINGTIMEOUT;
141   cptr->since = UPINGTIMEOUT;
142   cptr->flags |= (FLAGS_PING);
143
144   return 0;
145 }
146
147 /*
148  * send_ping
149  *
150  */
151 void send_ping(aClient *cptr)
152 {
153   struct sockaddr_in remote_addr;
154   struct timeval tv;
155
156   memcpy(&remote_addr.sin_addr, &cptr->ip, sizeof(struct in_addr));
157 #ifdef TESTNET
158   remote_addr.sin_port = htons(cptr->port + 10000);
159 #else
160   remote_addr.sin_port = htons(cptr->port);
161 #endif
162   remote_addr.sin_family = AF_INET;
163
164   gettimeofday(&tv, NULL);
165 #if defined(__sun__) || (__GLIBC__ >= 2) || defined(__NetBSD__)
166   sprintf((char *)cptr->confs, " %10lu%c%6lu", tv.tv_sec, '\0', tv.tv_usec);
167 #else
168   sprintf((char *)cptr->confs, " %10u%c%6u", tv.tv_sec, '\0', tv.tv_usec);
169 #endif
170
171   Debug((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d",
172       (char *)cptr->confs, (char *)cptr->confs + 12,
173       inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port), cptr->fd));
174
175   if (sendto(cptr->fd, (char *)cptr->confs, 1024, 0,
176       (struct sockaddr *)&remote_addr, sizeof(struct sockaddr_in)) != 1024)
177   {
178 #ifdef DEBUGMODE
179     int err = errno;
180 #endif
181     if (cptr->acpt)
182     {
183       if (MyUser(cptr->acpt)
184 #ifndef NO_PROTOCOL9
185           || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10)
186 #endif
187           )
188         sendto_one(cptr->acpt, ":%s NOTICE %s :UPING: sendto() failed: %s",
189             me.name, cptr->acpt->name, strerror(get_sockerr(cptr)));
190       else
191         sendto_one(cptr->acpt, "%s NOTICE %s%s :UPING: sendto() failed: %s",
192             NumServ(&me), NumNick(cptr->acpt), strerror(get_sockerr(cptr)));
193     }
194     Debug((DEBUG_SEND, "send_ping: sendto failed on %d (%d)", cptr->fd, err));
195     end_ping(cptr);
196   }
197   else if (--(cptr->sendB) <= 0)
198   {
199     ClearPing(cptr);
200     if (cptr->receiveB <= 0)
201       end_ping(cptr);
202   }
203
204   return;
205 }
206
207 /*
208  * read_ping
209  */
210 void read_ping(aClient *cptr)
211 {
212   size_t addr_len = sizeof(struct sockaddr_in);
213   struct sockaddr_in remote_addr;
214   struct timeval tv;
215   int len;
216   unsigned long int pingtime;
217   char *s;
218
219   memcpy(&remote_addr.sin_addr, &cptr->ip, sizeof(struct in_addr));
220 #ifdef TESTNET
221   remote_addr.sin_port = htons(cptr->port + 10000);
222 #else
223   remote_addr.sin_port = htons(cptr->port);
224 #endif
225   remote_addr.sin_family = AF_INET;
226
227   gettimeofday(&tv, NULL);
228
229   if ((len = recvfrom(cptr->fd, (char *)cptr->confs, UPINGBUFSIZE, 0,
230       (struct sockaddr *)&remote_addr, &addr_len)) == -1)
231   {
232     int err = errno;
233     if (MyUser(cptr->acpt)
234 #ifndef NO_PROTOCOL9
235         || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10)
236 #endif
237         )
238       sendto_one(cptr->acpt, ":%s NOTICE %s :UPING: recvfrom: %s",
239           me.name, cptr->acpt->name, strerror(get_sockerr(cptr)));
240     else
241       sendto_one(cptr->acpt, "%s NOTICE %s%s :UPING: recvfrom: %s",
242           NumServ(&me), NumNick(cptr->acpt), strerror(get_sockerr(cptr)));
243     Debug((DEBUG_SEND, "read_ping: recvfrom: %d", err));
244     if (err != EAGAIN)
245       end_ping(cptr);
246     return;
247   }
248
249   if (len < 19)
250     return;                     /* Broken packet */
251
252   pingtime = (tv.tv_sec - atoi((char *)cptr->confs + 1)) * 1000 +
253       (tv.tv_usec - atoi((char *)cptr->confs + strlen((char *)cptr->confs) +
254       1)) / 1000;
255   cptr->sendM += pingtime;
256   if (!(cptr->receiveK) || (cptr->receiveK > pingtime))
257     cptr->receiveK = pingtime;
258   if (pingtime > cptr->receiveM)
259     cptr->receiveM = pingtime;
260   /* Wait at most 10 times the average pingtime for the next one: */
261   if ((cptr->since =
262       cptr->sendM / (100 * (cptr->hopcount - cptr->receiveB + 1))) < 2)
263     cptr->since = 2;
264   cptr->firsttime = tv.tv_sec + cptr->since;
265
266   Debug((DEBUG_SEND, "read_ping: %d bytes, ti " TIME_T_FMT ": [%s %s] %lu ms",
267       len, cptr->since, (char *)cptr->confs,
268       (char *)cptr->confs + strlen((char *)cptr->confs) + 1, pingtime));
269
270   s = cptr->buffer + strlen(cptr->buffer);
271   sprintf(s, " %lu", pingtime);
272
273   if ((--(cptr->receiveB) <= 0 && !DoPing(cptr)) || !(cptr->acpt))
274     end_ping(cptr);
275
276   return;
277 }
278
279 int ping_server(aClient *cptr)
280 {
281   if ((!cptr->ip.s_addr)
282 #ifdef UNIXPORT
283       && ((cptr->sockhost[2]) != '/')
284 #endif
285       )
286   {
287     struct hostent *hp;
288     char *s;
289     Link lin;
290
291     if (!(cptr->acpt))
292       return -1;                /* Oper left already */
293
294     lin.flags = ASYNC_PING;
295     lin.value.cptr = cptr;
296     nextdnscheck = 1;
297     s = strchr(cptr->sockhost, '@');
298     s++;                        /* should never be NULL;
299                                    cptr->sockhost is actually a conf->host */
300     if ((cptr->ip.s_addr = inet_addr(s)) == INADDR_NONE)
301     {
302       cptr->ip.s_addr = INADDR_ANY;
303       hp = gethost_byname(s, &lin);
304       Debug((DEBUG_NOTICE, "ping_sv: hp %p ac %p ho %s", hp, cptr, s));
305       if (!hp)
306         return 0;
307       memcpy(&cptr->ip, hp->h_addr, sizeof(struct in_addr));
308     }
309   }
310
311   return start_ping(cptr);
312 }
313
314 /*
315  * m_uping  -- by Run
316  *
317  * parv[0] = sender prefix
318  * parv[1] = pinged server
319  * parv[2] = port
320  * parv[3] = hunted server
321  * parv[4] = number of requested pings
322  */
323 int m_uping(aClient *cptr, aClient *sptr, int parc, char *parv[])
324 {
325   aConfItem *aconf;
326   unsigned short int port;
327   int fd, opt;
328   char *p;
329
330   if (!IsPrivileged(sptr))
331   {
332     sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
333     return -1;
334   }
335
336   if (parc < 2)
337   {
338     sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UPING");
339     return 0;
340   }
341
342   if (MyUser(sptr))
343   {
344     if (parc == 2)
345     {
346       parv[parc++] = UDP_PORT;
347       parv[parc++] = me.name;
348       parv[parc++] = "5";
349     }
350     else if (parc == 3)
351     {
352       if (isDigit(*parv[2]))
353         parv[parc++] = me.name;
354       else
355       {
356         parv[parc++] = parv[2];
357         parv[2] = UDP_PORT;
358       }
359       parv[parc++] = "5";
360     }
361     else if (parc == 4)
362     {
363       if (isDigit(*parv[2]))
364       {
365         if (isDigit(*parv[3]))
366         {
367           parv[parc++] = parv[3];
368           parv[3] = me.name;
369         }
370         else
371           parv[parc++] = "5";
372       }
373       else
374       {
375         parv[parc++] = parv[3];
376         parv[3] = parv[2];
377         parv[2] = UDP_PORT;
378       }
379     }
380   }
381   if (hunt_server(1, cptr, sptr,
382       ":%s UPING %s %s %s %s", 3, parc, parv) != HUNTED_ISME)
383     return 0;
384
385   if (BadPtr(parv[4]) || atoi(parv[4]) <= 0)
386   {
387     if (MyUser(sptr) || Protocol(cptr) < 10)
388       sendto_one(sptr, ":%s NOTICE %s :UPING: Invalid number of packets: %s",
389           me.name, parv[0], parv[4]);
390     else
391       sendto_one(sptr, "%s NOTICE %s%s :UPING: Invalid number of packets: %s",
392           NumServ(&me), NumNick(sptr), parv[4]);
393     return 0;
394   }
395
396   /* Check if a CONNECT would be possible at all (adapted from m_connect) */
397   if (parc > 2 && !BadPtr(parv[2]))
398     p = strchr(parv[2], ':');
399   else
400     p = 0;
401   if (p)
402     *p = 0;
403   for (aconf = conf; aconf; aconf = aconf->next)
404     if (aconf->status == CONF_CONNECT_SERVER &&
405         match(parv[1], aconf->name) == 0 &&
406         (!p || match(parv[2], aconf->host) == 0 ||
407         match(parv[2], strchr(aconf->host, '@') + 1) == 0))
408       break;
409   if (!aconf)
410     for (aconf = conf; aconf; aconf = aconf->next)
411       if (aconf->status == CONF_CONNECT_SERVER &&
412           (match(parv[1], aconf->host) == 0 ||
413           match(parv[1], strchr(aconf->host, '@') + 1) == 0))
414         break;
415   if (p)
416     *p = ':';
417   if (!aconf)
418   {
419     if (MyUser(sptr) || Protocol(cptr) < 10)
420       sendto_one(sptr, ":%s NOTICE %s :UPING: Host %s not listed in ircd.conf",
421           me.name, parv[0], parv[1]);
422     else
423       sendto_one(sptr,
424           "%s NOTICE %s%s :UPING: Host %s not listed in ircd.conf",
425           NumServ(&me), NumNick(sptr), parv[1]);
426     return 0;
427   }
428
429   if (AskedPing(sptr))
430     cancel_ping(sptr, sptr);    /* Cancel previous ping request */
431
432   /*
433    * Determine port: First user supplied, then default : 7007
434    */
435   if (!p || (port = atoi(p + 1)) <= 0)
436     port = atoi(UDP_PORT);
437
438   alarm(2);
439   if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
440   {
441     int err = errno;
442     alarm(0);
443     sendto_ops("m_uping: socket: %s", (err != EAGAIN) ?
444         strerror(err) : "No more sockets");
445     if (MyUser(sptr) || Protocol(cptr) < 10)
446       sendto_one(sptr, ":%s NOTICE %s :UPING: Unable to create udp ping socket",
447           me.name, parv[0]);
448     else
449       sendto_one(sptr,
450           "%s NOTICE %s%s :UPING: Unable to create udp ping socket",
451           NumServ(&me), NumNick(sptr));
452 #ifdef  USE_SYSLOG
453     syslog(LOG_ERR, "Unable to create udp ping socket");
454 #endif
455     return 0;
456   }
457   alarm(0);
458
459   if (fcntl(fd, F_SETFL, FNDELAY) == -1)
460   {
461     sendto_ops("m_uping: fcntl FNDELAY: %s", strerror(errno));
462     if (MyUser(sptr) || Protocol(cptr) < 10)
463       sendto_one(sptr, ":%s NOTICE %s :UPING: Can't set fd non-blocking",
464           me.name, parv[0]);
465     else
466       sendto_one(sptr, "%s NOTICE %s%s :UPING: Can't set fd non-blocking",
467           NumServ(&me), NumNick(sptr));
468     close(fd);
469     return 0;
470   }
471   /*
472    * On some systems, receive and send buffers must be equal in size.
473    * Others block select() when the buffers are too small
474    * (Linux 1.1.50 blocks when < 2048) --Run
475    */
476   opt = 2048;
477   if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (OPT_TYPE *)&opt,
478       sizeof(opt)) < 0 ||
479       setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (OPT_TYPE *)&opt, sizeof(opt)) < 0)
480   {
481     int err = errno;
482     sendto_ops("m_uping: setsockopt SO_SNDBUF|SO_RCVBUF: %s", strerror(err));
483     if (MyUser(sptr) || Protocol(cptr) < 10)
484       sendto_one(sptr, ":%s NOTICE %s :UPING: error in setsockopt: %s",
485           me.name, parv[0], strerror(err));
486     else
487       sendto_one(sptr, "%s NOTICE %s%s :UPING: error in setsockopt: %s",
488           NumServ(&me), NumNick(sptr), strerror(err));
489     close(fd);
490     return 0;
491   }
492
493   if (fd >= MAXCONNECTIONS)
494   {
495     sendto_ops("Can't allocate fd for uping (all connections in use)");
496     if (MyUser(sptr) || Protocol(cptr) < 10)
497       sendto_one(sptr, ":%s NOTICE %s :UPING: All connections in use",
498           me.name, parv[0]);
499     else
500       sendto_one(sptr, "%s NOTICE %s%s :UPING: All connections in use",
501           NumServ(&me), NumNick(sptr));
502     close(fd);
503     return 0;
504   }
505   if (bind(fd, (struct sockaddr *)&cserv, sizeof(cserv)) == -1)
506   {
507     sendto_ops("Failure binding fd for uping");
508     if (MyUser(sptr) || Protocol(cptr) < 10)
509       sendto_one(sptr, ":%s NOTICE %s :UPING: failure binding udp socket",
510           me.name, parv[0]);
511     else
512       sendto_one(sptr, "%s NOTICE %s%s :UPING: failure binding udp socket",
513           NumServ(&me), NumNick(sptr));
514     close(fd);
515     return 0;
516   }
517
518   if (fd > highest_fd)
519     highest_fd = fd;
520   loc_clients[fd] = cptr = make_client(NULL, STAT_PING);
521   cptr->confs = (Link *)RunMalloc(UPINGBUFSIZE);        /* Really a (char *) */
522   cptr->fd = fd;
523   cptr->port = port;
524   cptr->hopcount = cptr->receiveB = cptr->sendB = MIN(20, atoi(parv[4]));
525   strcpy(cptr->sockhost, aconf->host);
526   cptr->acpt = sptr;
527   SetAskedPing(sptr);
528   memcpy(&cptr->ip, &aconf->ipnum, sizeof(struct in_addr));
529   strcpy(cptr->name, aconf->name);
530   cptr->firsttime = 0;
531
532   switch (ping_server(cptr))
533   {
534     case 0:
535       break;
536     case -1:
537       del_queries((char *)cptr);
538       end_ping(cptr);
539       break;
540   }
541   return 0;
542 }
543
544 void end_ping(aClient *cptr)
545 {
546   Debug((DEBUG_DEBUG, "end_ping: %p", cptr));
547   if (cptr->acpt)
548   {
549     if (MyUser(cptr->acpt)
550         || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10))
551     {
552       if (cptr->firsttime)      /* Started at all ? */
553       {
554         if (cptr->receiveB != cptr->hopcount)   /* Received any pings at all? */
555         {
556           sendto_one(cptr->acpt, ":%s NOTICE %s :UPING %s%s",
557               me.name, cptr->acpt->name, cptr->name, cptr->buffer);
558           sendto_one(cptr->acpt,
559               ":%s NOTICE %s :UPING Stats: sent %d recvd %d ; "
560               "min/avg/max = %u/%u/%u ms",
561               me.name, cptr->acpt->name, cptr->hopcount - cptr->sendB,
562               cptr->hopcount - cptr->receiveB, cptr->receiveK,
563               (2 * cptr->sendM + cptr->hopcount - cptr->receiveB) /
564               (2 * (cptr->hopcount - cptr->receiveB)), cptr->receiveM);
565         }
566         else
567           sendto_one(cptr->acpt,
568               ":%s NOTICE %s :UPING: no response from %s within %d seconds",
569               me.name, cptr->acpt->name, cptr->name,
570               (int)(now + cptr->since - cptr->firsttime));
571       }
572       else
573         sendto_one(cptr->acpt,
574             ":%s NOTICE %s :UPING: Could not start ping to %s %u",
575             me.name, cptr->acpt->name, cptr->name, cptr->port);
576     }
577     else
578     {
579       if (cptr->firsttime)      /* Started at all ? */
580       {
581         if (cptr->receiveB != cptr->hopcount)   /* Received any pings at all? */
582         {
583           sendto_one(cptr->acpt, "%s NOTICE %s%s :UPING %s%s",
584               NumServ(&me), NumNick(cptr->acpt), cptr->name, cptr->buffer);
585           sendto_one(cptr->acpt,
586               "%s NOTICE %s%s :UPING Stats: sent %d recvd %d ; "
587               "min/avg/max = %u/%u/%u ms",
588               NumServ(&me), NumNick(cptr->acpt), cptr->hopcount - cptr->sendB,
589               cptr->hopcount - cptr->receiveB, cptr->receiveK,
590               (2 * cptr->sendM + cptr->hopcount - cptr->receiveB) /
591               (2 * (cptr->hopcount - cptr->receiveB)), cptr->receiveM);
592         }
593         else
594           sendto_one(cptr->acpt,
595               "%s NOTICE %s%s :UPING: no response from %s within %d seconds",
596               NumServ(&me), NumNick(cptr->acpt), cptr->name,
597               (int)(now + cptr->since - cptr->firsttime));
598       }
599       else
600         sendto_one(cptr->acpt,
601             "%s NOTICE %s%s :UPING: Could not start ping to %s %d",
602             NumServ(&me), NumNick(cptr->acpt), cptr->name, cptr->port);
603     }
604   }
605   close(cptr->fd);
606   loc_clients[cptr->fd] = NULL;
607   if (cptr->acpt)
608     ClearAskedPing(cptr->acpt);
609   RunFree((char *)cptr->confs);
610   free_client(cptr);
611 }
612
613 void cancel_ping(aClient *sptr, aClient *acptr)
614 {
615   int i;
616   aClient *cptr;
617
618   Debug((DEBUG_DEBUG, "Cancelling uping for %p (%s)", sptr, sptr->name));
619   for (i = highest_fd; i >= 0; i--)
620     if ((cptr = loc_clients[i]) && IsPing(cptr) && cptr->acpt == sptr)
621     {
622       cptr->acpt = acptr;
623       del_queries((char *)cptr);
624       end_ping(cptr);
625       break;
626     }
627
628   ClearAskedPing(sptr);
629 }