2 * IRC - Internet Relay Chat, ircd/uping.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.
24 #include "ircd_alloc.h"
26 #include "ircd_osdep.h"
27 #include "ircd_string.h"
32 #include "s_bsd.h" /* vserv */
40 #include <arpa/inet.h>
47 #include <sys/socket.h>
51 #define UPINGTIMEOUT 60 /* Timeout waiting for ping responses */
54 #define INADDR_NONE 0xffffffff
57 static void ping_server(struct UPing*);
59 static struct UPing* pingList = 0;
60 int UPingListener = -1; /* UDP listener socket for upings */
63 * pings_begin - iterator function for ping list
65 struct UPing* uping_begin(void)
71 * pings_erase - removes ping struct from ping list
73 static void uping_erase(struct UPing* p)
76 struct UPing* last = 0;
80 for (it = pingList; it; last = it = it->next) {
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.
97 static void uping_dns_callback(void* v, struct DNSReply* r)
99 struct UPing* ping = (struct UPing*) v;
100 assert(valid_ptr((void*)ping, sizeof(struct UPing)));
103 memcpy(&ping->sin.sin_addr, r->hp->h_addr, sizeof(struct in_addr));
108 sendto_ops("UDP ping to %s failed: host lookup", ping->name);
115 * Setup a UDP socket and listen for incoming packets
119 struct sockaddr_in from;
122 memset(&from, 0, sizeof(from));
124 from.sin_addr = vserv.sin_addr;
126 from.sin_addr.s_addr = htonl(INADDR_ANY);
128 from.sin_port = htons(atoi(UDP_PORT));
129 from.sin_family = AF_INET;
131 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
132 Debug((DEBUG_ERROR, "UPING: UDP listener socket call failed: %s", strerror(errno)));
135 if (!os_set_reuseaddr(fd)) {
136 ircd_log(L_ERROR, "UPING: setsockopt UDP listener: fd %d", fd);
137 Debug((DEBUG_ERROR, "UPING: set reuseaddr on UDP listener failed: %s", strerror(errno)));
141 if (bind(fd, (struct sockaddr*) &from, sizeof(from)) == -1) {
142 ircd_log(L_ERROR, "UPING: bind UDP listener %d fd %d", htons(from.sin_port), fd);
143 Debug((DEBUG_ERROR, "UPING: bind on UDP listener failed : %s", strerror(errno)));
147 if (!os_set_nonblocking(fd)) {
148 Debug((DEBUG_ERROR, "UPING: set non-blocking: %s", strerror(errno)));
157 * max # of pings set to 15/sec.
159 void polludp(int udpfd)
161 struct sockaddr_in from;
163 static time_t last = 0;
164 static int counter = 0;
165 char buf[BUFSIZE + 1];
167 Debug((DEBUG_DEBUG, "UPING: poll"));
169 if (IO_SUCCESS != os_recvfrom_nonb(udpfd, buf, BUFSIZE, &len, &from))
172 * count em even if we're getting flooded so we can tell we're getting
175 ++ServerStats->uping_recv;
176 if (CurrentTime == last) {
186 sendto(udpfd, buf, len, 0, (struct sockaddr *)&from, sizeof(from));
193 static void start_ping(struct UPing* pptr)
195 assert(valid_ptr((void*) pptr, sizeof(struct UPing)));
197 if (MyUser(pptr->client) || Protocol(pptr->client->from) < 10) {
198 sendto_one(pptr->client,
199 ":%s NOTICE %s :Sending %d ping%s to %s[%s] port %d",
200 me.name, pptr->client->name, pptr->count,
201 (pptr->count == 1) ? "" : "s", pptr->name,
202 ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port));
206 sendto_one(pptr->client,
207 "%s NOTICE %s%s :Sending %d ping%s to %s[%s] port %d",
208 NumServ(&me), NumNick(pptr->client), pptr->count,
209 (pptr->count == 1) ? "" : "s", pptr->name,
210 ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port));
212 pptr->timeout = CurrentTime + UPINGTIMEOUT;
217 * ping_server - get the server host address if not valid
218 * then call start_ping
220 static void ping_server(struct UPing* pptr)
222 if (INADDR_NONE == pptr->sin.sin_addr.s_addr) {
225 if ((s = strchr(pptr->name, '@')))
230 if (INADDR_NONE == (pptr->sin.sin_addr.s_addr = inet_addr(s))) {
231 struct DNSQuery query;
232 struct DNSReply* rpl;
234 query.vptr = (void*) pptr;
235 query.callback = uping_dns_callback;
236 if (0 == (rpl = gethost_byname(s, &query)))
238 memcpy(&pptr->sin.sin_addr, rpl->hp->h_addr, sizeof(struct in_addr));
249 void send_ping(struct UPing* pptr)
252 char buf[BUFSIZE + 1];
255 memset(buf, 0, sizeof(buf));
257 gettimeofday(&tv, NULL);
258 sprintf(buf, " %10lu%c%6lu", tv.tv_sec, '\0', tv.tv_usec);
260 Debug((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d",
262 ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port),
265 if (sendto(pptr->fd, buf, BUFSIZE, 0, (struct sockaddr*) &pptr->sin,
266 sizeof(struct sockaddr_in)) != BUFSIZE)
271 if (MyUser(pptr->client)
273 || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10)
276 sendto_one(pptr->client, ":%s NOTICE %s :UPING: sendto() failed: %s",
277 me.name, pptr->client->name, strerror(errno));
279 sendto_one(pptr->client, "%s NOTICE %s%s :UPING: sendto() failed: %s",
280 NumServ(&me), NumNick(pptr->client), strerror(errno));
282 Debug((DEBUG_DEBUG, "UPING: send_ping: sendto failed on %d: %s", pptr->fd, strerror(err)));
292 void read_ping(struct UPing* pptr)
294 struct sockaddr_in sin;
297 unsigned int pingtime;
299 char buf[BUFSIZE + 1];
304 gettimeofday(&tv, NULL);
306 ior = os_recvfrom_nonb(pptr->fd, buf, BUFSIZE, &len, &sin);
307 if (IO_BLOCKED == ior)
309 else if (IO_FAILURE == ior) {
311 if (MyUser(pptr->client)
313 || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10)
316 sendto_one(pptr->client, ":%s NOTICE %s :UPING: recvfrom: %s",
317 me.name, pptr->client->name, strerror(err));
319 sendto_one(pptr->client, "%s NOTICE %s%s :UPING: recvfrom: %s",
320 NumServ(&me), NumNick(pptr->client), strerror(err));
321 Debug((DEBUG_SEND, "UPING: read_ping: recvfrom: %d", err));
327 return; /* Broken packet */
330 pingtime = (tv.tv_sec - atol(&buf[1])) * 1000
331 + (tv.tv_usec - atol(buf + strlen(buf) + 1)) / 1000;
333 pptr->ms_ave += pingtime;
334 if (!(pptr->ms_min) || (pptr->ms_min > pingtime))
335 pptr->ms_min = pingtime;
336 if (pingtime > pptr->ms_max)
337 pptr->ms_max = pingtime;
339 pptr->timeout = CurrentTime + UPINGTIMEOUT;
341 Debug((DEBUG_SEND, "read_ping: %d bytes, ti %lu: [%s %s] %lu ms",
342 len, pptr->timeout, buf, (buf + strlen(buf) + 1), pingtime));
344 s = pptr->buf + strlen(pptr->buf);
345 sprintf(s, " %u", pingtime);
347 if (pptr->received == pptr->count)
356 * parv[0] = sender prefix
357 * parv[1] = pinged server
359 * parv[3] = hunted server
360 * parv[4] = number of requested pings
362 int m_uping(struct Client* cptr, struct Client *sptr, int parc, char *parv[])
364 struct ConfItem *aconf;
367 struct UPing* pptr = 0;
369 if (!IsPrivileged(sptr))
371 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
377 sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UPING");
385 parv[parc++] = UDP_PORT;
386 parv[parc++] = me.name;
391 if (IsDigit(*parv[2]))
392 parv[parc++] = me.name;
395 parv[parc++] = parv[2];
402 if (IsDigit(*parv[2]))
404 if (IsDigit(*parv[3]))
406 parv[parc++] = parv[3];
414 parv[parc++] = parv[3];
420 if (hunt_server(1, cptr, sptr,
421 ":%s UPING %s %s %s %s", 3, parc, parv) != HUNTED_ISME)
424 if (BadPtr(parv[4]) || atoi(parv[4]) <= 0)
426 if (MyUser(sptr) || Protocol(cptr) < 10)
427 sendto_one(sptr, ":%s NOTICE %s :UPING: Illegal number of packets: %s",
428 me.name, parv[0], parv[4]);
430 sendto_one(sptr, "%s NOTICE %s%s :UPING: Illegal number of packets: %s",
431 NumServ(&me), NumNick(sptr), parv[4]);
435 /* Check if a CONNECT would be possible at all (adapted from m_connect) */
436 for (aconf = GlobalConfList; aconf; aconf = aconf->next)
438 if (aconf->status == CONF_SERVER &&
439 match(parv[1], aconf->name) == 0)
444 for (aconf = GlobalConfList; aconf; aconf = aconf->next)
446 if (aconf->status == CONF_SERVER &&
447 (match(parv[1], aconf->host) == 0 ||
448 match(parv[1], strchr(aconf->host, '@') + 1) == 0))
454 if (MyUser(sptr) || Protocol(cptr) < 10)
455 sendto_one(sptr, ":%s NOTICE %s :UPING: Host %s not listed in ircd.conf",
456 me.name, parv[0], parv[1]);
459 "%s NOTICE %s%s :UPING: Host %s not listed in ircd.conf",
460 NumServ(&me), NumNick(sptr), parv[1]);
465 cancel_ping(sptr, sptr); /* Cancel previous ping request */
468 * Determine port: First user supplied, then default : 7007
470 if (BadPtr(parv[2]) || (port = atoi(parv[2])) <= 0)
471 port = atoi(UDP_PORT);
473 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
475 sendto_ops("m_uping: socket: %s", (err != EMFILE)
476 ? strerror(err) : "No more sockets");
477 if (MyUser(sptr) || Protocol(cptr) < 10)
479 ":%s NOTICE %s :UPING: Unable to create udp ping socket",
483 "%s NOTICE %s%s :UPING: Unable to create udp ping socket",
484 NumServ(&me), NumNick(sptr));
485 ircd_log(L_ERROR, "UPING: Unable to create UDP socket");
489 if (!os_set_nonblocking(fd)) {
490 if (MyUser(sptr) || Protocol(cptr) < 10)
491 sendto_one(sptr, ":%s NOTICE %s :UPING: Can't set fd non-blocking",
494 sendto_one(sptr, "%s NOTICE %s%s :UPING: Can't set fd non-blocking",
495 NumServ(&me), NumNick(sptr));
499 pptr = (struct UPing*) MyMalloc(sizeof(struct UPing));
501 memset(pptr, 0, sizeof(struct UPing));
504 pptr->sin.sin_port = htons(port);
505 pptr->sin.sin_addr.s_addr = aconf->ipnum.s_addr;
506 pptr->sin.sin_family = AF_INET;
507 pptr->count = IRCD_MIN(20, atoi(parv[4]));
508 strcpy(pptr->name, aconf->host);
512 pptr->next = pingList;
520 void end_ping(struct UPing* pptr)
522 Debug((DEBUG_DEBUG, "end_ping: %p", pptr));
523 delete_resolver_queries((void*) pptr);
526 if (MyUser(pptr->client)
527 || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10))
529 if (pptr->lastsent) /* Started at all ? */
531 if (0 < pptr->received) /* Received any pings at all? */
533 sendto_one(pptr->client, ":%s NOTICE %s :UPING %s%s",
534 me.name, pptr->client->name, pptr->name, pptr->buf);
535 /* XXX - warning long unsigned int format, unsigned int arg (7, 8, 9) */
536 sendto_one(pptr->client,
537 ":%s NOTICE %s :UPING Stats: sent %d recvd %d ; "
538 "min/avg/max = %1lu/%1lu/%1lu ms",
539 me.name, pptr->client->name, pptr->sent,
540 pptr->received, pptr->ms_min,
541 (2 * pptr->ms_ave) / (2 * pptr->received),
545 sendto_one(pptr->client,
546 ":%s NOTICE %s :UPING: no response from %s within %d seconds",
547 me.name, pptr->client->name, pptr->name,
551 sendto_one(pptr->client,
552 ":%s NOTICE %s :UPING: Could not start ping to %s %d",
553 me.name, pptr->client->name, pptr->name, ntohs(pptr->sin.sin_port));
557 if (pptr->lastsent) /* Started at all ? */
559 if (0 < pptr->received) /* Received any pings at all? */
561 sendto_one(pptr->client, "%s NOTICE %s%s :UPING %s%s",
562 NumServ(&me), NumNick(pptr->client), pptr->name, pptr->buf);
563 /* XXX - warning: long unsigned int format, unsigned int arg(9, 10, 11) */
564 sendto_one(pptr->client,
565 "%s NOTICE %s%s :UPING Stats: sent %d recvd %d ; "
566 "min/avg/max = %1lu/%1lu/%1lu ms",
567 NumServ(&me), NumNick(pptr->client), pptr->sent,
568 pptr->received, pptr->ms_min,
569 (2 * pptr->ms_ave) / (2 * pptr->received),
573 sendto_one(pptr->client,
574 "%s NOTICE %s%s :UPING: no response from %s within %d seconds",
575 NumServ(&me), NumNick(pptr->client), pptr->name,
579 sendto_one(pptr->client,
580 "%s NOTICE %s%s :UPING: Could not start ping to %s %d",
581 NumServ(&me), NumNick(pptr->client), pptr->name,
582 ntohs(pptr->sin.sin_port));
589 ClearUPing(pptr->client);
593 void cancel_ping(struct Client *sptr, struct Client* acptr)
596 struct UPing* ping_next;
598 Debug((DEBUG_DEBUG, "UPING: cancelling uping for %s", sptr->name));
599 for (ping = pingList; ping; ping = ping_next) {
600 ping_next = ping->next;
601 if (sptr == ping->client) {
602 ping->client = acptr;