added basic ssl support to ircu
[ircu2.10.12-pk.git] / ircd / uping.c
index 68a8121a9f504fdc122808bc3bf39863782d5ba3..62b69560db348c636e148f2f967d99e487746c6f 100644 (file)
  * 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.
- *
- * $Id$
  */
+/** @file
+ * @brief UDP ping implementation.
+ * @version $Id$
+ */
+#include "config.h"
+
 #include "uping.h"
 #include "client.h"
 #include "ircd.h"
 #include "ircd_alloc.h"
+#include "ircd_events.h"
 #include "ircd_log.h"
 #include "ircd_osdep.h"
 #include "ircd_string.h"
 #include "match.h"
+#include "msg.h"
 #include "numeric.h"
 #include "numnicks.h"
-#include "res.h"
-#include "s_bsd.h"    /* vserv */
+#include "s_bsd.h"    /* VirtualHost */
 #include "s_conf.h"
 #include "s_debug.h"
 #include "s_misc.h"
@@ -37,8 +42,7 @@
 #include "send.h"
 #include "sys.h"
 
-#include <arpa/inet.h>
-#include <assert.h>
+/* #include <assert.h> -- Now using assert in ircd_log.h */
 #include <errno.h>
 #include <netdb.h>
 #include <stdio.h>
 #include <sys/time.h>
 #include <unistd.h>
 
-#define UPINGTIMEOUT 60   /* Timeout waiting for ping responses */
-
-#ifndef INADDR_NONE
-#define INADDR_NONE 0xffffffff
-#endif
-
-static void ping_server(struct UPing*);
+#define UPINGTIMEOUT 60   /**< Timeout waiting for ping responses */
 
-static struct UPing* pingList = 0;
-int UPingListener = -1; /* UDP listener socket for upings */
+static struct UPing* pingList = 0; /**< Linked list of UPing structs */
+static struct Socket upingSock_v4; /**< Socket struct for IPv4 upings */
+static struct Socket upingSock_v6; /**< Socket struct for IPv6 upings */
 
-/*
- * pings_begin - iterator function for ping list 
+/** Start iteration of uping list.
+ * @return Start of uping list.
  */
 struct UPing* uping_begin(void)
 {
   return pingList;
 }
 
-/*
- * pings_erase - removes ping struct from ping list
+/** Removes \a p from uping list.
+ * @param[in,out] p UPing to remove from list.
  */
 static void uping_erase(struct UPing* p)
 {
@@ -76,8 +75,8 @@ static void uping_erase(struct UPing* p)
   struct UPing* last = 0;
 
   assert(0 != p);
-  
-  for (it = pingList; it; last = it = it->next) {
+
+  for (it = pingList; it; last = it, it = it->next) {
     if (p == it) {
       if (last)
         last->next = p->next;
@@ -88,217 +87,230 @@ static void uping_erase(struct UPing* p)
   }
 }
 
-/*
- * uping_dns_callback - this gets called when the resolver either
- * succeeds or fails to locate the servers address.
- * If the dns query failed hp will be 0, otherwise it
- * will contain the stuff a hostent normally contains.
+/** Callback for uping listener socket.
+ * Reads a uping from the socket and respond, but not more than 10
+ * times per second.
+ * @param[in] ev I/O event for uping socket.
  */
-static void uping_dns_callback(void* v, struct DNSReply* r)
+static void uping_echo_callback(struct Event* ev)
 {
-  struct UPing* ping = (struct UPing*) v;
-  assert(valid_ptr((void*)ping, sizeof(struct UPing)));
+  struct Socket      *sock;
+  struct irc_sockaddr from;
+  unsigned int       len = 0;
+  static time_t      last = 0;
+  static int         counter = 0;
+  char               buf[BUFSIZE + 1];
 
-  if (r) {
-    memcpy(&ping->sin.sin_addr, r->hp->h_addr, sizeof(struct in_addr));
-    ping_server(ping);
-  }
-  else
-  {
-    sendto_ops("UDP ping to %s failed: host lookup", ping->name);
-    end_ping(ping);
-  }
-}
+  assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
+  sock = ev_socket(ev);
+  assert(sock == &upingSock_v4 || sock == &upingSock_v6);
 
+  Debug((DEBUG_DEBUG, "UPING: uping_echo"));
 
-/*
- * Setup a UDP socket and listen for incoming packets
+  if (IO_SUCCESS != os_recvfrom_nonb(s_fd(sock), buf, BUFSIZE, &len, &from))
+    return;
+  /*
+   * count em even if we're getting flooded so we can tell we're getting
+   * flooded.
+   */
+  ++ServerStats->uping_recv;
+  if (len < 19)
+    return;
+  else if (CurrentTime != last) {
+    counter = 0;
+    last = CurrentTime;
+  } else if (++counter > 10)
+    return;
+  os_sendto_nonb(s_fd(sock), buf, len, NULL, 0, &from);
+}
+
+/** Initialize a UDP socket for upings.
+ * @returns 0 on success, -1 on error.
  */
 int uping_init(void)
 {
-  struct sockaddr_in from;
+  struct irc_sockaddr from;
   int fd;
 
-  memset(&from, 0, sizeof(from));
-#ifdef VIRTUAL_HOST
-  from.sin_addr = vserv.sin_addr;
-#else
-  from.sin_addr.s_addr = htonl(INADDR_ANY);
-#endif
-  from.sin_port = htons(atoi(UDP_PORT));
-  from.sin_family = AF_INET;
+  memcpy(&from, &VirtualHost_v4, sizeof(from));
+  from.port = atoi(UDP_PORT);
 
-  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
-    Debug((DEBUG_ERROR, "UPING: UDP listener socket call failed: %s", 
-           (strerror(errno)) ? strerror(errno) : "Unknown error"));
+  fd = os_socket(&from, SOCK_DGRAM, "IPv4 uping listener", AF_INET);
+  if (fd < 0)
     return -1;
-  }
-  if (!os_set_reuseaddr(fd)) {
-    ircd_log(L_ERROR, "UPING: setsockopt UDP listener: fd %d", fd);
-    Debug((DEBUG_ERROR, "UPING: set reuseaddr on UDP listener failed: %s",
-           (strerror(errno)) ? strerror(errno) : "Unknown error"));
+  if (!socket_add(&upingSock_v4, uping_echo_callback, 0, SS_DATAGRAM,
+                  SOCK_EVENT_READABLE, fd)) {
+    Debug((DEBUG_ERROR, "UPING: Unable to queue fd to event system"));
     close(fd);
     return -1;
   }
-  if (bind(fd, (struct sockaddr*) &from, sizeof(from)) == -1) {
-    ircd_log(L_ERROR, "UPING: bind UDP listener %d fd %d", htons(from.sin_port), fd);
-    Debug((DEBUG_ERROR, "UPING: bind on UDP listener failed : %s",
-           (strerror(errno)) ? strerror(errno) : "Unknown error"));
-    close(fd);
+
+#ifdef AF_INET6
+  memcpy(&from, &VirtualHost_v6, sizeof(from));
+  from.port = atoi(UDP_PORT);
+
+  fd = os_socket(&from, SOCK_DGRAM, "IPv6 uping listener", AF_INET6);
+  if (fd < 0)
     return -1;
-  }
-  if (!os_set_nonblocking(fd)) {
-    Debug((DEBUG_ERROR, "UPING: set non-blocking: %s",
-           (strerror(errno)) ? strerror(errno) : "Unknown error"));
+  if (!socket_add(&upingSock_v6, uping_echo_callback, 0, SS_DATAGRAM,
+                  SOCK_EVENT_READABLE, fd)) {
+    Debug((DEBUG_ERROR, "UPING: Unable to queue fd to event system"));
     close(fd);
     return -1;
   }
-  return fd;
+#endif
+
+  return 0;
 }
 
 
-/*
- * max # of pings set to 15/sec.
+/** Callback for socket activity on an outbound uping socket.
+ * @param[in] ev I/O event for socket.
  */
-void polludp(int udpfd)
+static void uping_read_callback(struct Event* ev)
 {
-  struct sockaddr_in from;
-  unsigned int       len = 0;
-  static time_t      last = 0;
-  static int         counter = 0;
-  char               buf[BUFSIZE + 1];
+  struct UPing *pptr;
 
-  Debug((DEBUG_DEBUG, "UPING: poll"));
+  assert(0 != ev_socket(ev));
+  assert(0 != s_data(ev_socket(ev)));
 
-  if (IO_SUCCESS != os_recvfrom_nonb(udpfd, buf, BUFSIZE, &len, &from))
-    return;
-  /*
-   * count em even if we're getting flooded so we can tell we're getting
-   * flooded.
-   */
-  ++ServerStats->uping_recv;
-  if (CurrentTime == last) {
-    if (++counter > 10)
-      return;
-  }
-  else {
-    counter = 0;
-    last    = CurrentTime;
+  pptr = (struct UPing*) s_data(ev_socket(ev));
+
+  Debug((DEBUG_SEND, "uping_read_callback called, %p (%d)", pptr,
+        ev_type(ev)));
+
+  if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
+    pptr->freeable &= ~UPING_PENDING_SOCKET;
+
+    if (!pptr->freeable)
+      MyFree(pptr); /* done with it, finally */
+  } else {
+    assert(ev_type(ev) == ET_READ || ev_type(ev) == ET_ERROR);
+
+    uping_read(pptr); /* read uping response */
   }
-  if (len < 19)
-    return;
-  sendto(udpfd, buf, len, 0, (struct sockaddr *)&from, sizeof(from));
 }
 
-
-/*
- * start_ping
+/** Timer callback to send another outbound uping.
+ * @param[in] ev Event for uping timer.
  */
-static void start_ping(struct UPing* pptr)
+static void uping_sender_callback(struct Event* ev)
 {
-  assert(valid_ptr((void*) pptr, sizeof(struct UPing)));
-
-  if (MyUser(pptr->client) || Protocol(pptr->client->from) < 10) {
-    sendto_one(pptr->client,
-       ":%s NOTICE %s :Sending %d ping%s to %s[%s] port %d",
-       me.name, pptr->client->name, pptr->count,
-       (pptr->count == 1) ? "" : "s", pptr->name,
-       ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port));
-  }
-  else
-  {
-    sendto_one(pptr->client,
-       "%s NOTICE %s%s :Sending %d ping%s to %s[%s] port %d",
-       NumServ(&me), NumNick(pptr->client), pptr->count,
-       (pptr->count == 1) ? "" : "s", pptr->name,
-       ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port));
+  struct UPing *pptr;
+
+  assert(0 != ev_timer(ev));
+  assert(0 != t_data(ev_timer(ev)));
+
+  pptr = (struct UPing*) t_data(ev_timer(ev));
+
+  Debug((DEBUG_SEND, "uping_sender_callback called, %p (%d)", pptr,
+        ev_type(ev)));
+
+  if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
+    pptr->freeable &= ~UPING_PENDING_SENDER;
+
+    if (!pptr->freeable)
+      MyFree(pptr); /* done with it, finally */
+  } else {
+    assert(ev_type(ev) == ET_EXPIRE);
+
+    pptr->lastsent = CurrentTime; /* store last ping time */
+    uping_send(pptr); /* send a ping */
+
+    if (pptr->sent == pptr->count) /* done sending pings, don't send more */
+      timer_del(ev_timer(ev));
   }
-  pptr->timeout = CurrentTime + UPINGTIMEOUT;
-  pptr->active = 1;
 }
 
-/*
- * ping_server - get the server host address if not valid
- * then call start_ping
+/** Timer callback to stop upings.
+ * @param[in] ev Event for uping expiration.
  */
-static void ping_server(struct UPing* pptr)
+static void uping_killer_callback(struct Event* ev)
 {
-  if (INADDR_NONE == pptr->sin.sin_addr.s_addr) {
-    char *s;
-
-    if ((s = strchr(pptr->name, '@')))
-      ++s;                     
-    else
-      s = pptr->name;
-
-    if (INADDR_NONE == (pptr->sin.sin_addr.s_addr = inet_addr(s))) {
-      struct DNSQuery query;
-      struct DNSReply* rpl;
-
-      query.vptr = (void*) pptr;
-      query.callback = uping_dns_callback;
-      if (0 == (rpl = gethost_byname(s, &query)))
-       return;
-      memcpy(&pptr->sin.sin_addr, rpl->hp->h_addr, sizeof(struct in_addr));
-    }
+  struct UPing *pptr;
+
+  assert(0 != ev_timer(ev));
+  assert(0 != t_data(ev_timer(ev)));
+
+  pptr = (struct UPing*) t_data(ev_timer(ev));
+
+  Debug((DEBUG_SEND, "uping_killer_callback called, %p (%d)", pptr,
+        ev_type(ev)));
+
+  if (ev_type(ev) == ET_DESTROY) { /* being destroyed */
+    pptr->freeable &= ~UPING_PENDING_KILLER;
+
+    if (!pptr->freeable)
+      MyFree(pptr); /* done with it, finally */
+  } else {
+    assert(ev_type(ev) == ET_EXPIRE);
+
+    uping_end(pptr); /* <FUDD>kill the uping, kill the uping!</FUDD> */
   }
-  start_ping(pptr);
 }
 
+/** Start a uping.
+ * This sets up the timers, UPing flags, and sends a notice to the
+ * requesting client.
+ */
+static void uping_start(struct UPing* pptr)
+{
+  assert(0 != pptr);
+
+  timer_add(timer_init(&pptr->sender), uping_sender_callback, (void*) pptr,
+           TT_PERIODIC, 1);
+  timer_add(timer_init(&pptr->killer), uping_killer_callback, (void*) pptr,
+           TT_RELATIVE, UPINGTIMEOUT);
+  pptr->freeable |= UPING_PENDING_SENDER | UPING_PENDING_KILLER;
 
-/*
- * send_ping
- *
+  sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :Sending %d ping%s to %s",
+               pptr->client, pptr->count, (pptr->count == 1) ? "" : "s",
+               pptr->name);
+  pptr->active = 1;
+}
+
+/** Send a uping to another server.
+ * @param[in] pptr Descriptor for uping.
  */
-void send_ping(struct UPing* pptr)
+void uping_send(struct UPing* pptr)
 {
   struct timeval tv;
   char buf[BUFSIZE + 1];
 
   assert(0 != pptr);
+  if (pptr->sent == pptr->count)
+    return;
   memset(buf, 0, sizeof(buf));
 
   gettimeofday(&tv, NULL);
-  sprintf(buf, " %10lu%c%6lu", tv.tv_sec, '\0', tv.tv_usec);
+  sprintf(buf, " %10lu%c%6lu", (unsigned long)tv.tv_sec, '\0', (unsigned long)tv.tv_usec);
 
   Debug((DEBUG_SEND, "send_ping: sending [%s %s] to %s.%d on %d",
          buf, &buf[12],
-         ircd_ntoa((const char*) &pptr->sin.sin_addr), ntohs(pptr->sin.sin_port),
+          ircd_ntoa(&pptr->addr.addr), pptr->addr.port,
          pptr->fd));
 
-  if (sendto(pptr->fd, buf, BUFSIZE, 0, (struct sockaddr*) &pptr->sin,
-             sizeof(struct sockaddr_in)) != BUFSIZE)
+  if (os_sendto_nonb(pptr->fd, buf, BUFSIZE, NULL, 0, &pptr->addr) != IO_SUCCESS)
   {
-    int err = errno;
+    const char* msg = strerror(errno);
+    if (!msg)
+      msg = "Unknown error";
     if (pptr->client)
-    {
-      if (MyUser(pptr->client)
-#ifndef NO_PROTOCOL9
-         || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10)
-#endif
-         )
-       sendto_one(pptr->client, ":%s NOTICE %s :UPING: sendto() failed: %s",
-                  me.name, pptr->client->name,
-                   (strerror(err)) ? strerror(err) : "Unknown error");
-      else
-       sendto_one(pptr->client, "%s NOTICE %s%s :UPING: sendto() failed: %s",
-                  NumServ(&me), NumNick(pptr->client),
-                   (strerror(err)) ? strerror(err) : "Unknown error");
-    }
-    Debug((DEBUG_DEBUG, "UPING: send_ping: sendto failed on %d: %s", pptr->fd,
-           (strerror(err)) ? strerror(err) : "Unknown error"));
-    end_ping(pptr);
+      sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: send failed: "
+                   "%s", pptr->client, msg);
+    Debug((DEBUG_DEBUG, "UPING: send_ping: sendto failed on %d: %s", pptr->fd, msg));
+    uping_end(pptr);
     return;
   }
   ++pptr->sent;
 }
 
-/*
- * read_ping
+/** Read the response from an outbound uping.
+ * @param[in] pptr UPing to check.
  */
-void read_ping(struct UPing* pptr)
+void uping_read(struct UPing* pptr)
 {
-  struct sockaddr_in sin;
+  struct irc_sockaddr sin;
   struct timeval     tv;
   unsigned int       len;
   unsigned int       pingtime;
@@ -314,304 +326,159 @@ void read_ping(struct UPing* pptr)
   if (IO_BLOCKED == ior)
     return;
   else if (IO_FAILURE == ior) {
-    int err = errno;
-    if (MyUser(pptr->client)
-#ifndef NO_PROTOCOL9
-        || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10)
-#endif
-        )
-      sendto_one(pptr->client, ":%s NOTICE %s :UPING: recvfrom: %s",
-                 me.name, pptr->client->name, 
-                 (strerror(err)) ? strerror(err) : "Unknown error");
-    else
-      sendto_one(pptr->client, "%s NOTICE %s%s :UPING: recvfrom: %s",
-                 NumServ(&me), NumNick(pptr->client),
-                 (strerror(err)) ? strerror(err) : "Unknown error");
-    Debug((DEBUG_SEND, "UPING: read_ping: recvfrom: %d", err));
-    end_ping(pptr);
+    const char* msg = strerror(errno);
+    if (!msg)
+      msg = "Unknown error";
+    sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: receive error: "
+                 "%s", pptr->client, msg);
+    uping_end(pptr);
     return;
-  }    
+  }
 
   if (len < 19)
     return;                    /* Broken packet */
 
   ++pptr->received;
+
+  buf[len] = 0;
   pingtime = (tv.tv_sec - atol(&buf[1])) * 1000
              + (tv.tv_usec - atol(buf + strlen(buf) + 1)) / 1000;
 
   pptr->ms_ave += pingtime;
-  if (!(pptr->ms_min) || (pptr->ms_min > pingtime))
+  if (!pptr->ms_min || pptr->ms_min > pingtime)
     pptr->ms_min = pingtime;
   if (pingtime > pptr->ms_max)
     pptr->ms_max = pingtime;
-  
-  pptr->timeout = CurrentTime + UPINGTIMEOUT;
 
-  Debug((DEBUG_SEND, "read_ping: %d bytes, ti %lu: [%s %s] %lu ms",
-      len, pptr->timeout, buf, (buf + strlen(buf) + 1), pingtime));
+  timer_chg(&pptr->killer, TT_RELATIVE, UPINGTIMEOUT);
 
   s = pptr->buf + strlen(pptr->buf);
   sprintf(s, " %u", pingtime);
 
   if (pptr->received == pptr->count)
-    end_ping(pptr);
+    uping_end(pptr);
   return;
 }
 
-
-/*
- * 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
+/** Start sending upings to a server.
+ * @param[in] sptr Client requesting the upings.
+ * @param[in] aconf ConfItem containing the address to ping.
+ * @param[in] port Port number to ping.
+ * @param[in] count Number of times to ping (should be at least 20).
+ * @return Zero.
  */
-int m_uping(struct Client* cptr, struct Client *sptr, int parc, char *parv[])
+int uping_server(struct Client* sptr, struct ConfItem* aconf, int port, int count)
 {
-  struct ConfItem *aconf;
-  int port;
   int fd;
-  struct UPing* pptr = 0;
+  int family = 0;
+  struct UPing* pptr;
+  struct irc_sockaddr *local;
 
-  if (!IsPrivileged(sptr))
-  {
-    sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
-    return -1;
-  }
+  assert(0 != sptr);
+  assert(0 != aconf);
 
-  if (parc < 2)
-  {
-    sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UPING");
+  if (!irc_in_addr_valid(&aconf->address.addr)) {
+    sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Host lookup failed for "
+                 "%s", sptr, aconf->name);
     return 0;
   }
 
-  if (MyUser(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(1, cptr, sptr,
-      ":%s UPING %s %s %s %s", 3, parc, parv) != HUNTED_ISME)
-    return 0;
-
-  if (BadPtr(parv[4]) || atoi(parv[4]) <= 0)
-  {
-    if (MyUser(sptr) || Protocol(cptr) < 10)
-      sendto_one(sptr, ":%s NOTICE %s :UPING: Illegal number of packets: %s",
-         me.name, parv[0], parv[4]);
-    else
-      sendto_one(sptr, "%s NOTICE %s%s :UPING: Illegal number of packets: %s",
-         NumServ(&me), NumNick(sptr), parv[4]);
-    return 0;
-  }
+  if (IsUPing(sptr))
+    uping_cancel(sptr, sptr);  /* Cancel previous ping request */
 
-  /* Check if a CONNECT would be possible at all (adapted from m_connect) */
-  for (aconf = GlobalConfList; aconf; aconf = aconf->next)
-  {
-    if (aconf->status == CONF_SERVER &&
-       match(parv[1], aconf->name) == 0)
-      break;
+  if (irc_in_addr_is_ipv4(&aconf->address.addr)) {
+    local = &VirtualHost_v4;
+    family = AF_INET;
+  } else {
+    local = &VirtualHost_v6;
   }
-  if (!aconf)
-  {
-    for (aconf = GlobalConfList; aconf; aconf = aconf->next)
-    {
-      if (aconf->status == CONF_SERVER &&
-         (match(parv[1], aconf->host) == 0 ||
-          match(parv[1], strchr(aconf->host, '@') + 1) == 0))
-       break;
-    }
-  }
-  if (!aconf)
-  {
-    if (MyUser(sptr) || Protocol(cptr) < 10)
-      sendto_one(sptr, ":%s NOTICE %s :UPING: Host %s not listed in ircd.conf",
-         me.name, parv[0], parv[1]);
-    else
-      sendto_one(sptr,
-         "%s NOTICE %s%s :UPING: Host %s not listed in ircd.conf",
-         NumServ(&me), NumNick(sptr), parv[1]);
+  fd = os_socket(local, SOCK_DGRAM, "Outbound uping socket", family);
+  if (fd < 0)
     return 0;
-  }
 
-  if (IsUPing(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);
-
-  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
-    int err = errno;
-    sendto_ops("m_uping: socket: %s", (err != EMFILE) 
-                ? ((strerror(err)) ? strerror(err) : "Unknown error") : "No more sockets");
-    if (MyUser(sptr) || Protocol(cptr) < 10)
-      sendto_one(sptr, 
-                 ":%s NOTICE %s :UPING: Unable to create udp ping socket",
-                 me.name, parv[0]);
-    else
-      sendto_one(sptr,
-                 "%s NOTICE %s%s :UPING: Unable to create udp ping socket",
-                 NumServ(&me), NumNick(sptr));
-    ircd_log(L_ERROR, "UPING: Unable to create UDP socket");
-    return 0;
-  }
+  pptr = (struct UPing*) MyMalloc(sizeof(struct UPing));
+  assert(0 != pptr);
+  memset(pptr, 0, sizeof(struct UPing));
 
-  if (!os_set_nonblocking(fd)) {
-    if (MyUser(sptr) || Protocol(cptr) < 10)
-      sendto_one(sptr, ":%s NOTICE %s :UPING: Can't set fd non-blocking",
-            me.name, parv[0]);
-    else
-      sendto_one(sptr, "%s NOTICE %s%s :UPING: Can't set fd non-blocking",
-            NumServ(&me), NumNick(sptr));
+  if (!socket_add(&pptr->socket, uping_read_callback, (void*) pptr,
+                 SS_DATAGRAM, SOCK_EVENT_READABLE, fd)) {
+    sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Can't queue fd for "
+                 "reading", sptr);
     close(fd);
+    MyFree(pptr);
     return 0;
   }
-  pptr = (struct UPing*) MyMalloc(sizeof(struct UPing));
-  assert(0 != pptr);
-  memset(pptr, 0, sizeof(struct UPing));
 
-  pptr->fd = fd;
-  pptr->sin.sin_port = htons(port);
-  pptr->sin.sin_addr.s_addr = aconf->ipnum.s_addr;
-  pptr->sin.sin_family = AF_INET;
-  pptr->count = IRCD_MIN(20, atoi(parv[4]));
-  strcpy(pptr->name, aconf->host);
-  pptr->client = sptr;
-  pptr->index = -1;
+  pptr->fd                  = fd;
+  memcpy(&pptr->addr.addr, &aconf->address.addr, sizeof(pptr->addr.addr));
+  pptr->addr.port           = port;
+  pptr->count               = IRCD_MIN(20, count);
+  pptr->client              = sptr;
+  pptr->freeable            = UPING_PENDING_SOCKET;
+  strcpy(pptr->name, aconf->name);
 
   pptr->next = pingList;
-  pingList = pptr;
+  pingList   = pptr;
 
   SetUPing(sptr);
-  ping_server(pptr);
+  uping_start(pptr);
   return 0;
 }
 
-void end_ping(struct UPing* pptr)
+/** Clean up a UPing structure, reporting results to the requester.
+ * @param[in,out] pptr UPing results.
+ */
+void uping_end(struct UPing* pptr)
 {
-  Debug((DEBUG_DEBUG, "end_ping: %p", pptr));
-  delete_resolver_queries((void*) pptr);
-  if (pptr->client)
-  {
-    if (MyUser(pptr->client)
-        || (IsServer(pptr->client->from) && Protocol(pptr->client->from) < 10))
-    {
-      if (pptr->lastsent)      /* Started at all ? */
-      {
-       if (0 < pptr->received) /* Received any pings at all? */
-       {
-         sendto_one(pptr->client, ":%s NOTICE %s :UPING %s%s",
-             me.name, pptr->client->name, pptr->name, pptr->buf);
-          /* XXX - warning long unsigned int format, unsigned int arg (7, 8, 9) */
-         sendto_one(pptr->client,
-             ":%s NOTICE %s :UPING Stats: sent %d recvd %d ; "
-             "min/avg/max = %1lu/%1lu/%1lu ms",
-             me.name, pptr->client->name, pptr->sent,
-             pptr->received, pptr->ms_min,
-             (2 * pptr->ms_ave) / (2 * pptr->received), 
-              pptr->ms_max);
-       }
-       else
-         sendto_one(pptr->client,
-             ":%s NOTICE %s :UPING: no response from %s within %d seconds",
-             me.name, pptr->client->name, pptr->name,
-             UPINGTIMEOUT);
-      }
-      else
-       sendto_one(pptr->client,
-           ":%s NOTICE %s :UPING: Could not start ping to %s %d",
-           me.name, pptr->client->name, pptr->name, ntohs(pptr->sin.sin_port));
-    }
-    else
-    {
-      if (pptr->lastsent)      /* Started at all ? */
-      {
-       if (0 < pptr->received) /* Received any pings at all? */
-       {
-         sendto_one(pptr->client, "%s NOTICE %s%s :UPING %s%s",
-             NumServ(&me), NumNick(pptr->client), pptr->name, pptr->buf);
-          /* XXX - warning: long unsigned int format, unsigned int arg(9, 10, 11) */
-         sendto_one(pptr->client,
-             "%s NOTICE %s%s :UPING Stats: sent %d recvd %d ; "
-             "min/avg/max = %1lu/%1lu/%1lu ms",
-             NumServ(&me), NumNick(pptr->client), pptr->sent,
-             pptr->received, pptr->ms_min,
-             (2 * pptr->ms_ave) / (2 * pptr->received), 
-              pptr->ms_max);
-       }
-       else
-         sendto_one(pptr->client,
-             "%s NOTICE %s%s :UPING: no response from %s within %d seconds",
-             NumServ(&me), NumNick(pptr->client), pptr->name,
-             UPINGTIMEOUT);
-      }
-      else
-       sendto_one(pptr->client,
-           "%s NOTICE %s%s :UPING: Could not start ping to %s %d",
-           NumServ(&me), NumNick(pptr->client), pptr->name, 
-            ntohs(pptr->sin.sin_port));
-    }
+  Debug((DEBUG_DEBUG, "uping_end: %p", pptr));
+
+  if (pptr->client) {
+    if (pptr->lastsent) {
+      if (0 < pptr->received) {
+       sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING %s%s",
+                     pptr->client, pptr->name, pptr->buf);
+       sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING Stats: "
+                     "sent %d recvd %d ; min/avg/max = %u/%u/%u ms",
+                     pptr->client, pptr->sent, pptr->received, pptr->ms_min,
+                     (2 * pptr->ms_ave) / (2 * pptr->received), pptr->ms_max);
+      } else
+       sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: no response "
+                     "from %s within %d seconds", pptr->client, pptr->name,
+                     UPINGTIMEOUT);
+    } else
+      sendcmdto_one(&me, CMD_NOTICE, pptr->client, "%C :UPING: Could not "
+                   "start ping to %s", pptr->client, pptr->name);
   }
+
   close(pptr->fd);
   pptr->fd = -1;
   uping_erase(pptr);
   if (pptr->client)
     ClearUPing(pptr->client);
-  MyFree(pptr);
+  if (pptr->freeable & UPING_PENDING_SOCKET)
+    socket_del(&pptr->socket);
+  if (pptr->freeable & UPING_PENDING_SENDER)
+    timer_del(&pptr->sender);
+  if (pptr->freeable & UPING_PENDING_KILLER)
+    timer_del(&pptr->killer);
 }
 
-void cancel_ping(struct Client *sptr, struct Client* acptr)
+/** Change notifications for any upings by \a sptr.
+ * @param[in] sptr Client to stop notifying.
+ * @param[in] acptr New client to notify (or NULL).
+ */
+void uping_cancel(struct Client *sptr, struct Client* acptr)
 {
   struct UPing* ping;
   struct UPing* ping_next;
 
-  Debug((DEBUG_DEBUG, "UPING: cancelling uping for %s", sptr->name));
+  Debug((DEBUG_DEBUG, "UPING: canceling uping for %s", cli_name(sptr)));
   for (ping = pingList; ping; ping = ping_next) {
     ping_next = ping->next;
     if (sptr == ping->client) {
       ping->client = acptr;
-      end_ping(ping);
+      uping_end(ping);
     }
   }
   ClearUPing(sptr);
 }
-