X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/blobdiff_plain/f4c9c08fb4a558d880d183b65a12de3770c3860b..7737eb87d283d46b7a90caba20ac75c3214451d3:/server/peer.c diff --git a/server/peer.c b/server/peer.c index 57891e7b..27ac4a78 100644 --- a/server/peer.c +++ b/server/peer.c @@ -9,30 +9,32 @@ * * This file is part of Trivial IP Encryption (TrIPE). * - * TrIPE 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 2 of the License, or - * (at your option) any later version. + * TrIPE 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 3 of the License, or (at your + * option) any later version. * - * TrIPE 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. + * TrIPE 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 TrIPE; if not, write to the Free Software Foundation, - * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with TrIPE. If not, see . */ /*----- Header files ------------------------------------------------------*/ #include "tripe.h" +/*----- Global state ------------------------------------------------------*/ + +udpsocket udpsock[NADDRFAM]; + /*----- Static variables --------------------------------------------------*/ static sym_table byname; static addrmap byaddr; -static sel_file sock; static unsigned nmobile; /*----- Tunnel table ------------------------------------------------------*/ @@ -167,6 +169,54 @@ static int p_encrypt(peer *p, int ty, buf *bin, buf *bout) return (err); } +/* --- @p_updateaddr@ --- * + * + * Arguments: @peer *p@ = pointer to peer block + * @const addr *a@ = address to associate with this peer + * + * Returns: Zero if the address was changed; @+1@ if it was already + * right. + * + * Use: Updates our idea of @p@'s address. + */ + +int p_updateaddr(peer *p, const addr *a) +{ + peer *q; + peer_byaddr *pa, *qa; + int ix; + unsigned f; + + /* --- Figure out how to proceed --- * + * + * If this address already belongs to a different peer, then swap the + * addresses over. This doesn't leave the displaced peer in an especially + * good state, but it ought to get sorted out soon enough. + */ + + pa = am_find(&byaddr, a, sizeof(peer_byaddr), &f); + if (f && pa->p == p) + return (+1); + else if (!f) { + T( trace(T_PEER, "peer: updating address for `%s'", p_name(p)); ) + am_remove(&byaddr, p->byaddr); + p->byaddr = pa; p->spec.sa = *a; pa->p = p; + p->afix = afix(p->spec.sa.sa.sa_family); assert(p->afix >= 0); + a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); + return (0); + } else { + q = pa->p; qa = p->byaddr; + T( trace(T_PEER, "peer: swapping addresses for `%s' and `%s'", + p_name(p), p_name(q)); ) + q->byaddr = qa; qa->p = q; q->spec.sa = p->spec.sa; + p->byaddr = pa; pa->p = p; p->spec.sa = *a; + ix = p->afix; p->afix = q->afix; q->afix = ix; + a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); + a_notify("NEWADDR", "?PEER", q, "?ADDR", &q->spec.sa, A_END); + return (0); + } +} + /* --- @p_decrypt@ --- * * * Arguments: @peer **pp@ = pointer to peer to decrypt message from @@ -189,9 +239,7 @@ static int p_decrypt(peer **pp, addr *a, size_t n, int ty, buf *bin, buf *bout) { peer *p, *q; - peer_byaddr *pa, *qa; int err = KSERR_DECRYPT; - unsigned f; /* --- If we have a match on the source address then try that first --- */ @@ -249,33 +297,11 @@ searched: return (-1); } - /* --- We found one that accepted, so update the peer's address --- * - * - * If we had an initial guess of which peer this packet came from -- i.e., - * @q@ is not null -- then swap the addresses over. This doesn't leave the - * evicted peer in an especially good state, but it ought to get sorted out - * soon enough. - */ + /* --- We found one that accepted, so update the peer's address --- */ if (!err) { *pp = p; - if (!q) { - T( trace(T_PEER, "peer: updating address for `%s'", p_name(p)); ) - pa = am_find(&byaddr, a, sizeof(peer_byaddr), &f); assert(!f); - am_remove(&byaddr, p->byaddr); - p->byaddr = pa; - pa->p = p; - p->spec.sa = *a; - a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); - } else { - T( trace(T_PEER, "peer: swapping addresses for `%s' and `%s'", - p_name(p), p_name(q)); ) - pa = p->byaddr; qa = q->byaddr; - pa->p = q; q->byaddr = pa; q->spec.sa = p->spec.sa; - qa->p = p; p->byaddr = qa; p->spec.sa = *a; - a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); - a_notify("NEWADDR", "?PEER", q, "?ADDR", &q->spec.sa, A_END); - } + p_updateaddr(p, a); } match: @@ -310,6 +336,10 @@ static void p_read(int fd, unsigned mode, void *v) ssize_t n; int ch; buf b, bb; +#ifndef NTRACE + int ix = -1; + char name[NI_MAXHOST], svc[NI_MAXSERV]; +#endif /* --- Read the data --- */ @@ -320,19 +350,23 @@ static void p_read(int fd, unsigned mode, void *v) a_warn("PEER", "-", "socket-read-error", "?ERRNO", A_END); return; } + IF_TRACING(T_PEER, { + ix = afix(a.sa.sa_family); + getnameinfo(&a.sa, sz, name, sizeof(name), svc, sizeof(svc), + NI_NUMERICHOST | NI_NUMERICSERV); + }) /* --- If the packet is a greeting, don't check peers --- */ if (n && buf_i[0] == (MSG_MISC | MISC_GREET)) { IF_TRACING(T_PEER, { - trace(T_PEER, "peer: greeting received from INET %s %u", - inet_ntoa(a.sin.sin_addr), - (unsigned)ntohs(a.sin.sin_port)); + trace(T_PEER, "peer: greeting received from %s %s %s", + aftab[ix].name, name, svc); trace_block(T_PACKET, "peer: greeting contents", buf_i, n); }) buf_init(&b, buf_i, n); buf_getbyte(&b); - if (c_check(&b) || BLEFT(&b)) { + if (c_check(0, 0, &b) || BLEFT(&b)) { a_warn("PEER", "-", "invalid-greeting", A_END); return; } @@ -353,11 +387,11 @@ static void p_read(int fd, unsigned mode, void *v) IF_TRACING(T_PEER, { if (p) { trace(T_PEER, - "peer: packet received from `%s' from address INET %s %d", - p_name(p), inet_ntoa(a.sin.sin_addr), ntohs(a.sin.sin_port)); + "peer: packet received from `%s' from address %s %s %s", + p_name(p), aftab[ix].name, name, svc); } else { - trace(T_PEER, "peer: packet received from unknown address INET %s %d", - inet_ntoa(a.sin.sin_addr), ntohs(a.sin.sin_port)); + trace(T_PEER, "peer: packet received from unknown address %s %s %s", + aftab[ix].name, name, svc); } trace_block(T_PACKET, "peer: packet contents", buf_i, n); }) @@ -393,9 +427,8 @@ static void p_read(int fd, unsigned mode, void *v) } break; case MSG_KEYEXCH: - if (!p) goto unexp; - p_rxupdstats(p, n); - kx_message(&p->kx, ch & MSG_TYPEMASK, &b); + if (p) p_rxupdstats(p, n); + if (kx_message(p ? &p->kx : 0, &a, ch & MSG_TYPEMASK, &b)) goto unexp; break; case MSG_MISC: switch (ch & MSG_TYPEMASK) { @@ -435,6 +468,16 @@ static void p_read(int fd, unsigned mode, void *v) p_ponged(p, MISC_EPONG, &bb); } break; + case MISC_BYE: + buf_init(&bb, buf_t, sizeof(buf_t)); + if (p_decrypt(&p, &a, n, ch, &b, &bb)) return; + if (!(p->spec.f&PSF_EPHEM)) return; + if (BOK(&bb)) { + buf_flip(&bb); + if (BSZ(&bb)) return; + p_destroy(p, 0); + } + break; } break; default: @@ -469,6 +512,35 @@ buf *p_txstart(peer *p, unsigned msg) return (&p->b); } +/* --- @p_txaddr@ --- * + * + * Arguments: @const addr *a@ = recipient address + * @const void *p@ = pointer to packet to send + * @size_t sz@ = length of packet + * + * Returns: Zero if successful, nonzero on error. + * + * Use: Sends a packet to an address which (possibly) isn't a current + * peer. + */ + +int p_txaddr(const addr *a, const void *p, size_t sz) +{ + socklen_t sasz = addrsz(a); + int i; + + if ((i = afix(a->sa.sa_family)) < 0) { + a_warn("PEER", "?ADDR", a, "disabled-address-family", A_END); + return (-1); + } + IF_TRACING(T_PEER, trace_block(T_PACKET, "peer: sending packet", p, sz); ) + if (sendto(udpsock[i].sf.fd, p, sz, 0, &a->sa, sasz) < 0) { + a_warn("PEER", "?ADDR", a, "socket-write-error", "?ERRNO", A_END); + return (-1); + } + return (0); +} + /* --- @p_txend@ --- * * * Arguments: @peer *p@ = pointer to peer block @@ -482,14 +554,16 @@ static void p_setkatimer(peer *); static int p_dotxend(peer *p) { + socklen_t sasz = addrsz(&p->spec.sa); + if (!BOK(&p->b)) { a_warn("PEER", "?PEER", p, "packet-build-failed", A_END); return (0); } IF_TRACING(T_PEER, trace_block(T_PACKET, "peer: sending packet", BBASE(&p->b), BLEN(&p->b)); ) - if (sendto(sock.fd, BBASE(&p->b), BLEN(&p->b), - 0, &p->spec.sa.sa, p->spec.sasz) < 0) { + if (sendto(udpsock[p->afix].sf.fd, BBASE(&p->b), BLEN(&p->b), + 0, &p->spec.sa.sa, sasz) < 0) { a_warn("PEER", "?PEER", p, "socket-write-error", "?ERRNO", A_END); return (0); } else { @@ -742,68 +816,88 @@ void p_setifname(peer *p, const char *name) const addr *p_addr(peer *p) { return (&p->spec.sa); } -/* --- @p_init@ --- * +/* --- @p_bind@ --- * * - * Arguments: @struct in_addr addr@ = address to bind to - * @unsigned port@ = port number to listen to + * Arguments: @struct addrinfo *ailist@ = addresses to bind to * * Returns: --- * * Use: Initializes the peer system; creates the socket. */ -void p_init(struct in_addr addr, unsigned port) +void p_bind(struct addrinfo *ailist) { int fd; - struct sockaddr_in sin; int len = PKBUFSZ; + int yes = 1; + int i; + struct addrinfo *ai; + unsigned port, lastport = 0; + addr a; + socklen_t sz; - /* --- Note on socket buffer sizes --- * - * - * For some bizarre reason, Linux 2.2 (at least) doubles the socket buffer - * sizes I pass to @setsockopt@. I'm not putting special-case code here - * for Linux: BSD (at least TCPv2) does what I tell it rather than second- - * guessing me. - */ - - if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) - die(EXIT_FAILURE, "socket creation failed: %s", strerror(errno)); - BURN(sin); - sin.sin_family = AF_INET; - sin.sin_addr = addr; - sin.sin_port = htons(port); - if (bind(fd, (struct sockaddr *)&sin, sizeof(sin))) - die(EXIT_FAILURE, "bind failed: %s", strerror(errno)); - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) || - setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len))) { - die(EXIT_FAILURE, "failed to set socket buffer sizes: %s", - strerror(errno)); + for (i = 0; i < NADDRFAM; i++) udpsock[i].sf.fd = -1; + + for (ai = ailist; ai; ai = ai->ai_next) { + if ((i = afix(ai->ai_family)) < 0) continue; + if (udpsock[i].sf.fd != -1) continue; + + /* --- Note on socket buffer sizes --- * + * + * For some bizarre reason, Linux 2.2 (at least) doubles the socket + * buffer sizes I pass to @setsockopt@. I'm not putting special-case + * code here for Linux: BSD (at least TCPv2) does what I tell it rather + * than second-guessing me. + */ + + if ((fd = socket(ai->ai_family, SOCK_DGRAM, 0)) < 0) + die(EXIT_FAILURE, "socket creation failed: %s", strerror(errno)); + if (i == AFIX_INET6 && + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes))) { + die(EXIT_FAILURE, "failed to set IPv6-only state: %s", + strerror(errno)); + } + assert(ai->ai_addrlen <= sizeof(a)); + memcpy(&a, ai->ai_addr, ai->ai_addrlen); + if ((port = getport(&a)) == 0 && lastport) setport(&a, lastport); + if (bind(fd, &a.sa, addrsz(&a))) + die(EXIT_FAILURE, "bind failed: %s", strerror(errno)); + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) || + setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len))) { + die(EXIT_FAILURE, "failed to set socket buffer sizes: %s", + strerror(errno)); + } + fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); + sel_initfile(&sel, &udpsock[i].sf, fd, SEL_READ, p_read, 0); + sel_addfile(&udpsock[i].sf); + T( trace(T_PEER, "peer: created %s socket", aftab[i].name); ) + if (port) + udpsock[i].port = port; + else { + sz = sizeof(a); + if (getsockname(fd, &a.sa, &sz)) { + die(EXIT_FAILURE, "failed to read local socket address: %s", + strerror(errno)); + } + udpsock[i].port = lastport = getport(&a); + } } - fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); - sel_initfile(&sel, &sock, fd, SEL_READ, p_read, 0); - sel_addfile(&sock); - T( trace(T_PEER, "peer: created socket"); ) - sym_create(&byname); - am_create(&byaddr); } -/* --- @p_port@ --- * +/* --- @p_init@ --- * * * Arguments: --- * - * Returns: Port number used for socket. + * Returns: --- + * + * Use: Initializes the peer system. */ -unsigned p_port(void) +void p_init(void) { - addr a; - socklen_t sz = sizeof(addr); - - if (getsockname(sock.fd, &a.sa, &sz)) - die(EXIT_FAILURE, "couldn't read port number: %s", strerror(errno)); - assert(a.sa.sa_family == AF_INET); - return (ntohs(a.sin.sin_port)); + sym_create(&byname); + am_create(&byaddr); } /* --- @p_keepalive@ --- * @@ -873,9 +967,11 @@ peer *p_create(peerspec *spec) p->spec.name = (/*unconst*/ char *)SYM_NAME(p->byname); if (spec->tag) p->spec.tag = xstrdup(spec->tag); if (spec->privtag) p->spec.privtag = xstrdup(spec->privtag); + if (spec->knock) p->spec.knock = xstrdup(spec->knock); p->ks = 0; p->pings = 0; p->ifname = 0; + p->afix = afix(p->spec.sa.sa.sa_family); assert(p->afix >= 0); memset(&p->st, 0, sizeof(stats)); p->st.t_start = time(0); if (!(tops->flags & TUNF_PRIVOPEN)) @@ -887,7 +983,7 @@ peer *p_create(peerspec *spec) T( trace(T_TUNNEL, "peer: attached interface %s to peer `%s'", p->ifname, p_name(p)); ) p_setkatimer(p); - if (kx_init(&p->kx, p, &p->ks, p->spec.f & PSF_KXMASK)) + if (kx_setup(&p->kx, p, &p->ks, p->spec.f & PSF_KXMASK)) goto tidy_4; a_notify("ADD", "?PEER", p, @@ -998,17 +1094,28 @@ peer *p_find(const char *name) /* --- @p_destroy@ --- * * * Arguments: @peer *p@ = pointer to a peer + * @int bye@ = say goodbye to the peer? * * Returns: --- * * Use: Destroys a peer. */ -void p_destroy(peer *p) +void p_destroy(peer *p, int bye) { ping *pg, *ppg; + buf *b, bb; T( trace(T_PEER, "peer: destroying peer `%s'", p->spec.name); ) + + if (bye && (p->spec.f&PSF_EPHEM)) { + b = p_txstart(p, MSG_MISC | MISC_BYE); + buf_init(&bb, buf_t, sizeof(buf_t)); + assert(BOK(&bb)); buf_flip(&bb); + p_encrypt(p, MSG_MISC | MISC_BYE, &bb, b); + p_txend(p); + } + a_notify("KILL", "%s", p->spec.name, A_END); ksl_free(&p->ks); kx_free(&p->kx); @@ -1016,6 +1123,7 @@ void p_destroy(peer *p) if (p->ifname) xfree(p->ifname); if (p->spec.tag) xfree(p->spec.tag); if (p->spec.privtag) xfree(p->spec.privtag); + if (p->spec.knock) xfree(p->spec.knock); p->t->ops->destroy(p->t); if (p->spec.t_ka) sel_rmtimer(&p->tka); for (pg = p->pings; pg; pg = ppg) {