chiark / gitweb /
Greetings and challenges.
[tripe] / peer.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Communication with the peer
6  *
7  * (c) 2001 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Trivial IP Encryption (TrIPE).
13  *
14  * TrIPE is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  * 
19  * TrIPE is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with TrIPE; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Header files ------------------------------------------------------*/
30
31 #include "tripe.h"
32
33 /*----- Static variables --------------------------------------------------*/
34
35 static peer *peers = 0;
36 static sel_file sock;
37
38 /*----- Tunnel table ------------------------------------------------------*/
39
40 const tunnel_ops *tunnels[] = {
41 #ifdef TUN_LINUX
42   &tun_linux,
43 #endif
44 #ifdef TUN_BSD
45   &tun_bsd,
46 #endif
47 #ifdef TUN_UNET
48   &tun_unet,
49 #endif
50   &tun_slip,
51   0
52 }, *tun_default;
53
54 /*----- Main code ---------------------------------------------------------*/
55
56 /* --- @p_pingtype@ --- *
57  *
58  * Arguments:   @unsigned msg@ = message type
59  *
60  * Returns:     String to describe the message.
61  */
62
63 static const char *p_pingtype(unsigned msg)
64 {
65   switch (msg & MSG_TYPEMASK) {
66     case MISC_PING:
67     case MISC_PONG:
68       return "transport-ping";
69     case MISC_EPING:
70     case MISC_EPONG:
71       return "encrypted-ping";
72     default:
73       abort();
74   }
75 }
76
77 /* --- @p_ponged@ --- *
78  *
79  * Arguments:   @peer *p@ = peer packet arrived from
80  *              @unsigned msg@ = message type
81  *              @buf *b@ = buffer containing payload
82  *
83  * Returns:     ---
84  *
85  * Use:         Processes a ping response.
86  */
87
88 static void p_ponged(peer *p, unsigned msg, buf *b)
89 {
90   uint32 id;
91   const octet *magic;
92   ping *pg;
93   
94   IF_TRACING(T_PEER, {
95     trace(T_PEER, "peer: received %s reply from %s",
96           p_pingtype(msg), p->spec.name);
97     trace_block(T_PACKET, "peer: ping contents", BBASE(b), BSZ(b));
98   })
99
100   if (buf_getu32(b, &id) ||
101       (magic = buf_get(b, sizeof(pg->magic))) == 0 ||
102       BLEFT(b)) {
103     a_warn("PEER %s malformed-%s", p->spec.name, p_pingtype(msg));
104     return;
105   }
106
107   for (pg = p->pings; pg; pg = pg->next) {
108     if (pg->id == id)
109       goto found;
110   }
111   a_warn("PEER %s unexpected-%s 0x%08lx",
112          p->spec.name, p_pingtype(msg), (unsigned long)id);
113   return;
114
115 found:
116   if (memcmp(magic, pg->magic, sizeof(pg->magic)) != 0) {
117     a_warn("PEER %s corrupt-%s", p->spec.name, p_pingtype(msg));
118     return;
119   }
120   p_pingdone(pg, PING_OK);
121 }
122
123 /* --- @p_read@ --- *
124  *
125  * Arguments:   @int fd@ = file descriptor to read from
126  *              @unsigned mode@ = what happened
127  *              @void *v@ = an uninteresting pointer
128  *
129  * Returns:     ---
130  *
131  * Use:         Reads a packet from somewhere.
132  */
133
134 static void p_read(int fd, unsigned mode, void *v)
135 {
136   peer *p = 0;
137   addr a;
138   size_t sz;
139   ssize_t n;
140   int ch;
141   buf b, bb;
142
143   /* --- Read the data --- */
144
145   TIMER;
146   sz = sizeof(addr);
147   n = recvfrom(fd, buf_i, sizeof(buf_i), 0, &a.sa, &sz);
148   if (n < 0) {
149     a_warn("PEER - socket-read-error -- %s", strerror(errno));
150     return;
151   }
152
153   /* --- If the packet is a greeting, don't check peers --- */
154
155   if (n && buf_i[0] == (MSG_MISC | MISC_GREET)) {
156     IF_TRACING(T_PEER, {
157       trace(T_PEER, "peer: greeting received from INET %s %u",
158             inet_ntoa(a.sin.sin_addr),
159             (unsigned)ntohs(a.sin.sin_port));
160       trace_block(T_PACKET, "peer: greeting contents", buf_i, n);
161     })
162     buf_init(&b, buf_i, n);
163     buf_getbyte(&b);
164     if (c_check(&b) || BLEFT(&b)) {
165       a_warn("PEER - invalid-greeting");
166       return;
167     }
168     a_notify("GREET %s INET %s %u",
169              b64_encode(buf_i + 1, n - 1),
170              inet_ntoa(a.sin.sin_addr),
171              (unsigned)ntohs(a.sin.sin_port));
172     return;
173   }
174
175   /* --- Find the appropriate peer --- */
176
177   assert(a.sa.sa_family == AF_INET);
178   for (p = peers; p; p = p->next) {
179     if (p->spec.sa.sin.sin_addr.s_addr == a.sin.sin_addr.s_addr &&
180         p->spec.sa.sin.sin_port == a.sin.sin_port)
181       goto found;
182   }
183   a_warn("PEER - unexpected-source INET %s %u",
184          inet_ntoa(a.sin.sin_addr), (unsigned)ntohs(a.sin.sin_port));
185   return;
186
187 found:
188   IF_TRACING(T_PEER, {
189     trace(T_PEER, "peer: packet received from `%s'", p->spec.name);
190     trace_block(T_PACKET, "peer: packet contents", buf_i, n);
191   })
192
193   /* --- Pick the packet apart --- */
194
195   p->st.t_last = time(0);
196   p->st.n_in++;
197   p->st.sz_in += n;
198   buf_init(&b, buf_i, n);
199   if ((ch = buf_getbyte(&b)) < 0) {
200     a_warn("PEER %s bad-packet no-type", p->spec.name);
201     return;
202   }
203   switch (ch & MSG_CATMASK) {
204     case MSG_PACKET:
205       if (ch & MSG_TYPEMASK) {
206         a_warn("PEER %s bad-packet unknown-type 0x%02x", p->spec.name, ch);
207         p->st.n_reject++;
208         return;
209       }
210       buf_init(&bb, buf_o, sizeof(buf_o));
211       if (ksl_decrypt(&p->ks, MSG_PACKET, &b, &bb)) {
212         p->st.n_reject++;
213         a_warn("PEER %s decrypt-failed", p->spec.name);
214         return;
215       }
216       if (BOK(&bb)) {
217         p->st.n_ipin++;
218         p->st.sz_ipin += BSZ(&b);
219         p->t->ops->inject(p->t, &bb);
220       } else {
221         p->st.n_reject++;
222         a_warn("PEER %s packet-build-failed", p->spec.name);
223       }
224       break;
225     case MSG_KEYEXCH:
226       kx_message(&p->kx, ch & MSG_TYPEMASK, &b);
227       break;
228     case MSG_MISC:
229       switch (ch & MSG_TYPEMASK) {
230         case MISC_NOP:
231           T( trace(T_PEER, "peer: received NOP packet"); )
232           break;
233         case MISC_PING:
234           buf_put(p_txstart(p, MSG_MISC | MISC_PONG), BCUR(&b), BLEFT(&b));
235           p_txend(p);
236           break;        
237         case MISC_PONG:
238           p_ponged(p, MISC_PONG, &b);
239           break;
240         case MISC_EPING:
241           buf_init(&bb, buf_t, sizeof(buf_t));
242           if (ksl_decrypt(&p->ks, ch, &b, &bb)) {
243             p->st.n_reject++;
244             a_warn("PEER %s decrypt-failed", p->spec.name);
245             return;
246           }
247           if (BOK(&bb)) {
248             buf_flip(&bb);
249             if (ksl_encrypt(&p->ks, MSG_MISC | MISC_EPONG, &bb,
250                             p_txstart(p, MSG_MISC | MISC_EPONG)))
251               kx_start(&p->kx, 0);
252             p_txend(p);
253           }
254           break;
255         case MISC_EPONG:
256           buf_init(&bb, buf_t, sizeof(buf_t));
257           if (ksl_decrypt(&p->ks, ch, &b, &bb)) {
258             p->st.n_reject++;
259             a_warn("PEER %s decrypt-failed", p->spec.name);
260             return;
261           }
262           if (BOK(&bb)) {
263             buf_flip(&bb);
264             p_ponged(p, MISC_EPONG, &bb);
265           }
266           break;
267       }
268       break;
269     default:
270       p->st.n_reject++;
271       a_warn("PEER %s bad-packet unknown-category 0x%02x", p->spec.name, ch);
272       break;
273   }
274 }
275
276 /* --- @p_txstart@ --- *
277  *
278  * Arguments:   @peer *p@ = pointer to peer block
279  *              @unsigned msg@ = message type code
280  *
281  * Returns:     A pointer to a buffer to write to.
282  *
283  * Use:         Starts sending to a peer.  Only one send can happen at a
284  *              time.
285  */
286
287 buf *p_txstart(peer *p, unsigned msg)
288 {
289   buf_init(&p->b, buf_o, sizeof(buf_o));
290   buf_putbyte(&p->b, msg);
291   return (&p->b);
292 }
293
294 /* --- @p_txend@ --- *
295  *
296  * Arguments:   @peer *p@ = pointer to peer block
297  *
298  * Returns:     ---
299  *
300  * Use:         Sends a packet to the peer.
301  */
302
303 static void p_setkatimer(peer *);
304
305 static int p_dotxend(peer *p)
306 {
307   if (!BOK(&p->b)) {
308     a_warn("PEER %s packet-build-failed", p->spec.name);
309     return (0);
310   }
311   IF_TRACING(T_PEER, trace_block(T_PACKET, "peer: sending packet",
312                                  BBASE(&p->b), BLEN(&p->b)); )
313   if (sendto(sock.fd, BBASE(&p->b), BLEN(&p->b),
314              0, &p->spec.sa.sa, p->spec.sasz) < 0) {
315     a_warn("PEER %s socket-write-error -- %s",
316            p->spec.name, strerror(errno));
317     return (0);
318   } else {
319     p->st.n_out++;
320     p->st.sz_out += BLEN(&p->b);
321     return (1);
322   }
323 }
324
325 void p_txend(peer *p)
326 {
327   if (p_dotxend(p) && p->spec.t_ka) {
328     sel_rmtimer(&p->tka);
329     p_setkatimer(p);
330   }
331 }
332
333 /* --- @p_pingwrite@ --- *
334  *
335  * Arguments:   @ping *p@ = ping structure
336  *              @buf *b@ = buffer to write in
337  *
338  * Returns:     ---
339  *
340  * Use:         Fills in a ping structure and writes the packet payload.
341  */
342
343 static void p_pingwrite(ping *p, buf *b)
344 {
345   static uint32 seq = 0;
346
347   p->id = U32(seq++);
348   GR_FILL(&rand_global, p->magic, sizeof(p->magic));
349   buf_putu32(b, p->id);
350   buf_put(b, p->magic, sizeof(p->magic));
351 }
352
353 /* --- @p_pingdone@ --- *
354  *
355  * Arguments:   @ping *p@ = ping structure
356  *              @int rc@ = return code to pass on
357  *
358  * Returns:     ---
359  *
360  * Use:         Disposes of a ping structure, maybe sending a notification.
361  */
362
363 void p_pingdone(ping *p, int rc)
364 {
365   if (p->prev) p->prev->next = p->next;
366   else p->p->pings = p->next;
367   if (p->next) p->next->prev = p->prev;
368   if (rc != PING_TIMEOUT) sel_rmtimer(&p->t);
369   T( trace(T_PEER, "peer: ping 0x%08lx done (rc = %d)",
370            (unsigned long)p->id, rc); )
371   if (rc >= 0) p->func(rc, p->arg);
372 }
373
374 /* --- @p_pingtimeout@ --- *
375  *
376  * Arguments:   @struct timeval *now@ = the time now
377  *              @void *pv@ = pointer to ping block
378  *
379  * Returns:     ---
380  *
381  * Use:         Called when a ping times out.
382  */
383
384 static void p_pingtimeout(struct timeval *now, void *pv)
385 {
386   ping *p = pv;
387
388   T( trace(T_PEER, "peer: ping 0x%08lx timed out", (unsigned long)p->id); )
389   p_pingdone(p, PING_TIMEOUT);
390 }
391
392 /* --- @p_pingsend@ --- *
393  *
394  * Arguments:   @peer *p@ = destination peer
395  *              @ping *pg@ = structure to fill in
396  *              @unsigned type@ = message type
397  *              @unsigned long timeout@ = how long to wait before giving up
398  *              @void (*func)(int, void *)@ = callback function
399  *              @void *arg@ = argument for callback
400  *
401  * Returns:     Zero if successful, nonzero if it failed.
402  *
403  * Use:         Sends a ping to a peer.  Call @func@ with a nonzero argument
404  *              if we get an answer within the timeout, or zero if no answer.
405  */
406
407 int p_pingsend(peer *p, ping *pg, unsigned type,
408                unsigned long timeout,
409                void (*func)(int, void *), void *arg)
410 {
411   buf *b, bb;
412   struct timeval tv;
413
414   switch (type) {
415     case MISC_PING:
416       pg->msg = MISC_PONG;
417       b = p_txstart(p, MSG_MISC | MISC_PING);
418       p_pingwrite(pg, b);
419       p_txend(p);
420       break;
421     case MISC_EPING:
422       pg->msg = MISC_EPONG;
423       b = p_txstart(p, MSG_MISC | MISC_EPING);
424       buf_init(&bb, buf_t, sizeof(buf_t));
425       p_pingwrite(pg, &bb);
426       buf_flip(&bb);
427       if (ksl_encrypt(&p->ks, MSG_MISC | MISC_EPING, &bb, b))
428         kx_start(&p->kx, 0);
429       if (!BOK(b))
430         return (-1);
431       p_txend(p);
432       break;
433     default:
434       abort();
435       break;
436   }
437
438   pg->next = p->pings;
439   pg->prev = 0;
440   pg->p = p;
441   pg->func = func;
442   pg->arg = arg;
443   if (p->pings) p->pings->prev = pg;
444   p->pings = pg;
445   gettimeofday(&tv, 0);
446   tv.tv_sec += timeout;
447   sel_addtimer(&sel, &pg->t, &tv, p_pingtimeout, pg);
448   T( trace(T_PEER, "peer: send %s 0x%08lx to %s",
449            p_pingtype(type), (unsigned long)pg->id, p->spec.name); )
450   return (0);
451 }
452
453 /* --- @p_greet@ --- *
454  *
455  * Arguments:   @peer *p@ = peer to send to
456  *              @const void *c@ = pointer to challenge
457  *              @size_t sz@ = size of challenge
458  *
459  * Returns:     ---
460  *
461  * Use:         Sends a greeting packet.
462  */
463
464 void p_greet(peer *p, const void *c, size_t sz)
465 {
466   buf *b = p_txstart(p, MSG_MISC | MISC_GREET);
467   buf_put(b, c, sz);
468   p_txend(p);
469 }
470
471 /* --- @p_tun@ --- *
472  *
473  * Arguments:   @peer *p@ = pointer to peer block
474  *              @buf *b@ = buffer containing incoming packet
475  *
476  * Returns:     ---
477  *
478  * Use:         Handles a packet which needs to be sent to a peer.
479  */
480
481 void p_tun(peer *p, buf *b)
482 {
483   buf *bb = p_txstart(p, MSG_PACKET);
484
485   TIMER;
486   if (ksl_encrypt(&p->ks, MSG_PACKET, b, bb))
487     kx_start(&p->kx, 0);
488   if (BOK(bb) && BLEN(bb)) {
489     p->st.n_ipout++;
490     p->st.sz_ipout += BLEN(bb);
491     p_txend(p);
492   }
493 }
494
495 /* --- @p_keyreload@ --- *
496  *
497  * Arguments:   ---
498  *
499  * Returns:     ---
500  *
501  * Use:         Forces a check of the daemon's keyring files.
502  */
503
504 void p_keyreload(void)
505 {
506   peer *p;
507
508   if (km_reload()) {
509     for (p = peers; p; p = p->next)
510       kx_newkeys(&p->kx);
511   }
512 }
513
514 /* --- @p_interval@ --- *
515  *
516  * Arguments:   ---
517  *
518  * Returns:     ---
519  *
520  * Use:         Called periodically to do tidying.
521  */
522
523 void p_interval(void)
524 {
525   peer *p;
526
527   p_keyreload();
528   for (p = peers; p; p = p->next)
529     ksl_prune(&p->ks);
530 }
531
532 /* --- @p_stats@ --- *
533  *
534  * Arguments:   @peer *p@ = pointer to a peer block
535  *
536  * Returns:     A pointer to the peer's statistics.
537  */
538
539 stats *p_stats(peer *p) { return (&p->st); }
540
541 /* --- @p_ifname@ --- *
542  *
543  * Arguments:   @peer *p@ = pointer to a peer block
544  *
545  * Returns:     A pointer to the peer's interface name.
546  */
547
548 const char *p_ifname(peer *p) { return (p->t->ops->ifname(p->t)); }
549
550 /* --- @p_addr@ --- *
551  *
552  * Arguments:   @peer *p@ = pointer to a peer block
553  *
554  * Returns:     A pointer to the peer's address.
555  */
556
557 const addr *p_addr(peer *p) { return (&p->spec.sa); }
558
559 /* --- @p_init@ --- *
560  *
561  * Arguments:   @struct in_addr addr@ = address to bind to
562  *              @unsigned port@ = port number to listen to
563  *
564  * Returns:     ---
565  *
566  * Use:         Initializes the peer system; creates the socket.
567  */
568
569 void p_init(struct in_addr addr, unsigned port)
570 {
571   int fd;
572   struct sockaddr_in sin;
573   int len = PKBUFSZ;
574
575   /* --- Note on socket buffer sizes --- *
576    *
577    * For some bizarre reason, Linux 2.2 (at least) doubles the socket buffer
578    * sizes I pass to @setsockopt@.  I'm not putting special-case code here
579    * for Linux: BSD (at least TCPv2) does what I tell it rather than second-
580    * guessing me.
581    */
582
583   if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
584     die(EXIT_FAILURE, "socket creation failed: %s", strerror(errno));
585   BURN(sin);
586   sin.sin_family = AF_INET;
587   sin.sin_addr = addr;
588   sin.sin_port = htons(port);
589   if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)))
590     die(EXIT_FAILURE, "bind failed: %s", strerror(errno));
591   if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) ||
592       setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len))) {
593     die(EXIT_FAILURE, "failed to set socket buffer sizes: %s",
594         strerror(errno));
595   }
596   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
597   sel_initfile(&sel, &sock, fd, SEL_READ, p_read, 0);
598   sel_addfile(&sock);
599   T( trace(T_PEER, "peer: created socket"); )
600 }
601
602 /* --- @p_port@ --- *
603  *
604  * Arguments:   ---
605  *
606  * Returns:     Port number used for socket.
607  */
608
609 unsigned p_port(void)
610 {
611   addr a;
612   size_t sz = sizeof(addr);
613
614   if (getsockname(sock.fd, &a.sa, &sz))
615     die(EXIT_FAILURE, "couldn't read port number: %s", strerror(errno));
616   assert(a.sa.sa_family == AF_INET);
617   return (ntohs(a.sin.sin_port));
618 }
619
620 /* --- @p_keepalive@ --- *
621  *
622  * Arguments:   @struct timeval *now@ = the current time
623  *              @void *pv@ = peer to wake up
624  *
625  * Returns:     ---
626  *
627  * Use:         Sends a keepalive ping message to its peer.
628  */
629
630 static void p_keepalive(struct timeval *now, void *pv)
631 {
632   peer *p = pv;
633   p_txstart(p, MSG_MISC | MISC_NOP); p_dotxend(p);
634   T( trace(T_PEER, "peer: sent keepalive to %s", p->spec.name); )
635   p_setkatimer(p);
636 }
637
638 /* --- @p_setkatimer@ --- *
639  *
640  * Arguments:   @peer *p@ = peer to set
641  *
642  * Returns:     ---
643  *
644  * Use:         Resets the keepalive timer thing.
645  */
646
647 static void p_setkatimer(peer *p)
648 {
649   struct timeval tv;
650
651   if (!p->spec.t_ka)
652     return;
653   gettimeofday(&tv, 0);
654   tv.tv_sec += p->spec.t_ka;
655   sel_addtimer(&sel, &p->tka, &tv, p_keepalive, p);
656 }
657
658 /* --- @p_create@ --- *
659  *
660  * Arguments:   @peerspec *spec@ = information about this peer
661  *
662  * Returns:     Pointer to the peer block, or null if it failed.
663  *
664  * Use:         Creates a new named peer block.  No peer is actually attached
665  *              by this point.
666  */
667
668 peer *p_create(peerspec *spec)
669 {
670   peer *p = CREATE(peer);
671
672   T( trace(T_PEER, "peer: creating new peer `%s'", spec->name); )
673   p->spec = *spec;
674   p->spec.name = xstrdup(spec->name);
675   p->ks = 0;
676   p->prev = 0;
677   p->pings = 0;
678   memset(&p->st, 0, sizeof(stats));
679   p->st.t_start = time(0);
680   if ((p->t = spec->tops->create(p)) == 0)
681     goto tidy_0;
682   p_setkatimer(p);
683   if (kx_init(&p->kx, p, &p->ks))
684     goto tidy_1;
685   p->next = peers;
686   if (peers)
687     peers->prev = p;
688   peers = p;
689   switch (p->spec.sa.sa.sa_family) {
690     case AF_INET:
691       a_notify("ADD %s %s INET %s %u",
692                spec->name,
693                p->t->ops->ifname(p->t),
694                inet_ntoa(p->spec.sa.sin.sin_addr),
695                (unsigned)ntohs(p->spec.sa.sin.sin_port));
696       break;
697     default:
698       a_notify("ADD %s %s UNKNOWN", spec->name, p->t->ops->ifname(p->t));
699       break;
700   }
701   a_notify("KXSTART %s", spec->name);   /* Couldn't tell anyone before */
702   return (p);
703
704 tidy_1:
705   if (spec->t_ka)
706     sel_rmtimer(&p->tka);
707   p->t->ops->destroy(p->t);
708 tidy_0:
709   xfree(p->spec.name);
710   DESTROY(p);
711   return (0);
712 }
713
714 /* --- @p_name@ --- *
715  *
716  * Arguments:   @peer *p@ = pointer to a peer block
717  *
718  * Returns:     A pointer to the peer's name.
719  */
720
721 const char *p_name(peer *p) { return (p->spec.name); }
722
723 /* --- @p_spec@ --- *
724  *
725  * Arguments:   @peer *p@ = pointer to a peer block
726  *
727  * Returns:     Pointer to the peer's specification
728  */
729
730 const peerspec *p_spec(peer *p) { return (&p->spec); }
731
732 /* --- @p_find@ --- *
733  *
734  * Arguments:   @const char *name@ = name to look up
735  *
736  * Returns:     Pointer to the peer block, or null if not found.
737  *
738  * Use:         Finds a peer by name.
739  */
740
741 peer *p_find(const char *name)
742 {
743   peer *p;
744   for (p = peers; p; p = p->next) {
745     if (strcmp(name, p->spec.name) == 0)
746       return (p);
747   }
748   return (0);  
749 }
750
751 /* --- @p_destroy@ --- *
752  *
753  * Arguments:   @peer *p@ = pointer to a peer
754  *
755  * Returns:     ---
756  *
757  * Use:         Destroys a peer.
758  */
759
760 void p_destroy(peer *p)
761 {
762   ping *pg, *ppg;
763
764   T( trace(T_PEER, "peer: destroying peer `%s'", p->spec.name); )
765   a_notify("KILL %s", p->spec.name);
766   ksl_free(&p->ks);
767   kx_free(&p->kx);
768   p->t->ops->destroy(p->t);
769   if (p->spec.t_ka)
770     sel_rmtimer(&p->tka);
771   xfree(p->spec.name);
772   for (pg = p->pings; pg; pg = ppg) {
773     ppg = pg->next;
774     p_pingdone(pg, PING_PEERDIED);
775   }
776   if (p->next)
777     p->next->prev = p->prev;
778   if (p->prev)
779     p->prev->next = p->next;
780   else
781     peers = p->next;
782   DESTROY(p);
783 }
784
785 /* --- @p_first@, @p_next@ --- *
786  *
787  * Arguments:   @peer *p@ = a peer block
788  *
789  * Returns:     @peer_first@ returns the first peer in some ordering;
790  *              @peer_next@ returns the peer following a given one in the
791  *              same ordering.  Null is returned for the end of the list.
792  */
793
794 peer *p_first(void) { return (peers); }
795 peer *p_next(peer *p) { return (p->next); }
796
797 /*----- That's all, folks -------------------------------------------------*/