Author: Bleep <tomh@inxpress.net>
[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
329   if (!IsPrivileged(sptr))
330   {
331     sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
332     return -1;
333   }
334
335   if (parc < 2)
336   {
337     sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UPING");
338     return 0;
339   }
340
341   if (MyUser(sptr))
342   {
343     if (parc == 2)
344     {
345       parv[parc++] = UDP_PORT;
346       parv[parc++] = me.name;
347       parv[parc++] = "5";
348     }
349     else if (parc == 3)
350     {
351       if (isDigit(*parv[2]))
352         parv[parc++] = me.name;
353       else
354       {
355         parv[parc++] = parv[2];
356         parv[2] = UDP_PORT;
357       }
358       parv[parc++] = "5";
359     }
360     else if (parc == 4)
361     {
362       if (isDigit(*parv[2]))
363       {
364         if (isDigit(*parv[3]))
365         {
366           parv[parc++] = parv[3];
367           parv[3] = me.name;
368         }
369         else
370           parv[parc++] = "5";
371       }
372       else
373       {
374         parv[parc++] = parv[3];
375         parv[3] = parv[2];
376         parv[2] = UDP_PORT;
377       }
378     }
379   }
380   if (hunt_server(1, cptr, sptr,
381       ":%s UPING %s %s %s %s", 3, parc, parv) != HUNTED_ISME)
382     return 0;
383
384   if (BadPtr(parv[4]) || atoi(parv[4]) <= 0)
385   {
386     if (MyUser(sptr) || Protocol(cptr) < 10)
387       sendto_one(sptr, ":%s NOTICE %s :UPING: Invalid number of packets: %s",
388           me.name, parv[0], parv[4]);
389     else
390       sendto_one(sptr, "%s NOTICE %s%s :UPING: Invalid number of packets: %s",
391           NumServ(&me), NumNick(sptr), parv[4]);
392     return 0;
393   }
394
395   /* Check if a CONNECT would be possible at all (adapted from m_connect) */
396   for (aconf = conf; aconf; aconf = aconf->next)
397     if (aconf->status == CONF_CONNECT_SERVER &&
398         match(parv[1], aconf->name) == 0)
399       break;
400   if (!aconf)
401     for (aconf = conf; aconf; aconf = aconf->next)
402       if (aconf->status == CONF_CONNECT_SERVER &&
403           (match(parv[1], aconf->host) == 0 ||
404           match(parv[1], strchr(aconf->host, '@') + 1) == 0))
405         break;
406   if (!aconf)
407   {
408     if (MyUser(sptr) || Protocol(cptr) < 10)
409       sendto_one(sptr, ":%s NOTICE %s :UPING: Host %s not listed in ircd.conf",
410           me.name, parv[0], parv[1]);
411     else
412       sendto_one(sptr,
413           "%s NOTICE %s%s :UPING: Host %s not listed in ircd.conf",
414           NumServ(&me), NumNick(sptr), parv[1]);
415     return 0;
416   }
417
418   if (AskedPing(sptr))
419     cancel_ping(sptr, sptr);    /* Cancel previous ping request */
420
421   /*
422    * Determine port: First user supplied, then default : 7007
423    */
424   if (BadPtr(parv[2]) || (port = atoi(parv[2])) <= 0)
425     port = atoi(UDP_PORT);
426
427   alarm(2);
428   if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
429   {
430     int err = errno;
431     alarm(0);
432     sendto_ops("m_uping: socket: %s", (err != EAGAIN) ?
433         strerror(err) : "No more sockets");
434     if (MyUser(sptr) || Protocol(cptr) < 10)
435       sendto_one(sptr, ":%s NOTICE %s :UPING: Unable to create udp ping socket",
436           me.name, parv[0]);
437     else
438       sendto_one(sptr,
439           "%s NOTICE %s%s :UPING: Unable to create udp ping socket",
440           NumServ(&me), NumNick(sptr));
441 #ifdef  USE_SYSLOG
442     syslog(LOG_ERR, "Unable to create udp ping socket");
443 #endif
444     return 0;
445   }
446   alarm(0);
447
448   if (fcntl(fd, F_SETFL, FNDELAY) == -1)
449   {
450     sendto_ops("m_uping: fcntl FNDELAY: %s", strerror(errno));
451     if (MyUser(sptr) || Protocol(cptr) < 10)
452       sendto_one(sptr, ":%s NOTICE %s :UPING: Can't set fd non-blocking",
453           me.name, parv[0]);
454     else
455       sendto_one(sptr, "%s NOTICE %s%s :UPING: Can't set fd non-blocking",
456           NumServ(&me), NumNick(sptr));
457     close(fd);
458     return 0;
459   }
460   /*
461    * On some systems, receive and send buffers must be equal in size.
462    * Others block select() when the buffers are too small
463    * (Linux 1.1.50 blocks when < 2048) --Run
464    */
465   opt = 2048;
466   if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (OPT_TYPE *)&opt,
467       sizeof(opt)) < 0 ||
468       setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (OPT_TYPE *)&opt, sizeof(opt)) < 0)
469   {
470     int err = errno;
471     sendto_ops("m_uping: setsockopt SO_SNDBUF|SO_RCVBUF: %s", strerror(err));
472     if (MyUser(sptr) || Protocol(cptr) < 10)
473       sendto_one(sptr, ":%s NOTICE %s :UPING: error in setsockopt: %s",
474           me.name, parv[0], strerror(err));
475     else
476       sendto_one(sptr, "%s NOTICE %s%s :UPING: error in setsockopt: %s",
477           NumServ(&me), NumNick(sptr), strerror(err));
478     close(fd);
479     return 0;
480   }
481
482   if (fd >= MAXCONNECTIONS)
483   {
484     sendto_ops("Can't allocate fd for uping (all connections in use)");
485     if (MyUser(sptr) || Protocol(cptr) < 10)
486       sendto_one(sptr, ":%s NOTICE %s :UPING: All connections in use",
487           me.name, parv[0]);
488     else
489       sendto_one(sptr, "%s NOTICE %s%s :UPING: All connections in use",
490           NumServ(&me), NumNick(sptr));
491     close(fd);
492     return 0;
493   }
494
495   if (fd > highest_fd)
496     highest_fd = fd;
497   loc_clients[fd] = cptr = make_client(NULL, STAT_PING);
498   cptr->confs = (Link *)RunMalloc(UPINGBUFSIZE);        /* Really a (char *) */
499   cptr->fd = fd;
500   cptr->port = port;
501   cptr->hopcount = cptr->receiveB = cptr->sendB = MIN(20, atoi(parv[4]));
502   strcpy(cptr->sockhost, aconf->host);
503   cptr->acpt = sptr;
504   SetAskedPing(sptr);
505   memcpy(&cptr->ip, &aconf->ipnum, sizeof(struct in_addr));
506   strcpy(cptr->name, aconf->name);
507   cptr->firsttime = 0;
508
509   switch (ping_server(cptr))
510   {
511     case 0:
512       break;
513     case -1:
514       del_queries((char *)cptr);
515       end_ping(cptr);
516       break;
517   }
518   return 0;
519 }
520
521 void end_ping(aClient *cptr)
522 {
523   Debug((DEBUG_DEBUG, "end_ping: %p", cptr));
524   if (cptr->acpt)
525   {
526     if (MyUser(cptr->acpt)
527         || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10))
528     {
529       if (cptr->firsttime)      /* Started at all ? */
530       {
531         if (cptr->receiveB != cptr->hopcount)   /* Received any pings at all? */
532         {
533           sendto_one(cptr->acpt, ":%s NOTICE %s :UPING %s%s",
534               me.name, cptr->acpt->name, cptr->name, cptr->buffer);
535           sendto_one(cptr->acpt,
536               ":%s NOTICE %s :UPING Stats: sent %d recvd %d ; "
537               "min/avg/max = %u/%u/%u ms",
538               me.name, cptr->acpt->name, cptr->hopcount - cptr->sendB,
539               cptr->hopcount - cptr->receiveB, cptr->receiveK,
540               (2 * cptr->sendM + cptr->hopcount - cptr->receiveB) /
541               (2 * (cptr->hopcount - cptr->receiveB)), cptr->receiveM);
542         }
543         else
544           sendto_one(cptr->acpt,
545               ":%s NOTICE %s :UPING: no response from %s within %d seconds",
546               me.name, cptr->acpt->name, cptr->name,
547               (int)(now + cptr->since - cptr->firsttime));
548       }
549       else
550         sendto_one(cptr->acpt,
551             ":%s NOTICE %s :UPING: Could not start ping to %s %u",
552             me.name, cptr->acpt->name, cptr->name, cptr->port);
553     }
554     else
555     {
556       if (cptr->firsttime)      /* Started at all ? */
557       {
558         if (cptr->receiveB != cptr->hopcount)   /* Received any pings at all? */
559         {
560           sendto_one(cptr->acpt, "%s NOTICE %s%s :UPING %s%s",
561               NumServ(&me), NumNick(cptr->acpt), cptr->name, cptr->buffer);
562           sendto_one(cptr->acpt,
563               "%s NOTICE %s%s :UPING Stats: sent %d recvd %d ; "
564               "min/avg/max = %u/%u/%u ms",
565               NumServ(&me), NumNick(cptr->acpt), cptr->hopcount - cptr->sendB,
566               cptr->hopcount - cptr->receiveB, cptr->receiveK,
567               (2 * cptr->sendM + cptr->hopcount - cptr->receiveB) /
568               (2 * (cptr->hopcount - cptr->receiveB)), cptr->receiveM);
569         }
570         else
571           sendto_one(cptr->acpt,
572               "%s NOTICE %s%s :UPING: no response from %s within %d seconds",
573               NumServ(&me), NumNick(cptr->acpt), cptr->name,
574               (int)(now + cptr->since - cptr->firsttime));
575       }
576       else
577         sendto_one(cptr->acpt,
578             "%s NOTICE %s%s :UPING: Could not start ping to %s %d",
579             NumServ(&me), NumNick(cptr->acpt), cptr->name, cptr->port);
580     }
581   }
582   close(cptr->fd);
583   loc_clients[cptr->fd] = NULL;
584   if (cptr->acpt)
585     ClearAskedPing(cptr->acpt);
586   RunFree((char *)cptr->confs);
587   free_client(cptr);
588 }
589
590 void cancel_ping(aClient *sptr, aClient *acptr)
591 {
592   int i;
593   aClient *cptr;
594
595   Debug((DEBUG_DEBUG, "Cancelling uping for %p (%s)", sptr, sptr->name));
596   for (i = highest_fd; i >= 0; i--)
597     if ((cptr = loc_clients[i]) && IsPing(cptr) && cptr->acpt == sptr)
598     {
599       cptr->acpt = acptr;
600       del_queries((char *)cptr);
601       end_ping(cptr);
602       break;
603     }
604
605   ClearAskedPing(sptr);
606 }