/*
 * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
 *               2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017
 *      Inferno Nettverk A/S, Norway.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. The above copyright notice, this list of conditions and the following
 *    disclaimer must appear in all copies of the software, derivative works
 *    or modified versions, and any portions thereof, aswell as in all
 *    supporting documentation.
 * 2. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by
 *      Inferno Nettverk A/S, Norway.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Inferno Nettverk A/S requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  sdc@inet.no
 *  Inferno Nettverk A/S
 *  Oslo Research Park
 *  Gaustadalléen 21
 *  NO-0349 Oslo
 *  Norway
 *
 * any improvements or extensions that they make and grant Inferno Nettverk A/S
 * the rights to redistribute these changes.
 *
 */

#include "common.h"
#include "config_parse.h"

static const char rcsid[] =
"$Id: dante_udp.c,v 1.93.4.3.2.3 2017/01/31 08:17:38 karls Exp $";


udpheader_t *
getudptarget(const char *buf, const size_t buflen, udpheader_t *header,
             size_t *headerlen, char *emsg, const size_t emsglen);
/*
 * Gets the target address for the SOCKS UDP packet stored in "buf".
 * "buflen" gives the length of the received UDP packet.
 *
 * On success "header" is returned, filled in appropriately.
 * "headerlen" gives the length of the SOCKS UDP header.  Payload will
 * start at the first byte following it.
 *
 * On failure NULL is returned.  "emsg" of "emsglen" will then contain an
 * error string describing the reason for failure.
 */


static int
fromaddr_as_expected(struct sockaddr_storage *expected,
                     const struct sockaddr_storage *from,
                     char *emsg, size_t emsglen);

/*
 * Checks that the packet received on socket "s", from "from", matches the
 * expected address as given in "expected".
 * If so, the socket is connected to "from" and "expected" is updated to
 * contain the address of "from".
 *
 * Returns if the from address is as expected, or false otherwise.  If false,
 * "emsg" may be filled with more information as to what went wrong or did
 * not match.
 */

extern int rawsocket;

iostatus_t
io_udp_client2target(control, client, twotargets, cauth, state,
                     clog, dlog, badfd, packetrule, bwused)
   sockd_io_direction_t *control;
   sockd_io_direction_t *client;
   sockd_io_direction_t *twotargets;
   const authmethod_t *cauth;
   connectionstate_t *state;
   int *badfd;
   iologaddr_t *clog;
   iologaddr_t *dlog;
   rule_t *packetrule;
   size_t *bwused;
{
   /*
    * Dante has only one client per i/o session, but each UDP-based i/o
    * session can have up to two targets (ipv4 and ipv6).  When transferring
    * data from the client we thus want to bill it to the correct target
    * target address.
    */
   const char *function = "io_udp_client2target()";
   iostatus_t iostatus = IO_NOERROR;
   udptarget_t *target = NULL;
   recvfrom_info_t recvflags;
   sendto_info_t sendtoflags;
   udpheader_t header;
   struct sockaddr_storage from, targetaddr;
   socklen_t len;
   ssize_t w, r;
   size_t headerlen, payloadlen, emsglen;
   char hosta[MAXSOCKSHOSTSTRING], hostb[MAXSOCKSHOSTSTRING], emsg[2048],
        buf[SOCKD_BUFSIZE + sizeof(udpheader_t)], *payload;
   int sametarget, gaierr,
       doconnect           = 0,
       permit              = 0,
       icmp_type           = ICMP_TYPE_UNREACHABLE,
       icmp_code           = ICMP_CODE_UNREACHABLE_PORT;

   recvflags.side = INTERNALIF;
   recvflags.peer = client->raddr;
   recvflags.type = SOCK_DGRAM;

   len = sizeof(from);
   if ((r = socks_recvfrom(client->s,
                           buf,
                           sizeof(buf),
                           0,
                           &from,
                           &len,
                           &recvflags,
                           &client->auth)) == -1) {
      if (ERRNOISPREVIOUSPACKET(errno)) {
         /*
          * error is from a previous packet sent by us out on this socket,
          * i.e., /from target/ to client.
          * Note that Linux apparently can return this error even if the
          * socket is not connected.
          *
          * Don't treat this as fatal; more packets could come
          * and they may be accepted by the client.  As long
          * as we still have the control connection, assume
          * the client is alive.  When we no longer have the
          * control connection, we delete this io.
          */
         struct sockaddr_storage *packetsrc;
         struct sockaddr_storage *receivedonaddr;
         const int oldcommand = state->command;
         state->command       = SOCKS_UDPREPLY;

         if (twotargets->dstc <= 1) {
            *badfd = twotargets->dstv[0].s;
            SASSERTX(*badfd != -1);

            receivedonaddr = &twotargets->dstv[0].laddr;
            packetsrc      = &twotargets->dstv[0].raddr;
         }
         else {
            /*
             * don't know which target socket was used.
             */
            *badfd         = -1;
            receivedonaddr = NULL;
            packetsrc      = NULL;
         }

         emsglen = snprintf(emsg, sizeof(emsg),
                            "%s (delayed error)", strerror(errno));

         iolog(packetrule,
               state,
               OPERATION_TMPERROR,
               dlog,
               clog,
               NULL,
               NULL,
               emsg,
               emsglen);

         state->command = oldcommand;

         send_icmperror(rawsocket,
                        receivedonaddr,
                        packetsrc,
                        &client->raddr,
                        -1,
                        -1,
                        icmp_type,
                        icmp_code);


         return IO_TMPERROR;
      }

      *badfd = client->s;

      if (ERRNOISTMP(errno))
         return IO_EAGAIN;
      else {
         log_unexpected_udprecv_error(function, client->s, errno, INTERNALIF);
         return IO_IOERROR;
      }
   }

   iostatus = io_packet_received(&recvflags, r, &from, &client->laddr);

   if (iostatus != IO_NOERROR) {
      *badfd = client->s;
      return iostatus;
   }

   sockaddr2sockshost(&from, &clog->peer);
   clog->peer_isset = 1;

   slog(LOG_DEBUG, "%s: received udp packet from %s, length: %ld",
        function, sockaddr2string(&from, NULL, 0), (long)r);

   /*
    * Read udp packet.  Now figure out if it should be forwarded, where it
    * should be forwarded, and from what address we should forward it.
    */

   *emsg = NUL;

   if (!client->state.isconnected) {
      /*
       * Have not yet connected to the client, so need to check whether
       * the packet is really from our expected client.  After the connect,
       * the kernel takes care of this for us.
       *
       * XXX or, what happens if somebody else also sends packets that ends
       * up in client the socket buffer before we connect to the real client?
       * Do we get those packet too from recvfrom(), even after we connect to
       * a different address?  Should check this out.
       */
      const int blocked
      = !fromaddr_as_expected(&client->raddr, &from, emsg, sizeof(emsg));

      if (!blocked) {
         sockaddr2sockshost(&client->raddr, &client->host);

         /* More efficient to be connected.  */
         if (socks_connecthost(client->s,
                               INTERNALIF,
                               &client->host,
                               NULL,
                               NULL,
                               (long)-1,
                               emsg,
                               sizeof(emsg)) != 0 ) {
            swarnx("%s: strange ... could not connect(2) back to new UDP "
                   "client that sent us its first packet from %s: %s",
                   function, sockaddr2string(&client->raddr, NULL, 0), emsg);

            *badfd = client->s;
            iostatus = IO_ERROR;
         }
         else
            client->state.isconnected = 1;
      }

      SASSERTX(!((iostatus != IO_NOERROR) && blocked));

      if (iostatus != IO_NOERROR || blocked) {
         iolog(packetrule,
               state,
               IOOP(blocked, iostatus),
               clog,
               dlog,
               NULL,
               NULL,
               emsg,
               strlen(emsg));

         return blocked ? IO_TMPERROR : iostatus;
      }

      SASSERTX(client->state.isconnected);
   }
   else
      /*
       * already connected to client.  Kernel should make sure address is
       * correct.
       */

   SASSERTX(iostatus == IO_NOERROR);

   if (getudptarget(buf, (size_t)r, &header, &headerlen, emsg, sizeof(emsg))
   == NULL) {
      slog(LOG_DEBUG,
           "%s: getudptarget() failed for packet of length %lu from client %s, "
           "received on local address %s for target %s: %s",
           function,
           (unsigned long)r,
           sockaddr2string(&client->raddr, hosta, sizeof(hosta)),
           sockaddr2string(&client->laddr, hostb, sizeof(hostb)),
           header.host.atype == SOCKS_ADDR_NOTSET ?
               "N/A" : sockshost2string(&header.host, NULL, 0),
           emsg);

      icmp_code        = ICMP_CODE_UNREACHABLE_DESTHOSTUNKNOWN;
      iostatus         = IO_TMPERROR;
   }

   if (header.host.atype != SOCKS_ADDR_NOTSET) {
      /*
       * Whether getudptarget() failed or not, we did get a hostname at
       * least, so update for logging.
       */
      dlog->peer        = header.host;
      dlog->peer_isset  = 1;
   }
   else
      dlog->peer_isset  = 0;

   if (iostatus == IO_NOERROR)  {
      SASSERTX(header.host.atype != SOCKS_ADDR_NOTSET);

      SASSERTX(headerlen > 0);
      payload    = buf + headerlen;
      payloadlen = r   - headerlen;

      sockshost2sockaddr2(&header.host,
                          &targetaddr,
                          &gaierr,
                          emsg,
                          sizeof(emsg));

      if (IPADDRISBOUND(&targetaddr)) {
         /*
          * we want to log ipaddress used, not (a possible) hostname sent
          * by the client.
          */
         sockaddr2sockshost(&targetaddr, &dlog->peer);
         SASSERTX(dlog->peer_isset);
      }
      else {
         if (gaierr != 0) {  /* dns-error. */
            SASSERTX(header.host.atype == SOCKS_ADDR_DOMAIN);
            log_resolvefailed(header.host.addr.domain, EXTERNALIF, gaierr);

            snprintf(emsg, sizeof(emsg),
                     "could not resolve target address %s: %s ",
                     sockshost2string(&header.host, NULL, 0),
                     gai_strerror(gaierr));
         }
         else { /* some other, non-dns error. */
            snprintf(emsg, sizeof(emsg),
                     "could not resolve target address %s.  Non-DNS error: %s ",
                     sockshost2string(&header.host, NULL, 0),
                     strerror(errno));
         }

         iostatus  = IO_TMPERROR;
         icmp_code = ICMP_CODE_UNREACHABLE_DESTHOSTUNKNOWN;
      }
   }
   else {
      bzero(&targetaddr, sizeof(targetaddr));
      SET_SOCKADDR(&targetaddr, AF_INET);
   }

   if (iostatus != IO_NOERROR) {
      iolog(packetrule,
            state,
            IOOP(packetrule->verdict == VERDICT_BLOCK, iostatus),
            clog,
            dlog,
            NULL,
            NULL,
            emsg,
            strlen(emsg));

      /*
       * This may be considered a violation of rfc 1928, which says UDP
       * packets should be forwarded or dropped "silently", but what does
       * silently mean?  Just not closing the control connection (obviously),
       * or not providing any ICMP errors either?
       * Allowing the latter seems useful, so keep it until we have reason to
       * think it breaks things for someone.
       */
      send_icmperror(rawsocket,
                     &client->laddr,
                     &client->raddr,
                     IPADDRISBOUND(&targetaddr) ? &targetaddr : NULL,
                     -1,
                     -1,
                     icmp_type,
                     iostatus == IO_NOERROR ?
                        ICMP_CODE_UNREACHABLE_HOSTPROHIBITED : icmp_code);

      return IO_TMPERROR;
   }

   /*
    * Ok, received a packet and things look ok so far.  Now check the rules
    * for whether the packet is to be allowed through or not.
    */

   slog(LOG_DEBUG,
        "%s: packet of length %lu (payload length %lu) from client %s "
        "received on local address, %s for target %s",
        function,
        (unsigned long)r,
        (unsigned long)payloadlen,
        sockaddr2string(&client->raddr, hosta, sizeof(hosta)),
        sockaddr2string(&client->laddr, hostb, sizeof(hostb)),
        sockshost2string(&header.host, NULL, 0));

   /*
    * Do we have a saved rule from before?  If so, see if we can reuse it
    * and avoid having to do a new rule-lookup.
    */
   if (client->state.use_saved_srule) {
      int addtarget;

      SASSERTX(twotargets->dstc > 0);

      /*
       * Can reuse, but only if target is the same as last time.
       * First figure out what target object is the correct one to use,
       * so we know what the target was the last time.
       */

      switch (twotargets->dstc) {
         case 1:
            /*
             * Have one target socket created already.  Is it of the
             * same addressfamily as the packet we now received?
             */
            if (twotargets->dstv[0].raddr.ss_family == targetaddr.ss_family) {
              target    = &twotargets->dstv[0]; /* yes. */
              addtarget = 0;
            }
            else /* nope, must create a new socket for this target. */
              addtarget = 1;

            break;

         case 2:
            /*
             * Have two target sockets created already.  One of them
             * must be of the correct addressfamily.
             */
            if (twotargets->dstv[0].raddr.ss_family == targetaddr.ss_family)
              target = &twotargets->dstv[0]; /* this is the one. */
            else {
               SASSERTX(twotargets->dstv[1].raddr.ss_family
               == targetaddr.ss_family);

              target = &twotargets->dstv[1]; /* this is the one. */
            }

            addtarget = 0;
            break;

         default:
            SERRX(twotargets->dstc);
      }

      if (addtarget) {
         SASSERTX(twotargets->dstc == 1);
         SASSERTX(twotargets->dstv[0].raddr.ss_family != targetaddr.ss_family);

         slog(LOG_DEBUG,
              "%s: new to add a new targetsocket due to change of af.  "
              "Have %s, need %s",
              function,
              safamily2string(twotargets->dstv[0].raddr.ss_family),
              safamily2string(targetaddr.ss_family));

         SASSERTX(twotargets->dstv[twotargets->dstc].s == 0);

         if (initclient(control->s,
                        &client->laddr,
                        &client->raddr,
                        &header.host,
                        &targetaddr,
                        packetrule,
                        emsg,
                        sizeof(emsg),
                        &twotargets->dstv[twotargets->dstc]) != NULL) {
            target = addclient(&client->laddr,
                               &twotargets->dstv[twotargets->dstc],
                               &twotargets->dstc,
                               &twotargets->dstcmax,
                               &twotargets->dstv,
                               state,
                               packetrule);

            SASSERTX(target != NULL);
            SASSERTX(twotargets->dstc == 2);

            doconnect = sockscf.udpconnectdst;
         }
         else {
            /*
             * still have the other (first) target socket, so don't consider
             * this a fatal error.
             */
            iostatus = IO_TMPERROR;

            iolog(packetrule,
                  state,
                  IOOP(packetrule->verdict == VERDICT_BLOCK, iostatus),
                  clog,
                  dlog,
                  NULL,
                  NULL,
                  emsg,
                  strlen(emsg));

            send_icmperror(rawsocket,
                           &client->laddr,
                           &client->raddr,
                           &targetaddr,
                           -1,
                           -1,
                           icmp_type,
                           icmp_code);

            return iostatus;
         }

         sametarget = 0;
      }
      else
         sametarget = addrmatch(sockshost2ruleaddr(&target->raddrhost, NULL),
                                &header.host,
                                NULL,
                                SOCKS_UDP,
                                0);

      twotargets->s = target->s; /* update as soon as known. */

      sockaddr2sockshost(&target->laddr, &dlog->local);
      dlog->local_isset = 1;

      slog(LOG_DEBUG,
           "%s: use_saved_srule set.  UDP packet #%"PRIu64" from %s.  "
           "Destination %s.  Already set up with socks-rule #%lu (%s) "
           "for previous destination %s (%ssame as now)",
           function,
           client->read.packets + 1,
           sockaddr2string(&client->raddr, NULL, 0),
           sockshost2string(&header.host, NULL, 0),
           (unsigned long)packetrule->number,
           verdict2string(packetrule->verdict),
           sockshost2string(&target->raddrhost, hosta, sizeof(hosta)),
           sametarget ? "" : "not ");

      if (sametarget)
         permit = (packetrule->verdict == VERDICT_PASS);
      else {
         sockshost_t targethost;
         char clientstr[MAXSOCKADDRSTRING], dstbefore[MAXSOCKSHOSTSTRING],
              dstnow[MAXSOCKSHOSTSTRING];

         SASSERTX(iostatus == IO_NOERROR);

         permit = rulespermit(control->s,
                              &control->raddr,
                              &control->laddr,
                              cauth,
                              &control->auth,
                              packetrule,
                              state,
                              &client->host,
                              &header.host,
                              &targethost,
                              emsg,
                              sizeof(emsg));

         if (permit)
            header.host = targethost;

         if (!addtarget)
            slog(LOG_DEBUG,
                 "%s: destination host for packet from client %s changed "
                 "from %s to %s%s",
                 function,
                 sockaddr2string(&control->raddr, clientstr, sizeof(clientstr)),
                 sockshost2string(&target->raddrhost,
                                  dstbefore,
                                  sizeof(dstbefore)),
                 sockshost2string(&header.host,
                                  dstnow,
                                  sizeof(dstnow)),
                 permit ?
                   "" : ".  Packets to this address are not permitted however");

         /*
          * Target destination changed.
          * Unconnect the socket if it's connected and keep it unconnected,
          * so we can receive replies from the previous destination too.
          *
          * We do this regardless of whether the new destination is
          * permitted or not, as we have no reason to assume the previous
          * (permitted) target is any more likely to be the next target
          * than this (possibly, not permitted) target.  I.e., we want to
          * cache the negative rulespermit() verdict also.
          */

         if (target->isconnected) {
            sockd_unconnect(target->s, &target->raddr);
            target->isconnected = 0;
         }

         target->raddrhost = header.host;
         target->raddr     = targetaddr;

         if (permit) {
            /* possibly the socket options differ in this rule. */
            setconfsockoptions(target->s,
                               client->s,
                               SOCKS_UDP,
                               0,
                               packetrule->socketoptionc,
                               packetrule->socketoptionv,
                               SOCKETOPT_PRE | SOCKETOPT_ANYTIME,
                               0);
         }
      }

      if (permit) {
         if (redirect(target->s,
                      &target->laddr,
                      &target->raddrhost,
                      state->command,
                      &packetrule->rdr_from,
                      &packetrule->rdr_to) == 0)
            sockaddr2sockshost(&target->laddr, &dlog->local);
         else {
            snprintf(emsg, sizeof(emsg),
                     "redirect failed: %s", strerror(errno));

            if (errno != 0)
               swarnx("%s: %s", function, emsg);

            iostatus = IO_TMPERROR;
         }
      }
   }
   else {
      /*
       * No saved rule we can reuse.  Must do a new rule-lookup.
       */
      sockshost_t targethost;
      int addtarget;

      permit = rulespermit(control->s,
                           &control->raddr,
                           &control->laddr,
                           cauth,
                           &control->auth,
                           packetrule,
                           state,
                           &client->host,
                           &header.host,
                           &targethost,
                           emsg,
                           sizeof(emsg));

      if (permit) {
         /*
          * Ok, packet is permitted.  Now figure out what target struct to
          * use for forwarding this packet.
          */

         header.host = targethost;

         switch (twotargets->dstc) {
            case 0:
               /*
                * No target sockets created yet, so create the first now.
                */
               addtarget = 1;
               break;

            case 1:
               /*
                * Have one target socket created already.  Is it of the
                * same addressfamily as the target?
                */
               if (twotargets->dstv[0].raddr.ss_family
               == targetaddr.ss_family) {
                 target    = &twotargets->dstv[0]; /* yes. */
                 addtarget = 0;
               }
               else
                 addtarget = 1;

               break;

            case 2:
               /*
                * Have two target sockets created already.  One of them must
                * be of the correct addressfamily.
                */
               if (twotargets->dstv[0].raddr.ss_family == targetaddr.ss_family)
                 target = &twotargets->dstv[0]; /* this is the one. */
               else {
                  SASSERTX(twotargets->dstv[1].raddr.ss_family
                  == targetaddr.ss_family);

                 target = &twotargets->dstv[1]; /* this is the one. */
               }

               addtarget = 0;
               break;

            default:
               SERRX(twotargets->dstc);
         }

         if (addtarget) {
            SASSERTX(twotargets->dstc < 2);

            if (initclient(control->s,
                           &client->laddr,
                           &client->raddr,
                           &header.host,
                           &targetaddr,
                           packetrule,
                           emsg,
                           sizeof(emsg),
                           &twotargets->dstv[twotargets->dstc]) != NULL) {
               target = addclient(&client->laddr,
                                  &twotargets->dstv[twotargets->dstc],
                                  &twotargets->dstc,
                                  &twotargets->dstcmax,
                                  &twotargets->dstv,
                                  state,
                                  packetrule);

               SASSERTX(target != NULL);
               SASSERTX(twotargets->dstc > 0);

               doconnect = sockscf.udpconnectdst;
            }
            else
               iostatus = IO_TMPERROR;
         }

         if (target != NULL) {
            sockaddr2sockshost(&target->laddr, &dlog->local);
            dlog->local_isset = 1;
            twotargets->s     = target->s; /* update as soon as known. */
         }
      }
      else {
         /*
          * This packet was not permitted.
          */

         if (twotargets->dstc == 0) {
            /*
             * No targets sockets added yet and this packet from the client
             * was not permitted.  Is it possible other packets from this
             * client will be permitted?  Try.  If not, we can close this
             * session as there's no point in having the client keep
             * sending us packets if all of them will be blocked.
             * A bit unfortunate since we already told the client that
             * the udpassociate was ok, but that was only because it did
             * not tell us what address it would be sending us packets from.
             */
            rule_t tryrule;
            const int try_permits = rulespermit(control->s,
                                                &control->raddr,
                                                &control->laddr,
                                                cauth,
                                                &control->auth,
                                                &tryrule,
                                                state,
                                                &client->host,
                                                NULL, /* try any dst. */
                                                NULL,
                                                NULL,
                                                0);

            if (!try_permits) {
               slog(LOG_DEBUG,
                   "%s: only now we have the complete UDP client address (%s) "
                   "for the client that connected to us from %s.  "
                   "Packets from that client address are not permitted "
                   "according to current rules however, regardless of their "
                   "target destination.  We thus close this session now, "
                   "though we are unfortunately unable to tell the client why",
                   function,
                   sockaddr2string(&client->raddr, hosta, sizeof(hosta)),
                   sockaddr2string(&control->raddr, hostb, sizeof(hostb)));

               iostatus = IO_BLOCK;
            }
         }
      }

      if (iostatus != IO_NOERROR || !permit) {
         iolog(packetrule,
               state,
               IOOP(!permit, iostatus),
               clog,
               dlog,
               NULL,
               NULL,
               emsg,
               strlen(emsg));

         send_icmperror(rawsocket,
                        &client->laddr,
                        &client->raddr,
                        &targetaddr,
                        -1,
                        -1,
                        icmp_type,
                        iostatus == IO_NOERROR ?
                           ICMP_CODE_UNREACHABLE_HOSTPROHIBITED : icmp_code);

         if (iostatus != IO_NOERROR)
            return iostatus;
         else
            return IO_TMPBLOCK;
      }

      if (addtarget) {
         sametarget = 0;

         slog(LOG_DEBUG, "%s: new target: %s",
              function, sockshost2string(&header.host, hostb, sizeof(hostb)));
      }
      else {
         sametarget = addrmatch(sockshost2ruleaddr(&target->raddrhost, NULL),
                                &header.host,
                                NULL,
                                SOCKS_UDP,
                                0);

         slog(LOG_DEBUG,
              "%s: previous target: %s, current: %s (%s before)",
              function,
              sockshost2string(&target->raddrhost, hosta, sizeof(hosta)),
              sockshost2string(&header.host, hostb, sizeof(hostb)),
              sametarget ? "same as" : "different from");
      }

      if (target->isconnected && !sametarget) {

         /*
          * need to unconnect the socket so we can continue to receive
          * packets from the old destination.
          */
          sockd_unconnect(target->s, &target->raddr);
          target->isconnected = 0;
          target->raddr     = targetaddr;
      }
   }

   SASSERTX(target != NULL);

   target->client_read.bytes   += recvflags.fromsocket;
   target->client_read.packets += 1;

   client->state.use_saved_srule = 1;

   if (permit && iostatus == IO_NOERROR) {
      /*
       * Should we connect to the destination?
       * If the client will only be sending udp packets to one address, it
       * is more efficient to connect the socket to that address.
       * If we do that we must however be sure to unconnect the socket before
       * sending target on it again if the client wants to send to a new
       * address, and from that point on, leave the socket unconnected,
       * so that possible future packets from the address we first
       * sent/connected to will also be received.
       */

      if (doconnect) {
         slog(LOG_DEBUG, "%s: connecting fd %d to %s, for client %s",
              function,
              target->s,
              sockshost2string(&header.host, hosta, sizeof(hosta)),
              sockshost2string(&client->host, hostb, sizeof(hostb)));

         if (socks_connecthost(target->s,
                               EXTERNALIF,
                               &header.host,
                               NULL,
                               NULL,
                               (long)-1,
                               emsg,
                               sizeof(emsg)) == -1) {
            iolog(packetrule,
                  state,
                  OPERATION_TMPERROR,
                  clog,
                  dlog,
                  NULL,
                  NULL,
                  emsg,
                  strlen(emsg));

            iostatus = IO_TMPERROR;
            return iostatus;
         }

         target->isconnected = 1;
      }

      sendtoflags.side = EXTERNALIF;
      w = socks_sendto(target->s,
                       payload,
                       payloadlen,
                       0,
                       target->isconnected ?  NULL : &target->raddr,
                       target->isconnected ?
                       (socklen_t)0 : (socklen_t)sizeof(target->raddr),
                       &sendtoflags,
                       NULL);

      if (w >= 0) {
         iostatus = io_packet_sent(payloadlen,
                                   w,
                                   &recvflags.ts,
                                   &client->raddr,
                                   &target->raddr,
                                   emsg,
                                   sizeof(emsg));

         *bwused = w;

         target->target_written.bytes   += sendtoflags.tosocket;
         target->target_written.packets += 1;

         gettimeofday_monotonic(&target->lastio);

         /*
          * Also update twotargets so caller can know how much i/o was done
          * on this call, as otherwise he would have no way of knowing this
          * because he could not beforehand know what our target socket would
          * be (could even be a new target).
          */
         twotargets->written.bytes   += sendtoflags.tosocket;
         twotargets->written.packets += 1;
      }
      else {
         snprintf(emsg, sizeof(emsg), "sendto of %lu bytes failed: %s",
                  (unsigned long)payloadlen, strerror(errno));

         iostatus = IOSTATUS_UDP_SEND_FAILED(errno);
         *badfd   = target->s;
      }
   }
   else
      w = -1;

   if (iostatus != IO_NOERROR || !permit) {
      iolog(packetrule,
            state,
            IOOP(!permit, iostatus),
            clog,
            dlog,
            NULL,
            NULL,
            emsg,
            strlen(emsg));

      send_icmperror(rawsocket,
                     &client->laddr,
                     &client->raddr,
                     &targetaddr,
                     -1,
                     -1,
                     icmp_type,
                     iostatus == IO_NOERROR ?
                        ICMP_CODE_UNREACHABLE_HOSTPROHIBITED : icmp_code);

      if (iostatus != IO_NOERROR)
         return iostatus;
      else
         return IO_TMPBLOCK;
   }

   SASSERTX(w == (ssize_t)payloadlen);

   iolog(packetrule,
         state,
         OPERATION_IO,
         clog,
         dlog,
         NULL,
         NULL,
         payload,
         payloadlen);

   return IO_NOERROR;
}

iostatus_t
io_udp_target2client(control, client, twotargets, state,
                     clog, dlog, badfd, packetrule, bwused)
   sockd_io_direction_t *control;
   sockd_io_direction_t *client;
   sockd_io_direction_t *twotargets;
   connectionstate_t *state;
   iologaddr_t *clog;
   iologaddr_t *dlog;
   int *badfd;
   rule_t *packetrule;
   size_t *bwused;
{
   /*
    * Received a reply from one of the up to two twotargets sockets (one for
    * ipv4 and one for ipv6).
    * When called we already know what the twotargets socket is and things
    * should be set up correctly.
    */
   const char *function = "io_udp_target2client()";
   iostatus_t iostatus = IO_NOERROR;
   udptarget_t *target = NULL;
   sendto_info_t sendtoflags;
   recvfrom_info_t recvflags;
   struct sockaddr_storage from, hostaddr;
   socklen_t len;
   ssize_t w, r;
   size_t headerlen, payloadlen, emsglen, originallen;
   char hosta[MAXSOCKSHOSTSTRING], hostb[MAXSOCKSHOSTSTRING], emsg[1024],
        buf[SOCKD_BUFSIZE + sizeof(udpheader_t)], *payload;
   int samesrc, permit = 0,
       icmp_type = ICMP_TYPE_UNREACHABLE,
       icmp_code = ICMP_CODE_UNREACHABLE_PORT;

   target = clientofsocket(twotargets->s, twotargets->dstc, twotargets->dstv);
   SASSERTX(target != NULL);

   *emsg = NUL;

   recvflags.side = EXTERNALIF;
   recvflags.peer = twotargets->raddr;
   recvflags.type = SOCK_DGRAM;

   len = sizeof(from);
   if ((r = socks_recvfrom(twotargets->s,
                           buf,
                           sizeof(buf),
                           0,
                           &from,
                           &len,
                           &recvflags,
                           &twotargets->auth)) == -1) {
      if (ERRNOISPREVIOUSPACKET(errno)) {
         /*
          * error is from the target of an earlier packet from client,
          * sent by us /out/ on this socket, i.e. from client to target.
          * Note that Linux apparently can return this error even if
          * the socket is not connected.
          *
          * Don't treat it as fatal, more packets could come, and they
          * may be accepted by the twotargets.
          */
         const int oldcommand = state->command;
         state->command       = SOCKS_UDPASSOCIATE;

         *badfd = client->s;

         emsglen = snprintf(emsg, sizeof(emsg),
                            "%s (delayed error)", strerror(errno));

         iolog(packetrule,
               state,
               OPERATION_TMPERROR,
               clog,
               dlog,
               NULL,
               NULL,
               emsg,
               emsglen);

         state->command = oldcommand;

         send_icmperror(rawsocket,
                        &client->laddr,
                        &client->raddr,
                        &twotargets->raddr,
                        -1,
                        -1,
                        icmp_type,
                        icmp_code);

         /* tmp error; using control connection do detect fatal errors. */
         return IO_TMPERROR;
      }

      *badfd = twotargets->s;

      if (ERRNOISTMP(errno))
         return IO_EAGAIN;
      else {
         log_unexpected_udprecv_error(function,
                                      twotargets->s,
                                      errno,
                                      EXTERNALIF);
         return IO_IOERROR;
      }
   }

   iostatus = io_packet_received(&recvflags, r, &from, &twotargets->laddr);

   gettimeofday_monotonic(&target->lastio);

   if (iostatus != IO_NOERROR) {
      *badfd = twotargets->s;
      return iostatus;
   }

   /*
    * Read a packet.  Now check whether it should be forwarded.
    */

   payloadlen                   = r;

   target->target_read.bytes   += recvflags.fromsocket;
   target->target_read.packets += 1;

   slog(LOG_DEBUG,
        "%s: udp packet #%"PRIu64" from twotargets %s received for client %s "
        "on fd %d, packet length is %ld, use_saved_srule is %d",
        function,
        twotargets->read.packets,
        sockaddr2string(&from, hosta, sizeof(hosta)),
        sockaddr2string(&client->raddr, hostb, sizeof(hostb)),
        twotargets->s,
        (long)r,
        twotargets->state.use_saved_srule);

   if (twotargets->written.packets == 0) {
      slog(LOG_DEBUG,
           "%s: unusual ... the client at %s has not sent any packets "
           "yet, but is receiving a %ld byte reply on address %s from %s",
           function,
           sockaddr2string(&client->raddr, NULL, 0),
           (long)r,
           sockaddr2string(&twotargets->laddr, hosta, sizeof(hosta)),
           sockaddr2string(&from, hostb, sizeof(hostb)));
   }

   /*
    * Can we reuse the saved rule, if any, from the last time we received a
    * packet on this socket, or must we do a new rule-lookup now?
    */
   if (twotargets->state.use_saved_srule) {
      /*
       * Yes, previous rule lookup is valid, but if the packet is received
       * from the same address as previously.
       */
      if (twotargets->state.isconnected) {
         slog(LOG_DEBUG, "%s: socket is connected.  Kernel should take care "
                         "of making sure the source address %s is the same "
                         "as last, so that we can reuse previous rule-lookup "
                         "(rule #%lu, verdict: %s)",
                         function,
                         sockaddr2string(&from, NULL, 0),
                         (unsigned long)packetrule->number,
                         verdict2string(packetrule->verdict));
         samesrc = 1;
      }
      else {
         if (IPADDRISBOUND(&twotargets->raddr)) {
            if (! (samesrc = sockaddrareeq(&twotargets->raddr, &from, 0))) {
               /*
                * ack, not reply from previous twotargets.
                */
               slog(LOG_DEBUG,
                    "%s: use_saved_srule set.  UDP packet #%"PRIu64" from %s.  "
                    "Destination %s.  Previously set up with "
                    "%s #%lu (%s) for packets from twotargets %s.  "
                    "Not the same as now, so have to do a new rule-lookup",
                    function,
                    twotargets->read.packets,
                    sockaddr2string(&from, hosta, sizeof(hosta)),
                    sockshost2string(&client->host, hostb, sizeof(hostb)),
                    objecttype2string(packetrule->type),
                    (unsigned long)packetrule->number,
                    verdict2string(packetrule->verdict),
                    sockaddr2string(&twotargets->raddr, NULL, 0));
            }
         }
         else
            samesrc = 0; /* have nothing we should compare with. */
      }
   }
   else
      samesrc = 0; /* have nothing we should compare with. */

   if (samesrc)
      permit = (packetrule->verdict == VERDICT_PASS);
   else {
      /*
       * Nope, can not reuse the last rule lookup.  Have to do a new one.
       */

      twotargets->raddr = from;

      sockaddr2sockshost(&twotargets->raddr, &twotargets->host);
      dlog->peer        = twotargets->host;

      slog(LOG_DEBUG,
           "%s: received packet from %s to %s on fd %d, "
           "use_saved_srule is %d, samesrc is %d",
           function,
           sockaddr2string(&twotargets->raddr, hosta, sizeof(hosta)),
           sockaddr2string(&client->raddr, hostb, sizeof(hostb)),
           twotargets->s,
           twotargets->state.use_saved_srule,
           samesrc);

      if (!twotargets->state.use_saved_srule) {
         if (client->written.packets > 0)
         slog(LOG_DEBUG, "%s: not first packet and use_saved_srule not set "
                         "... should only happen after SIGHUP",
                         function);
      }

      SASSERTX(iostatus == IO_NOERROR);

      permit = rulespermit(control->s,
                           &control->raddr,
                           &control->laddr,
                           NULL,
                           &twotargets->auth,
                           packetrule,
                           state,
                           &twotargets->host,
                           &client->host,
                           NULL,
                           NULL,
                           0);

      if (permit) {
         /* use redirected addresses, if applicable. */
         /*
          * XXX make sure this does not change the address of s, as
          * we need to continue receiving packets on that socket.
          */
         struct sockaddr_storage p;

         if (redirect(client->s,
                      &client->laddr,
                      &client->host,
                      state->command,
                      &packetrule->rdr_from,
                      &packetrule->rdr_to) == 0) {

            /*
             * Use ipaddresses when logging.
             */

            sockaddr2sockshost(&client->laddr, &clog->local);
            sockaddr2sockshost(sockshost2sockaddr(&client->host, &p),
                               &clog->peer);
         }
         else {
            snprintf(emsg, sizeof(emsg),
                     "redirect failed: %s", strerror(errno));

            iostatus = IO_TMPERROR;
         }
      }
   }

   if (iostatus != IO_NOERROR || !permit) {
      iolog(packetrule,
            state,
            IOOP(!permit, iostatus),
            dlog,
            clog,
            NULL,
            NULL,
            emsg,
            strlen(emsg));

      send_icmperror(rawsocket,
                     &twotargets->laddr,
                     &twotargets->raddr,
                     sockshost2sockaddr(&client->host, &hostaddr),
                     -1,
                     -1,
                     icmp_type,
                     iostatus == IO_NOERROR ?
                        ICMP_CODE_UNREACHABLE_HOSTPROHIBITED : icmp_code);

      if (iostatus != IO_NOERROR)
         return iostatus;
      else
         return IO_TMPBLOCK;
   }

   twotargets->state.use_saved_srule = 1;

   sendtoflags.side = INTERNALIF;

   originallen = payloadlen;
   payload = udpheader_add(sockaddr2sockshost(&twotargets->raddr, NULL),
                           buf,
                           &payloadlen,
                           sizeof(buf));

   SASSERTX(payload == buf);
   SASSERTX(payloadlen > (size_t)r);
   SASSERTX(payloadlen - originallen >= MINSOCKSUDPHLEN);

   headerlen = payloadlen - originallen;

   w = socks_sendto(client->s,
                    payload,
                    payloadlen,
                    0,
                    client->state.isconnected ?  NULL : &client->raddr,
                    client->state.isconnected ?
                        (socklen_t)0 : (socklen_t)sizeof(client->raddr),
                    &sendtoflags,
                    &client->auth);

   if (w >= 0) {
      iostatus = io_packet_sent(payloadlen,
                                w,
                                &recvflags.ts,
                                &twotargets->raddr,
                                &client->raddr,
                                emsg,
                                sizeof(emsg));

      *bwused = w;

      target->client_written.bytes   += sendtoflags.tosocket;
      target->client_written.packets += 1;
   }
   else {
      snprintf(emsg, sizeof(emsg), "sendto of %lu bytes failed: %s",
               (unsigned long)payloadlen, strerror(errno));

      iostatus = IOSTATUS_UDP_SEND_FAILED(errno);
      *badfd   = client->s;
   }

   if (iostatus != IO_NOERROR) {
      iolog(packetrule,
            state,
            IOOP(!permit, iostatus),
            dlog,
            clog,
            NULL,
            NULL,
            emsg,
            strlen(emsg));

      send_icmperror(rawsocket,
                     &twotargets->laddr,
                     &twotargets->raddr,
                     sockshost2sockaddr(&client->host, &hostaddr),
                     -1,
                     -1,
                     icmp_type,
                     icmp_code);

      return iostatus;
   }

   SASSERTX(w == (ssize_t)payloadlen);

   iolog(packetrule,
         state,
         OPERATION_IO,
         dlog,
         clog,
         NULL,
         NULL,
         payload    + headerlen,
         payloadlen - headerlen);

   return IO_NOERROR;
}


static int
fromaddr_as_expected(expected, from, emsg, emsglen)
   struct sockaddr_storage *expected;
   const struct sockaddr_storage *from;
   char *emsg;
   size_t emsglen;
{
   const char *function = "fromaddr_as_expected()";
   char expectedstr[MAXSOCKADDRSTRING], fromstr[MAXSOCKADDRSTRING],
        buf[sizeof(expectedstr) + sizeof(fromstr) + 256];
   int matches;

   snprintf(buf, sizeof(buf),
            "expected udp packet from clientaddress %s, got it from %s",
            sockaddr2string(expected, expectedstr, sizeof(expectedstr)),
            sockaddr2string(from, fromstr, sizeof(fromstr)));

   if (!ADDRISBOUND(expected)) {
      /*
       * Client hasn't sent us it's complete address yet, but if
       * the parts of the address it has sent (if any) matches
       * the source of this packet, we have to assume this packet
       * is from it.  We can then update the expected address with
       * complete info, based on this packet.
       *
       * We also connect the socket to the client, for better performance,
       * for receiving errors from sendto(2), for getpeername(2) by libwrap
       * in rulespermit(), for ...  well, that's reasons enough already.
       */
       struct sockaddr_storage test;

       sockaddrcpy(&test, expected, sizeof(test));

      if (!IPADDRISBOUND(expected))
         SET_SOCKADDRADDR(&test,   GET_SOCKADDRADDR(from));

      if (!PORTISBOUND(expected))
         SET_SOCKADDRPORT((&test), GET_SOCKADDRPORT(from));

      matches = sockaddrareeq(&test, from, 0);

      if (matches) {
         /*
          * from-address matches the parts the client told us, so
          * assume this packet is from the client and update expected
          * address to contain the complete address, both ip and port.
          */
         sockaddrcpy(expected, &test, sizeof(test));
      }
      /* else; no match, presumably not from client. */
   }
   else /* clients complete address is known to us.  Matches this packet? */
      matches = sockaddrareeq(expected, from, 0);

   if (!matches && emsglen > 0) {
      strncpy(emsg, buf, emsglen - 1);
      emsg[emsglen - 1] = NUL;
   }

   slog(LOG_DEBUG, "%s: %s: %s",
        function, buf, matches ? "matches" : "no match");

   return matches;
}

udpheader_t *
getudptarget(buf, buflen, header, headerlen, emsg, emsglen)
   const char *buf;
   const size_t buflen;
   udpheader_t *header;
   size_t *headerlen;
   char *emsg;
   const size_t emsglen;
{
   const char *function = "getudptarget()";

   if (string2udpheader(buf, buflen, header) == NULL) {
      snprintf(emsg, emsglen,
               "SOCKS protocol error in socks udp packet of length %lu.  "
               "Could not extract valid address from SOCKS UDP header",
               (unsigned long)buflen);

      header->host.atype = SOCKS_ADDR_NOTSET;
      return NULL;
   }

   if (header->frag != 0) {
      snprintf(emsg, emsglen, "fragmented socks udp packets are not supported");
      return NULL;
   }

   *headerlen = HEADERSIZE_UDP(header);

   switch (header->host.atype) {
      case SOCKS_ADDR_IPV6:
         if (IN6_IS_ADDR_V4MAPPED(&header->host.addr.ipv6.ip)) {
            /*
             * We never use that; either we have IPv4 available on the
             * external interface, or we do not.  Convert it to an ordinary
             * IPv4 address and proceed as usual.
             */
            sockshost_t convertedhost;

            convertedhost = header->host;
            ipv4_mapped_to_regular(&header->host.addr.ipv6.ip,
                                   &convertedhost.addr.ipv4);

            header->host.atype     = SOCKS_ADDR_IPV4;
            header->host.addr.ipv4 = convertedhost.addr.ipv4;
         }

         /* FALLTHROUGH */

      case SOCKS_ADDR_IPV4:
         if (!external_has_safamily(atype2safamily(header->host.atype))) {
            snprintf(emsg, emsglen,
                     "packet for %s target, but no %s configured for "
                     "our usage on the external interface ",
                     atype2string(header->host.atype),
                     atype2string(header->host.atype));

            return NULL;
         }

         break;

      case SOCKS_ADDR_DOMAIN:
         break;

      default:
         SERRX(header->host.atype);
   }

   return header;
}
