+void p_txend(peer *p)
+{
+ if (p_dotxend(p) && p->spec.t_ka) {
+ sel_rmtimer(&p->tka);
+ p_setkatimer(p);
+ }
+}
+
+/* --- @p_pingwrite@ --- *
+ *
+ * Arguments: @ping *p@ = ping structure
+ * @buf *b@ = buffer to write in
+ *
+ * Returns: ---
+ *
+ * Use: Fills in a ping structure and writes the packet payload.
+ */
+
+static void p_pingwrite(ping *p, buf *b)
+{
+ static uint32 seq = 0;
+
+ p->id = U32(seq++);
+ GR_FILL(&rand_global, p->magic, sizeof(p->magic));
+ buf_putu32(b, p->id);
+ buf_put(b, p->magic, sizeof(p->magic));
+}
+
+/* --- @p_pingdone@ --- *
+ *
+ * Arguments: @ping *p@ = ping structure
+ * @int rc@ = return code to pass on
+ *
+ * Returns: ---
+ *
+ * Use: Disposes of a ping structure, maybe sending a notification.
+ */
+
+void p_pingdone(ping *p, int rc)
+{
+ if (p->prev) p->prev->next = p->next;
+ else p->p->pings = p->next;
+ if (p->next) p->next->prev = p->prev;
+ if (rc != PING_TIMEOUT) sel_rmtimer(&p->t);
+ T( trace(T_PEER, "peer: ping 0x%08lx done (rc = %d)",
+ (unsigned long)p->id, rc); )
+ if (rc >= 0) p->func(rc, p->arg);
+}
+
+/* --- @p_pingtimeout@ --- *
+ *
+ * Arguments: @struct timeval *now@ = the time now
+ * @void *pv@ = pointer to ping block
+ *
+ * Returns: ---
+ *
+ * Use: Called when a ping times out.
+ */
+
+static void p_pingtimeout(struct timeval *now, void *pv)
+{
+ ping *p = pv;
+
+ T( trace(T_PEER, "peer: ping 0x%08lx timed out", (unsigned long)p->id); )
+ p_pingdone(p, PING_TIMEOUT);
+}
+
+/* --- @p_pingsend@ --- *
+ *
+ * Arguments: @peer *p@ = destination peer
+ * @ping *pg@ = structure to fill in
+ * @unsigned type@ = message type
+ * @unsigned long timeout@ = how long to wait before giving up
+ * @void (*func)(int, void *)@ = callback function
+ * @void *arg@ = argument for callback
+ *
+ * Returns: Zero if successful, nonzero if it failed.
+ *
+ * Use: Sends a ping to a peer. Call @func@ with a nonzero argument
+ * if we get an answer within the timeout, or zero if no answer.
+ */
+
+int p_pingsend(peer *p, ping *pg, unsigned type,
+ unsigned long timeout,
+ void (*func)(int, void *), void *arg)
+{
+ buf *b, bb;
+ struct timeval tv;
+
+ switch (type) {
+ case MISC_PING:
+ pg->msg = MISC_PONG;
+ b = p_txstart(p, MSG_MISC | MISC_PING);
+ p_pingwrite(pg, b);
+ p_txend(p);
+ break;
+ case MISC_EPING:
+ pg->msg = MISC_EPONG;
+ b = p_txstart(p, MSG_MISC | MISC_EPING);
+ buf_init(&bb, buf_t, sizeof(buf_t));
+ p_pingwrite(pg, &bb);
+ buf_flip(&bb);
+ if (ksl_encrypt(&p->ks, MSG_MISC | MISC_EPING, &bb, b))
+ kx_start(&p->kx, 0);
+ if (!BOK(b))
+ return (-1);
+ p_txend(p);
+ break;
+ default:
+ abort();
+ break;
+ }
+
+ pg->next = p->pings;
+ pg->prev = 0;
+ pg->p = p;
+ pg->func = func;
+ pg->arg = arg;
+ if (p->pings) p->pings->prev = pg;
+ p->pings = pg;
+ gettimeofday(&tv, 0);
+ tv.tv_sec += timeout;
+ sel_addtimer(&sel, &pg->t, &tv, p_pingtimeout, pg);
+ T( trace(T_PEER, "peer: send %s 0x%08lx to %s",
+ p_pingtype(type), (unsigned long)pg->id, p->spec.name); )
+ return (0);
+}
+
+/* --- @p_greet@ --- *
+ *
+ * Arguments: @peer *p@ = peer to send to
+ * @const void *c@ = pointer to challenge
+ * @size_t sz@ = size of challenge
+ *
+ * Returns: ---
+ *
+ * Use: Sends a greeting packet.
+ */
+
+void p_greet(peer *p, const void *c, size_t sz)
+{
+ buf *b = p_txstart(p, MSG_MISC | MISC_GREET);
+ buf_put(b, c, sz);
+ p_txend(p);
+}
+