/************************************************************************ * IRC - Internet Relay Chat, ircd/s_ping.c * Copyright (C) 1994 Carlo K ( Run @ undernet.org ) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef lint static char sccsid[] = "@(#)s_ping.c 1.0 9/21/94 (C) 1994 Carlo Kid"; #endif #include "struct.h" #include "common.h" #include "sys.h" #include "numeric.h" #include "patchlevel.h" #include #include #include #ifdef UNIXPORT # include #endif #if defined(__hpux) # include "inet.h" #endif #include #include "sock.h" /* If FD_ZERO isn't defined up to this point, */ /* define it (BSD4.2 needs this) */ #include "h.h" #define UPINGBUFSIZE 2000 /* Lot bigger then 1024, bit smaller then 2048 */ #define UPINGTIMEOUT 120 /* Timeout waitting for first ping response */ /* * start_ping * * As for now, I am abusing the client structure for a ping connection. * Used members are: * These are used by existing routines as well, and do have their own meaning: * fd : The socket file descriptor. * status : To flag that this IS one of these abused ping structures * sockhost : Name of requested server to ping (aconf->host). * name : aconf->name * ip : ip# * These have more or less their own meaning, * but are not used by existing routines: * flags : To flag that a next ping is requested. * port : Requested remote port. * These are only used by the 'uping' routines * and have totally different meanings: * buffer : buffer hold pingtimes of received packets * confs : recv/send (char *) buffer. * hopcount : Total number of requested pings * count : Number of pings left to send. * hashv : Number of pings left to be received. * acpt : client asking for this ping * lasttime : last time a ping was sent * firsttime: recvfrom timeout * since : timeout in seconds to next recvfrom * receiveK : minimum in ms * sendM : average in ms * receiveM : maximum in ms */ int start_ping(cptr) Reg1 aClient *cptr; { struct sockaddr_in remote_addr; Debug((DEBUG_NOTICE,"start_ping(%x) status %d", cptr, cptr->status)); if (!(cptr->acpt)) return -1; bcopy((char *)&cptr->ip, (char *)&remote_addr.sin_addr, sizeof(struct in_addr)); #ifdef TESTNET remote_addr.sin_port = htons(cptr->port + 10000); #else remote_addr.sin_port = htons(cptr->port); #endif remote_addr.sin_family = AF_INET; sendto_one(cptr->acpt, ":%s NOTICE %s :Sending %d ping%s to %s[%s] port %d", me.name, cptr->acpt->name, cptr->hopcount, (cptr->hopcount == 1) ? "" : "s", cptr->name, #ifdef TESTNET inetntoa((char *)&remote_addr.sin_addr), ntohs(remote_addr.sin_port) - 10000); #else inetntoa((char *)&remote_addr.sin_addr), ntohs(remote_addr.sin_port)); #endif cptr->firsttime = now + UPINGTIMEOUT; cptr->since = UPINGTIMEOUT; cptr->flags |= (FLAGS_PING); return 0; } /* * send_ping * */ void send_ping(cptr) aClient *cptr; { struct sockaddr_in remote_addr; struct timeval tv; bcopy((char *)&cptr->ip, (char *)&remote_addr.sin_addr, sizeof(struct in_addr)); #ifdef TESTNET remote_addr.sin_port = htons(cptr->port + 10000); #else remote_addr.sin_port = htons(cptr->port); #endif remote_addr.sin_family = AF_INET; (void) gettimeofday(&tv, NULL); (void)sprintf((char *)cptr->confs, " %10lu%c%6lu", tv.tv_sec, '\0', tv.tv_usec); Debug((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d", (char *)cptr->confs, (char *)cptr->confs + 12, inetntoa((char *)&remote_addr.sin_addr), ntohs(remote_addr.sin_port), cptr->fd)); if (sendto(cptr->fd, (char *)cptr->confs, 1024, 0, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr_in)) != 1024) { int err=errno; if (cptr->acpt) sendto_one(cptr->acpt, ":%s NOTICE %s :UPING: sendto() failed: %s", me.name, cptr->acpt->name, strerror(get_sockerr(cptr))); Debug((DEBUG_SEND, "send_ping: sendto failed on %d (%d)", cptr->fd, err)); end_ping(cptr); } else if (--(cptr->count) <= 0 ) { ClearPing(cptr); if (cptr->hashv <= 0) end_ping(cptr); } return; } /* * read_ping * */ void read_ping(cptr) aClient *cptr; { socklen_t addr_len = sizeof(struct sockaddr_in); struct sockaddr_in remote_addr; struct timeval tv; int len; unsigned long int pingtime; char *s; bcopy((char *)&cptr->ip, (char *)&remote_addr.sin_addr, sizeof(struct in_addr)); #ifdef TESTNET remote_addr.sin_port = htons(cptr->port + 10000); #else remote_addr.sin_port = htons(cptr->port); #endif remote_addr.sin_family = AF_INET; (void)gettimeofday(&tv, NULL); if ((len=recvfrom(cptr->fd, (char *)cptr->confs, UPINGBUFSIZE, 0, (struct sockaddr *)&remote_addr, &addr_len)) == -1) { int err = errno; sendto_one(cptr->acpt, ":%s NOTICE %s :UPING: recvfrom: %s", me.name, cptr->acpt->name, strerror(get_sockerr(cptr))); Debug((DEBUG_SEND, "read_ping: recvfrom: %d", err)); if (err != EAGAIN) end_ping(cptr); return; } if (len<19) return; /* Broken packet */ pingtime = (tv.tv_sec - atoi((char *)cptr->confs + 1)) * 1000 + (tv.tv_usec - atoi((char *)cptr->confs + strlen((char *)cptr->confs) + 1)) / 1000; cptr->sendM += pingtime; if (!(cptr->receiveK) || (cptr->receiveK > pingtime)) cptr->receiveK = pingtime; if (pingtime > cptr->receiveM) cptr->receiveM = pingtime; /* Wait at most 10 times the average pingtime for the next one: */ if ((cptr->since = cptr->sendM / ( 100 * (cptr->hopcount - cptr->hashv + 1))) < 2) cptr->since = 2; cptr->firsttime = tv.tv_sec + cptr->since; Debug((DEBUG_DEBUG, "read_ping: %d bytes, ti %lu: [%s %s] %u ms", len, cptr->since, (char *)cptr->confs, (char *)cptr->confs + strlen((char *)cptr->confs) + 1, pingtime)); s = cptr->buffer + strlen(cptr->buffer); sprintf(s, " %lu", pingtime); if ((--(cptr->hashv) <= 0 && !DoPing(cptr)) || !(cptr->acpt)) end_ping(cptr); return; } int ping_server(cptr, hp) aClient *cptr; struct hostent *hp; { if ( ( !cptr->ip.s_addr ) #ifdef UNIXPORT && ( ( cptr->sockhost[2] ) != '/' ) #endif ) { struct hostent *hp; char *s; Link lin; if (!(cptr->acpt)) return -1; /* Oper left already */ lin.flags = ASYNC_PING; lin.value.cptr = cptr; nextdnscheck = 1; s = (char *)index(cptr->sockhost, '@'); s++; /* should never be NULL; cptr->sockhost is actually a conf->host */ if ((cptr->ip.s_addr = inet_addr(s)) == -1) { cptr->ip.s_addr = 0; hp = gethost_byname(s, &lin); Debug((DEBUG_NOTICE, "ping_sv: hp %x ac %x ho %s", hp, cptr, s)); if (!hp) return 0; bcopy(hp->h_addr, (char *)&cptr->ip, sizeof(struct in_addr)); } } return start_ping(cptr); } /* ** m_uping -- by Run ** ** parv[0] = sender prefix ** parv[1] = pinged server ** parv[2] = port ** parv[3] = hunted server ** parv[4] = number of requested pings */ int m_uping(cptr, sptr, parc, parv) aClient *cptr, *sptr; int parc; char *parv[]; { aConfItem *aconf; int port = 7007, fd, opt; if (!IsPrivileged(sptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return -1; } if (parc < 2) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UPING"); return 0; } if (MyClient(sptr)) { if (parc == 2) { parv[parc++]=UDP_PORT; parv[parc++]=me.name; parv[parc++]="5"; } else if (parc == 3) { if (isdigit(*parv[2])) { parv[parc++]=me.name; } else { parv[parc++]=parv[2]; parv[2]=UDP_PORT; } parv[parc++]="5"; } else if (parc == 4) { if (isdigit(*parv[2])) { if (isdigit(*parv[3])) { parv[parc++]=parv[3]; parv[3]=me.name; } else parv[parc++]="5"; } else { parv[parc++]=parv[3]; parv[3]=parv[2]; parv[2]=UDP_PORT; } } } if (hunt_server(cptr,sptr,":%s UPING %s %s %s %s",3,parc,parv) != HUNTED_ISME) return 0; if (BadPtr(parv[4]) || atoi(parv[4])<=0) { sendto_one(sptr,":%s NOTICE %s :UPING: Illegal number of packets: %s", me.name, parv[0], parv[4]); return 0; } /* Check if a CONNECT would be possible at all (adapted from m_connect) */ for (aconf = conf; aconf; aconf = aconf->next) if (aconf->status == CONF_CONNECT_SERVER && matches(parv[1], aconf->name) == 0) break; if (!aconf) for (aconf = conf; aconf; aconf = aconf->next) if (aconf->status == CONF_CONNECT_SERVER && (matches(parv[1], aconf->host) == 0 || matches(parv[1], index(aconf->host, '@')+1) == 0)) break; if (!aconf) { sendto_one(sptr, "NOTICE %s :UPING: Host %s not listed in ircd.conf", parv[0], parv[1]); return 0; } if (AskedPing(sptr)) cancel_ping(sptr, sptr); /* Cancel previous ping request */ /* * Determine port: First user supplied, then default : 7007 */ if (!BadPtr(parv[2]) && (port = atoi(parv[2])) <= 0) port=atoi(UDP_PORT); (void)alarm(2); if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { int err = errno; (void)alarm(0); sendto_ops("m_uping: socket: %s", (err != EAGAIN) ? strerror(err) : "No more sockets"); sendto_one(sptr, ":%s NOTICE %s :UPING: Unable to create udp ping socket", me.name, parv[0]); #ifdef USE_SYSLOG syslog(LOG_ERR, "Unable to create udp ping socket"); #endif return 0; } (void)alarm(0); if (fcntl(fd, F_SETFL, FNDELAY)==-1) { sendto_ops("m_uping: fcntl FNDELAY: %s", strerror(errno)); sendto_one(sptr, ":%s NOTICE %s :UPING: Can't set fd non-blocking", me.name, parv[0]); close(fd); return 0; } /* ** On some systems, receive and send buffers must be equal in size. ** Others block select() when the buffers are too small ** (Linux 1.1.50 blocks when < 2048) --Run */ opt = 2048; if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (OPT_TYPE *)&opt, sizeof(opt)) < 0 || setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (OPT_TYPE *)&opt, sizeof(opt)) < 0) { int err=errno; sendto_ops("m_uping: setsockopt SO_SNDBUF|SO_RCVBUF: %s", strerror(err)); sendto_one(sptr, ":%s NOTICE %s :UPING: error in setsockopt: %s", me.name, parv[0], strerror(err)); close(fd); return 0; } if (fd >= MAXCONNECTIONS) { sendto_ops("Can't allocate fd for uping (all connections in use)"); sendto_one(sptr, ":%s NOTICE %s :UPING: All connections in use", me.name, parv[0]); close(fd); return 0; } if (fd > highest_fd) highest_fd = fd; local[fd] = cptr = make_client(NULL); cptr->confs = (Link *)MyMalloc(UPINGBUFSIZE); /* Really a (char *) */ cptr->fd = fd; cptr->port = port; cptr->hopcount = cptr->hashv = cptr->count = MIN(20, atoi(parv[4])); strcpy(cptr->sockhost, aconf->host); cptr->acpt = sptr; SetAskedPing(sptr); bcopy((void *)&aconf->ipnum, (void *)&cptr->ip, sizeof(struct in_addr)); strcpy(cptr->name, aconf->name); cptr->firsttime = 0; SetPing(cptr); switch (ping_server(cptr, NULL)) { case 0: break; case -1: del_queries((char *)cptr); end_ping(cptr); break; } return 0; } void end_ping(cptr) register aClient *cptr; { Debug((DEBUG_DEBUG,"end_ping: %x", cptr)); if (cptr->acpt) { if (cptr->firsttime) /* Started at all ? */ { if (cptr->hashv != cptr->hopcount) /* Received any pings at all ? */ { sendto_one(cptr->acpt, ":%s NOTICE %s :UPING %s%s", me.name, cptr->acpt->name, cptr->name, cptr->buffer); sendto_one(cptr->acpt, ":%s NOTICE %s :UPING Stats: sent %d recvd %d ; min/avg/max = %lu/%lu/%lu ms", me.name, cptr->acpt->name, cptr->hopcount - cptr->count, cptr->hopcount - cptr->hashv, cptr->receiveK, (2 * cptr->sendM + cptr->hopcount - cptr->hashv) / (2 * ( cptr->hopcount - cptr->hashv)), cptr->receiveM); } else sendto_one(cptr->acpt, ":%s NOTICE %s :UPING: no response from %s within %d seconds", me.name, cptr->acpt->name, cptr->name, now + cptr->since - cptr->firsttime); } else sendto_one(cptr->acpt, ":%s NOTICE %s :UPING: Could not start ping to %s %d", me.name, cptr->acpt->name, cptr->name, cptr->port); } (void)close(cptr->fd); local[cptr->fd] = NULL; if (cptr->acpt) ClearAskedPing(cptr->acpt); MyFree((char *)cptr->confs); free_client(cptr); } void cancel_ping(sptr, acptr) aClient *sptr, *acptr; { int i; aClient *cptr; Debug((DEBUG_DEBUG, "Cancelling uping for %x (%s)", sptr, sptr->name)); for (i = highest_fd; i >= 0; i--) if ((cptr = local[i]) && IsPing(cptr) && cptr->acpt == sptr) { cptr->acpt = acptr; del_queries((char *)cptr); end_ping(cptr); break; } ClearAskedPing(sptr); }