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;
329 if (!IsPrivileged(sptr))
331 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
337 sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UPING");
345 parv[parc++] = UDP_PORT;
346 parv[parc++] = me.name;
351 if (isDigit(*parv[2]))
352 parv[parc++] = me.name;
355 parv[parc++] = parv[2];
362 if (isDigit(*parv[2]))
364 if (isDigit(*parv[3]))
366 parv[parc++] = parv[3];
374 parv[parc++] = parv[3];
380 if (hunt_server(1, cptr, sptr,
381 ":%s UPING %s %s %s %s", 3, parc, parv) != HUNTED_ISME)
384 if (BadPtr(parv[4]) || atoi(parv[4]) <= 0)
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]);
390 sendto_one(sptr, "%s NOTICE %s%s :UPING: Invalid number of packets: %s",
391 NumServ(&me), NumNick(sptr), parv[4]);
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)
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))
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]);
413 "%s NOTICE %s%s :UPING: Host %s not listed in ircd.conf",
414 NumServ(&me), NumNick(sptr), parv[1]);
419 cancel_ping(sptr, sptr); /* Cancel previous ping request */
422 * Determine port: First user supplied, then default : 7007
424 if (BadPtr(parv[2]) || (port = atoi(parv[2])) <= 0)
425 port = atoi(UDP_PORT);
428 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
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",
439 "%s NOTICE %s%s :UPING: Unable to create udp ping socket",
440 NumServ(&me), NumNick(sptr));
442 syslog(LOG_ERR, "Unable to create udp ping socket");
448 if (fcntl(fd, F_SETFL, FNDELAY) == -1)
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",
455 sendto_one(sptr, "%s NOTICE %s%s :UPING: Can't set fd non-blocking",
456 NumServ(&me), NumNick(sptr));
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
466 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (OPT_TYPE *)&opt,
468 setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (OPT_TYPE *)&opt, sizeof(opt)) < 0)
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));
476 sendto_one(sptr, "%s NOTICE %s%s :UPING: error in setsockopt: %s",
477 NumServ(&me), NumNick(sptr), strerror(err));
482 if (fd >= MAXCONNECTIONS)
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",
489 sendto_one(sptr, "%s NOTICE %s%s :UPING: All connections in use",
490 NumServ(&me), NumNick(sptr));
497 loc_clients[fd] = cptr = make_client(NULL, STAT_PING);
498 cptr->confs = (Link *)RunMalloc(UPINGBUFSIZE); /* Really a (char *) */
501 cptr->hopcount = cptr->receiveB = cptr->sendB = MIN(20, atoi(parv[4]));
502 strcpy(cptr->sockhost, aconf->host);
505 memcpy(&cptr->ip, &aconf->ipnum, sizeof(struct in_addr));
506 strcpy(cptr->name, aconf->name);
509 switch (ping_server(cptr))
514 del_queries((char *)cptr);
521 void end_ping(aClient *cptr)
523 Debug((DEBUG_DEBUG, "end_ping: %p", cptr));
526 if (MyUser(cptr->acpt)
527 || (IsServer(cptr->acpt->from) && Protocol(cptr->acpt->from) < 10))
529 if (cptr->firsttime) /* Started at all ? */
531 if (cptr->receiveB != cptr->hopcount) /* Received any pings at all? */
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);
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));
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);
556 if (cptr->firsttime) /* Started at all ? */
558 if (cptr->receiveB != cptr->hopcount) /* Received any pings at all? */
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);
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));
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);
583 loc_clients[cptr->fd] = NULL;
585 ClearAskedPing(cptr->acpt);
586 RunFree((char *)cptr->confs);
590 void cancel_ping(aClient *sptr, aClient *acptr)
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)
600 del_queries((char *)cptr);
605 ClearAskedPing(sptr);