2 * IRC - Internet Relay Chat, ircd/s_ping.c
3 * Copyright (C) 1994 Carlo Wood ( Run @ undernet.org )
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)
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.
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.
21 #include <sys/socket.h>
26 #include <sys/ioctl.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
63 #define UPINGBUFSIZE 2000 /* Lot bigger then 1024,
64 bit smaller then 2048 */
65 #define UPINGTIMEOUT 120 /* Timeout waitting for first ping response */
70 * As for now, I am abusing the client structure for a ping connection.
71 * .... And I really don't like this solution --Nemesi
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).
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
98 int start_ping(aClient *cptr)
100 struct sockaddr_in remote_addr;
102 Debug((DEBUG_NOTICE, "start_ping(%p) status %d", cptr, cptr->status));
107 memcpy(&remote_addr.sin_addr, &cptr->ip, sizeof(struct in_addr));
109 remote_addr.sin_port = htons(cptr->port + 10000);
111 remote_addr.sin_port = htons(cptr->port);
113 remote_addr.sin_family = AF_INET;
115 if (MyUser(cptr->acpt) || Protocol(cptr->acpt->from) < 10)
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,
122 inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port) - 10000);
124 inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
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,
134 inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port) - 10000);
136 inetntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port));
140 cptr->firsttime = now + UPINGTIMEOUT;
141 cptr->since = UPINGTIMEOUT;
142 cptr->flags |= (FLAGS_PING);
151 void send_ping(aClient *cptr)
153 struct sockaddr_in remote_addr;
156 memcpy(&remote_addr.sin_addr, &cptr->ip, sizeof(struct in_addr));
158 remote_addr.sin_port = htons(cptr->port + 10000);
160 remote_addr.sin_port = htons(cptr->port);
162 remote_addr.sin_family = AF_INET;
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);
168 sprintf((char *)cptr->confs, " %10u%c%6u", tv.tv_sec, '\0', tv.tv_usec);
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));
175 if (sendto(cptr->fd, (char *)cptr->confs, 1024, 0,
176 (struct sockaddr *)&remote_addr, sizeof(struct sockaddr_in)) != 1024)
183 if (MyUser(cptr->acpt)
185 || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10)
188 sendto_one(cptr->acpt, ":%s NOTICE %s :UPING: sendto() failed: %s",
189 me.name, cptr->acpt->name, strerror(get_sockerr(cptr)));
191 sendto_one(cptr->acpt, "%s NOTICE %s%s :UPING: sendto() failed: %s",
192 NumServ(&me), NumNick(cptr->acpt), strerror(get_sockerr(cptr)));
194 Debug((DEBUG_SEND, "send_ping: sendto failed on %d (%d)", cptr->fd, err));
197 else if (--(cptr->sendB) <= 0)
200 if (cptr->receiveB <= 0)
210 void read_ping(aClient *cptr)
212 size_t addr_len = sizeof(struct sockaddr_in);
213 struct sockaddr_in remote_addr;
216 unsigned long int pingtime;
219 memcpy(&remote_addr.sin_addr, &cptr->ip, sizeof(struct in_addr));
221 remote_addr.sin_port = htons(cptr->port + 10000);
223 remote_addr.sin_port = htons(cptr->port);
225 remote_addr.sin_family = AF_INET;
227 gettimeofday(&tv, NULL);
229 if ((len = recvfrom(cptr->fd, (char *)cptr->confs, UPINGBUFSIZE, 0,
230 (struct sockaddr *)&remote_addr, &addr_len)) == -1)
233 if (MyUser(cptr->acpt)
235 || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10)
238 sendto_one(cptr->acpt, ":%s NOTICE %s :UPING: recvfrom: %s",
239 me.name, cptr->acpt->name, strerror(get_sockerr(cptr)));
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));
250 return; /* Broken packet */
252 pingtime = (tv.tv_sec - atoi((char *)cptr->confs + 1)) * 1000 +
253 (tv.tv_usec - atoi((char *)cptr->confs + strlen((char *)cptr->confs) +
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: */
262 cptr->sendM / (100 * (cptr->hopcount - cptr->receiveB + 1))) < 2)
264 cptr->firsttime = tv.tv_sec + cptr->since;
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));
270 s = cptr->buffer + strlen(cptr->buffer);
271 sprintf(s, " %lu", pingtime);
273 if ((--(cptr->receiveB) <= 0 && !DoPing(cptr)) || !(cptr->acpt))
279 int ping_server(aClient *cptr)
281 if ((!cptr->ip.s_addr)
283 && ((cptr->sockhost[2]) != '/')
292 return -1; /* Oper left already */
294 lin.flags = ASYNC_PING;
295 lin.value.cptr = cptr;
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)
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));
307 memcpy(&cptr->ip, hp->h_addr, sizeof(struct in_addr));
311 return start_ping(cptr);
317 * parv[0] = sender prefix
318 * parv[1] = pinged server
320 * parv[3] = hunted server
321 * parv[4] = number of requested pings
323 int m_uping(aClient *cptr, aClient *sptr, int parc, char *parv[])
326 unsigned short int port;
330 if (!IsPrivileged(sptr))
332 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
338 sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UPING");
346 parv[parc++] = UDP_PORT;
347 parv[parc++] = me.name;
352 if (isDigit(*parv[2]))
353 parv[parc++] = me.name;
356 parv[parc++] = parv[2];
363 if (isDigit(*parv[2]))
365 if (isDigit(*parv[3]))
367 parv[parc++] = parv[3];
375 parv[parc++] = parv[3];
381 if (hunt_server(1, cptr, sptr,
382 ":%s UPING %s %s %s %s", 3, parc, parv) != HUNTED_ISME)
385 if (BadPtr(parv[4]) || atoi(parv[4]) <= 0)
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]);
391 sendto_one(sptr, "%s NOTICE %s%s :UPING: Invalid number of packets: %s",
392 NumServ(&me), NumNick(sptr), parv[4]);
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], ':');
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))
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))
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]);
424 "%s NOTICE %s%s :UPING: Host %s not listed in ircd.conf",
425 NumServ(&me), NumNick(sptr), parv[1]);
430 cancel_ping(sptr, sptr); /* Cancel previous ping request */
433 * Determine port: First user supplied, then default : 7007
435 if (!p || (port = atoi(p + 1)) <= 0)
436 port = atoi(UDP_PORT);
439 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
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",
450 "%s NOTICE %s%s :UPING: Unable to create udp ping socket",
451 NumServ(&me), NumNick(sptr));
453 syslog(LOG_ERR, "Unable to create udp ping socket");
459 if (fcntl(fd, F_SETFL, FNDELAY) == -1)
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",
466 sendto_one(sptr, "%s NOTICE %s%s :UPING: Can't set fd non-blocking",
467 NumServ(&me), NumNick(sptr));
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
477 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (OPT_TYPE *)&opt,
479 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (OPT_TYPE *)&opt, sizeof(opt)) < 0)
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));
487 sendto_one(sptr, "%s NOTICE %s%s :UPING: error in setsockopt: %s",
488 NumServ(&me), NumNick(sptr), strerror(err));
493 if (fd >= MAXCONNECTIONS)
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",
500 sendto_one(sptr, "%s NOTICE %s%s :UPING: All connections in use",
501 NumServ(&me), NumNick(sptr));
505 if (bind(fd, (struct sockaddr *)&cserv, sizeof(cserv)) == -1)
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",
512 sendto_one(sptr, "%s NOTICE %s%s :UPING: failure binding udp socket",
513 NumServ(&me), NumNick(sptr));
520 loc_clients[fd] = cptr = make_client(NULL, STAT_PING);
521 cptr->confs = (Link *)RunMalloc(UPINGBUFSIZE); /* Really a (char *) */
524 cptr->hopcount = cptr->receiveB = cptr->sendB = MIN(20, atoi(parv[4]));
525 strcpy(cptr->sockhost, aconf->host);
528 memcpy(&cptr->ip, &aconf->ipnum, sizeof(struct in_addr));
529 strcpy(cptr->name, aconf->name);
532 switch (ping_server(cptr))
537 del_queries((char *)cptr);
544 void end_ping(aClient *cptr)
546 Debug((DEBUG_DEBUG, "end_ping: %p", cptr));
549 if (MyUser(cptr->acpt)
550 || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10))
552 if (cptr->firsttime) /* Started at all ? */
554 if (cptr->receiveB != cptr->hopcount) /* Received any pings at all? */
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);
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));
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);
579 if (cptr->firsttime) /* Started at all ? */
581 if (cptr->receiveB != cptr->hopcount) /* Received any pings at all? */
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);
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));
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);
606 loc_clients[cptr->fd] = NULL;
608 ClearAskedPing(cptr->acpt);
609 RunFree((char *)cptr->confs);
613 void cancel_ping(aClient *sptr, aClient *acptr)
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)
623 del_queries((char *)cptr);
628 ClearAskedPing(sptr);