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",
133 (strerror(errno)) ? strerror(errno) : "Unknown error"));
136 if (!os_set_reuseaddr(fd)) {
137 ircd_log(L_ERROR, "UPING: setsockopt UDP listener: fd %d", fd);
138 Debug((DEBUG_ERROR, "UPING: set reuseaddr on UDP listener failed: %s",
139 (strerror(errno)) ? strerror(errno) : "Unknown error"));
143 if (bind(fd, (struct sockaddr*) &from, sizeof(from)) == -1) {
144 ircd_log(L_ERROR, "UPING: bind UDP listener %d fd %d", htons(from.sin_port), fd);
145 Debug((DEBUG_ERROR, "UPING: bind on UDP listener failed : %s",
146 (strerror(errno)) ? strerror(errno) : "Unknown error"));
150 if (!os_set_nonblocking(fd)) {
151 Debug((DEBUG_ERROR, "UPING: set non-blocking: %s",
152 (strerror(errno)) ? strerror(errno) : "Unknown error"));
161 * max # of pings set to 15/sec.
163 void polludp(int udpfd)
165 struct sockaddr_in from;
166 unsigned int len = 0;
167 static time_t last = 0;
168 static int counter = 0;
169 char buf[BUFSIZE + 1];
171 Debug((DEBUG_DEBUG, "UPING: poll"));
173 if (IO_SUCCESS != os_recvfrom_nonb(udpfd, buf, BUFSIZE, &len, &from))
176 * count em even if we're getting flooded so we can tell we're getting
179 ++ServerStats->uping_recv;
180 if (CurrentTime == last) {
190 sendto(udpfd, buf, len, 0, (struct sockaddr *)&from, sizeof(from));
197 static void start_ping(struct UPing* pptr)
199 assert(valid_ptr((void*) pptr, sizeof(struct UPing)));
201 if (MyUser(pptr->client) || Protocol(pptr->client->from) < 10) {
202 sendto_one(pptr->client,
203 ":%s NOTICE %s :Sending %d ping%s to %s[%s] port %d",
204 me.name, pptr->client->name, pptr->count,
205 (pptr->count == 1) ? "" : "s", pptr->name,
206 ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port));
210 sendto_one(pptr->client,
211 "%s NOTICE %s%s :Sending %d ping%s to %s[%s] port %d",
212 NumServ(&me), NumNick(pptr->client), pptr->count,
213 (pptr->count == 1) ? "" : "s", pptr->name,
214 ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port));
216 pptr->timeout = CurrentTime + UPINGTIMEOUT;
221 * ping_server - get the server host address if not valid
222 * then call start_ping
224 static void ping_server(struct UPing* pptr)
226 if (INADDR_NONE == pptr->sin.sin_addr.s_addr) {
229 if ((s = strchr(pptr->name, '@')))
234 if (INADDR_NONE == (pptr->sin.sin_addr.s_addr = inet_addr(s))) {
235 struct DNSQuery query;
236 struct DNSReply* rpl;
238 query.vptr = (void*) pptr;
239 query.callback = uping_dns_callback;
240 if (0 == (rpl = gethost_byname(s, &query)))
242 memcpy(&pptr->sin.sin_addr, rpl->hp->h_addr, sizeof(struct in_addr));
253 void send_ping(struct UPing* pptr)
256 char buf[BUFSIZE + 1];
259 memset(buf, 0, sizeof(buf));
261 gettimeofday(&tv, NULL);
262 sprintf(buf, " %10lu%c%6lu", tv.tv_sec, '\0', tv.tv_usec);
264 Debug((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d",
266 ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port),
269 if (sendto(pptr->fd, buf, BUFSIZE, 0, (struct sockaddr*) &pptr->sin,
270 sizeof(struct sockaddr_in)) != BUFSIZE)
275 if (MyUser(pptr->client)
277 || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10)
280 sendto_one(pptr->client, ":%s NOTICE %s :UPING: sendto() failed: %s",
281 me.name, pptr->client->name,
282 (strerror(err)) ? strerror(err) : "Unknown error");
284 sendto_one(pptr->client, "%s NOTICE %s%s :UPING: sendto() failed: %s",
285 NumServ(&me), NumNick(pptr->client),
286 (strerror(err)) ? strerror(err) : "Unknown error");
288 Debug((DEBUG_DEBUG, "UPING: send_ping: sendto failed on %d: %s", pptr->fd,
289 (strerror(err)) ? strerror(err) : "Unknown error"));
299 void read_ping(struct UPing* pptr)
301 struct sockaddr_in sin;
304 unsigned int pingtime;
306 char buf[BUFSIZE + 1];
311 gettimeofday(&tv, NULL);
313 ior = os_recvfrom_nonb(pptr->fd, buf, BUFSIZE, &len, &sin);
314 if (IO_BLOCKED == ior)
316 else if (IO_FAILURE == ior) {
318 if (MyUser(pptr->client)
320 || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10)
323 sendto_one(pptr->client, ":%s NOTICE %s :UPING: recvfrom: %s",
324 me.name, pptr->client->name,
325 (strerror(err)) ? strerror(err) : "Unknown error");
327 sendto_one(pptr->client, "%s NOTICE %s%s :UPING: recvfrom: %s",
328 NumServ(&me), NumNick(pptr->client),
329 (strerror(err)) ? strerror(err) : "Unknown error");
330 Debug((DEBUG_SEND, "UPING: read_ping: recvfrom: %d", err));
336 return; /* Broken packet */
339 pingtime = (tv.tv_sec - atol(&buf[1])) * 1000
340 + (tv.tv_usec - atol(buf + strlen(buf) + 1)) / 1000;
342 pptr->ms_ave += pingtime;
343 if (!(pptr->ms_min) || (pptr->ms_min > pingtime))
344 pptr->ms_min = pingtime;
345 if (pingtime > pptr->ms_max)
346 pptr->ms_max = pingtime;
348 pptr->timeout = CurrentTime + UPINGTIMEOUT;
350 Debug((DEBUG_SEND, "read_ping: %d bytes, ti %lu: [%s %s] %lu ms",
351 len, pptr->timeout, buf, (buf + strlen(buf) + 1), pingtime));
353 s = pptr->buf + strlen(pptr->buf);
354 sprintf(s, " %u", pingtime);
356 if (pptr->received == pptr->count)
365 * parv[0] = sender prefix
366 * parv[1] = pinged server
368 * parv[3] = hunted server
369 * parv[4] = number of requested pings
371 int m_uping(struct Client* cptr, struct Client *sptr, int parc, char *parv[])
373 struct ConfItem *aconf;
376 struct UPing* pptr = 0;
378 if (!IsPrivileged(sptr))
380 sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
386 sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UPING");
394 parv[parc++] = UDP_PORT;
395 parv[parc++] = me.name;
400 if (IsDigit(*parv[2]))
401 parv[parc++] = me.name;
404 parv[parc++] = parv[2];
411 if (IsDigit(*parv[2]))
413 if (IsDigit(*parv[3]))
415 parv[parc++] = parv[3];
423 parv[parc++] = parv[3];
429 if (hunt_server(1, cptr, sptr,
430 ":%s UPING %s %s %s %s", 3, parc, parv) != HUNTED_ISME)
433 if (BadPtr(parv[4]) || atoi(parv[4]) <= 0)
435 if (MyUser(sptr) || Protocol(cptr) < 10)
436 sendto_one(sptr, ":%s NOTICE %s :UPING: Illegal number of packets: %s",
437 me.name, parv[0], parv[4]);
439 sendto_one(sptr, "%s NOTICE %s%s :UPING: Illegal number of packets: %s",
440 NumServ(&me), NumNick(sptr), parv[4]);
444 /* Check if a CONNECT would be possible at all (adapted from m_connect) */
445 for (aconf = GlobalConfList; aconf; aconf = aconf->next)
447 if (aconf->status == CONF_SERVER &&
448 match(parv[1], aconf->name) == 0)
453 for (aconf = GlobalConfList; aconf; aconf = aconf->next)
455 if (aconf->status == CONF_SERVER &&
456 (match(parv[1], aconf->host) == 0 ||
457 match(parv[1], strchr(aconf->host, '@') + 1) == 0))
463 if (MyUser(sptr) || Protocol(cptr) < 10)
464 sendto_one(sptr, ":%s NOTICE %s :UPING: Host %s not listed in ircd.conf",
465 me.name, parv[0], parv[1]);
468 "%s NOTICE %s%s :UPING: Host %s not listed in ircd.conf",
469 NumServ(&me), NumNick(sptr), parv[1]);
474 cancel_ping(sptr, sptr); /* Cancel previous ping request */
477 * Determine port: First user supplied, then default : 7007
479 if (BadPtr(parv[2]) || (port = atoi(parv[2])) <= 0)
480 port = atoi(UDP_PORT);
482 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
484 sendto_ops("m_uping: socket: %s", (err != EMFILE)
485 ? ((strerror(err)) ? strerror(err) : "Unknown error") : "No more sockets");
486 if (MyUser(sptr) || Protocol(cptr) < 10)
488 ":%s NOTICE %s :UPING: Unable to create udp ping socket",
492 "%s NOTICE %s%s :UPING: Unable to create udp ping socket",
493 NumServ(&me), NumNick(sptr));
494 ircd_log(L_ERROR, "UPING: Unable to create UDP socket");
498 if (!os_set_nonblocking(fd)) {
499 if (MyUser(sptr) || Protocol(cptr) < 10)
500 sendto_one(sptr, ":%s NOTICE %s :UPING: Can't set fd non-blocking",
503 sendto_one(sptr, "%s NOTICE %s%s :UPING: Can't set fd non-blocking",
504 NumServ(&me), NumNick(sptr));
508 pptr = (struct UPing*) MyMalloc(sizeof(struct UPing));
510 memset(pptr, 0, sizeof(struct UPing));
513 pptr->sin.sin_port = htons(port);
514 pptr->sin.sin_addr.s_addr = aconf->ipnum.s_addr;
515 pptr->sin.sin_family = AF_INET;
516 pptr->count = IRCD_MIN(20, atoi(parv[4]));
517 strcpy(pptr->name, aconf->host);
521 pptr->next = pingList;
529 void end_ping(struct UPing* pptr)
531 Debug((DEBUG_DEBUG, "end_ping: %p", pptr));
532 delete_resolver_queries((void*) pptr);
535 if (MyUser(pptr->client)
536 || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10))
538 if (pptr->lastsent) /* Started at all ? */
540 if (0 < pptr->received) /* Received any pings at all? */
542 sendto_one(pptr->client, ":%s NOTICE %s :UPING %s%s",
543 me.name, pptr->client->name, pptr->name, pptr->buf);
544 /* XXX - warning long unsigned int format, unsigned int arg (7, 8, 9) */
545 sendto_one(pptr->client,
546 ":%s NOTICE %s :UPING Stats: sent %d recvd %d ; "
547 "min/avg/max = %1lu/%1lu/%1lu ms",
548 me.name, pptr->client->name, pptr->sent,
549 pptr->received, pptr->ms_min,
550 (2 * pptr->ms_ave) / (2 * pptr->received),
554 sendto_one(pptr->client,
555 ":%s NOTICE %s :UPING: no response from %s within %d seconds",
556 me.name, pptr->client->name, pptr->name,
560 sendto_one(pptr->client,
561 ":%s NOTICE %s :UPING: Could not start ping to %s %d",
562 me.name, pptr->client->name, pptr->name, ntohs(pptr->sin.sin_port));
566 if (pptr->lastsent) /* Started at all ? */
568 if (0 < pptr->received) /* Received any pings at all? */
570 sendto_one(pptr->client, "%s NOTICE %s%s :UPING %s%s",
571 NumServ(&me), NumNick(pptr->client), pptr->name, pptr->buf);
572 /* XXX - warning: long unsigned int format, unsigned int arg(9, 10, 11) */
573 sendto_one(pptr->client,
574 "%s NOTICE %s%s :UPING Stats: sent %d recvd %d ; "
575 "min/avg/max = %1lu/%1lu/%1lu ms",
576 NumServ(&me), NumNick(pptr->client), pptr->sent,
577 pptr->received, pptr->ms_min,
578 (2 * pptr->ms_ave) / (2 * pptr->received),
582 sendto_one(pptr->client,
583 "%s NOTICE %s%s :UPING: no response from %s within %d seconds",
584 NumServ(&me), NumNick(pptr->client), pptr->name,
588 sendto_one(pptr->client,
589 "%s NOTICE %s%s :UPING: Could not start ping to %s %d",
590 NumServ(&me), NumNick(pptr->client), pptr->name,
591 ntohs(pptr->sin.sin_port));
598 ClearUPing(pptr->client);
602 void cancel_ping(struct Client *sptr, struct Client* acptr)
605 struct UPing* ping_next;
607 Debug((DEBUG_DEBUG, "UPING: cancelling uping for %s", sptr->name));
608 for (ping = pingList; ping; ping = ping_next) {
609 ping_next = ping->next;
610 if (sptr == ping->client) {
611 ping->client = acptr;