1 /*
2  * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2020
3  *      Inferno Nettverk A/S, Norway.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. The above copyright notice, this list of conditions and the following
9  *    disclaimer must appear in all copies of the software, derivative works
10  *    or modified versions, and any portions thereof, aswell as in all
11  *    supporting documentation.
12  * 2. All advertising materials mentioning features or use of this software
13  *    must display the following acknowledgement:
14  *      This product includes software developed by
15  *      Inferno Nettverk A/S, Norway.
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * Inferno Nettverk A/S requests users of this software to return to
31  *
32  *  Software Distribution Coordinator  or  sdc@inet.no
33  *  Inferno Nettverk A/S
34  *  Oslo Research Park
35  *  Gaustadall�en 21
36  *  NO-0349 Oslo
37  *  Norway
38  *
39  * any improvements or extensions that they make and grant Inferno Nettverk A/S
40  * the rights to redistribute these changes.
41  *
42  */
43 
44 static const char rcsid[] =
45 "$Id: upnp.c,v 1.153.4.4.2.2.4.2 2020/11/11 16:11:55 karls Exp $";
46 
47 #include "common.h"
48 
49 #include "upnp.h"
50 
51 #if HAVE_LIBMINIUPNP
52 
53 #if SOCKS_CLIENT
54 static struct sigaction oldsig;
55 static void sighandler(int sig);
56 static void atexit_upnpcleanup(void);
57 #endif /* SOCKS_CLIENT */
58 
59 #endif /* HAVE_LIBMINIUPNP */
60 
61 int
socks_initupnp(gw,emsg,emsglen)62 socks_initupnp(gw, emsg, emsglen)
63    gateway_t *gw;
64    char *emsg;
65    const size_t emsglen;
66 {
67    const char *function = "socks_initupnp()";
68 #if HAVE_LIBMINIUPNP
69    struct UPNPDev *dev;
70    struct UPNPUrls url;
71    struct IGDdatas data;
72    char myaddr[INET_ADDRSTRLEN], addrstring[MAXSOCKSHOSTSTRING], vbuf[1024];
73    int devtype, rc;
74 
75    if (*gw->state.data.upnp.controlurl != NUL) {
76       slog(LOG_DEBUG, "%s: already inited with controlurl %s",
77            function,
78            str2vis(gw->state.data.upnp.controlurl,
79                    strlen(gw->state.data.upnp.controlurl),
80                    vbuf,
81                    sizeof(vbuf)));
82 
83       return 0;
84    }
85 
86    slog(LOG_DEBUG, "%s", function);
87 
88    bzero(&url, sizeof(url));
89    bzero(&data, sizeof(data));
90    errno = 0;
91 
92    if (gw->addr.atype == SOCKS_ADDR_URL) {
93       slog(LOG_NEGOTIATE, "%s: trying to use UPNP IGD at %s",
94            function, str2vis(gw->addr.addr.urlname,
95                              strlen(gw->addr.addr.urlname),
96                              vbuf,
97                              sizeof(vbuf)));
98 
99       if (UPNP_GetIGDFromUrl(gw->addr.addr.urlname,
100                              &url,
101                              &data,
102                              myaddr,
103                              sizeof(myaddr)) != 1) {
104          snprintf(emsg, emsglen, "failed to contact UPNP IGD at url %s: %s",
105                   vbuf, strerror(errno));
106          swarnx("%s: %s", function, emsg);
107 
108          if (errno == 0)
109             errno = ENETUNREACH;
110 
111          return -1;
112       }
113 
114       slog(LOG_DEBUG, "%s: UPNP_GetIGDFromUrl() url %s was successful",
115            function, vbuf);
116 
117       rc = 0;
118    }
119    else {
120       struct UPNPDev *p;
121       struct sockaddr_storage addr;
122       command_t commands;
123       protocol_t protocols;
124 
125       slog(LOG_NEGOTIATE, "%s: searching for UPNP IGD using address %s",
126            function, sockshost2string(&gw->addr, NULL, 0));
127 
128       sockshost2sockaddr(&gw->addr, &addr);
129       if (inet_ntop(addr.ss_family,
130                     GET_SOCKADDRADDR(&addr),
131                     addrstring,
132                     sizeof(addrstring)) == NULL) {
133          snprintf(emsg, emsglen,
134                   "%s: failed to convert %s to an ipaddress: %s",
135                   function,
136                   sockshost2string(&gw->addr, NULL, 0),
137                   strerror(errno));
138 
139          swarnx("%s: %s", function, emsg);
140          return -1;
141       }
142 
143       slog(LOG_NEGOTIATE,
144            "%s: doing upnp discovery on interface belonging to addr %s, "
145            "resolved from %s.  errno = %d",
146            function, addrstring, sockshost2string(&gw->addr, NULL, 0), errno);
147 
148 #if SOCKS_CLIENT && SOCKSLIBRARY_DYNAMIC
149       socks_mark_io_as_native();
150 #endif /* SOCKS_CLIENT && SOCKSLIBRARY_DYNAMIC */
151 
152       errno = 0;
153       dev   = upnpDiscover(UPNP_DISCOVERYTIME_MS,
154                            addrstring,
155                            NULL,
156                            0
157 #if HAVE_LIBMINIUPNP17
158                           ,0,
159 
160 #if MINIUPNPC_API_VERSION >= 14
161                            UPNP_IP_TTL,
162 #endif /* MINIUPNPC_API_VERSION >= 14 */
163 
164                           &rc
165 #endif /* HAVE_LIBMINIUPNP17 */
166                          );
167 
168 #if SOCKS_CLIENT && SOCKSLIBRARY_DYNAMIC
169       socks_mark_io_as_normal();
170 #endif /* SOCKS_CLIENT && SOCKSLIBRARY_DYNAMIC */
171 
172       if (dev == NULL) {
173          snprintf(emsg, emsglen, "upnpDiscover() failed: %s", strerror(errno));
174          swarnx("%s: %s", function, emsg);
175 
176          if (errno == 0)
177             errno = ENETUNREACH;
178 
179          return -1;
180       }
181 
182       slog(LOG_NEGOTIATE,
183            "%s: upnp devices found.  Adding direct routes for them", function);
184 
185       bzero(&commands, sizeof(commands));
186       bzero(&protocols, sizeof(protocols));
187 
188       for (p = dev; p != NULL; p = p->pNext) {
189          struct sockaddr_storage saddr, smask;
190          int gaierr;
191 
192          if (urlstring2sockaddr(p->descURL, &saddr, &gaierr, emsg, emsglen)
193          == NULL) {
194             log_resolvefailed(p->descURL, EXTERNALIF, gaierr);
195             continue;
196          }
197 
198          bzero(&smask, sizeof(smask));
199          SET_SOCKADDR(&smask, AF_INET);
200          TOIN(&smask)->sin_addr.s_addr = htonl(IPV4_FULLNETMASK);
201 
202          commands.connect      = 1;
203          commands.udpassociate = 1;
204 
205          protocols.tcp         = 1;
206          protocols.udp         = 1;
207 
208          socks_autoadd_directroute(&commands, &protocols, &saddr, &smask);
209       }
210 
211       devtype = UPNP_GetValidIGD(dev, &url, &data, myaddr, sizeof(myaddr));
212       switch (devtype) {
213          case UPNP_NO_IGD:
214             snprintf(emsg, emsglen, "no UPNP IGD discovered on local network");
215             swarnx("%s: %s", function, emsg);
216             rc = -1;
217             break;
218 
219          case UPNP_CONNECTED_IGD:
220             slog(LOG_NEGOTIATE, "%s: UPNP IGD discovered at url %s",
221                  function,
222                  str2vis(url.controlURL,
223                          strlen(url.controlURL),
224                          vbuf,
225                          sizeof(vbuf)));
226             rc = 0;
227             break;
228 
229          case UPNP_DISCONNECTED_IGD:
230             snprintf(emsg, emsglen,
231                     "UPNP IGD discovered at url %s, but it is not connected",
232                     str2vis(url.controlURL,
233                            strlen(url.controlURL),
234                             vbuf,
235                             sizeof(vbuf)));
236 
237             swarnx("%s: %s", function, emsg);
238             rc = -1;
239             break;
240 
241          case UPNP_UNKNOWN_DEVICE:
242             snprintf(emsg, emsglen,
243                      "%s: unknown upnp device discovered at url %s",
244                      function,
245                      str2vis(url.controlURL,
246                              strlen(url.controlURL),
247                              vbuf,
248                              sizeof(vbuf)));
249 
250             swarnx("%s: %s", emsg, function);
251 
252             rc = -1;
253             break;
254 
255          default:
256             snprintf(emsg, emsglen,
257                      "%s: unknown return code from UPNP_GetValidIGD(): %d (%s)",
258                      function, devtype, strerror(errno));
259 
260             swarnx("%s: %s", function, emsg);
261             rc = -1;
262       }
263 
264       freeUPNPDevlist(dev);
265    }
266 
267    if (rc == 0) {
268       char visbuf2[sizeof(vbuf)];
269 
270       SASSERTX(url.controlURL != NULL);
271       STRCPY_ASSERTLEN(gw->state.data.upnp.controlurl, url.controlURL);
272 
273 #if HAVE_LIBMINIUPNP13
274       STRCPY_ASSERTLEN(gw->state.data.upnp.servicetype, data.servicetype);
275 
276 #elif HAVE_LIBMINIUPNP14 || HAVE_LIBMINIUPNP17
277       STRCPY_ASSERTLEN(gw->state.data.upnp.servicetype, data.CIF.servicetype);
278 
279 #else
280 #  error "unexpected miniupnp version"
281 #endif /* HAVE_LIBMINIUPNP17 */
282 
283       slog(LOG_NEGOTIATE, "%s: inited ok.  controlurl: %s, servicetype: %s",
284            function,
285            str2vis(gw->state.data.upnp.controlurl,
286                    strlen(gw->state.data.upnp.controlurl),
287                    vbuf,
288                    sizeof(vbuf)),
289            str2vis(gw->state.data.upnp.servicetype,
290                    strlen(gw->state.data.upnp.servicetype),
291                    visbuf2,
292                    sizeof(visbuf2)));
293    }
294    else {
295       if (errno == 0)
296          errno = ENETUNREACH;
297    }
298 
299    FreeUPNPUrls(&url);
300 
301    return rc;
302 
303 #else /* !HAVE_LIBMINIUPNP */
304    return -1;
305 
306 #endif /* !HAVE_LIBMINIUPNP */
307 }
308 
309 int
upnp_negotiate(s,packet,gw,emsg,emsglen)310 upnp_negotiate(s, packet, gw, emsg, emsglen)
311    const int s;
312    socks_t *packet;
313    gateway_t *gw;
314    char *emsg;
315    const size_t emsglen;
316 {
317    const char *function = "upnp_negotiate()";
318 #if HAVE_LIBMINIUPNP
319    struct sockaddr_storage addr;
320    socklen_t addrlen;
321    char straddr[INET6_ADDRSTRLEN], strport[sizeof("65535")], vbuf[1024];
322    int rc;
323 #endif /* HAVE_LIBMINIUPNP */
324 
325    slog(LOG_DEBUG, "%s: command %s",
326         function, command2string(packet->req.command));
327 
328 #if !HAVE_LIBMINIUPNP
329    SERRX(0);
330 
331 #else
332    packet->res.version = PROXY_UPNP;
333 
334    switch (packet->req.command) {
335       case SOCKS_CONNECT: {
336          /*
337           * Can only find out what the external ip address of the device is.
338           *
339           * We could fetch the address here, but if the client never intends
340           * to find out what it's local address is, that's a waste of time.
341           * Therefor postpone it to the Rgetsockname() call, if it ever
342           * comes, and just connect(2) to the target for now, without
343           * attempting to retrieve any information from the IGD.
344           *
345           * For the socks server case (server chained) we need to fetch
346           * it here though, since it is part of the response returned
347           * to the socks client.
348           */
349 
350          if (socks_connecthost(s,
351 #if !SOCKS_CLIENT
352                                EXTERNALIF,
353 #endif /* !SOCKS_CLIENT */
354                                &packet->req.host,
355                                NULL,
356                                NULL,
357                                sockscf.timeout.connect
358                                  ? (long)sockscf.timeout.connect : (long)-1,
359                                emsg,
360                                emsglen) != 0)
361             if (errno != EINPROGRESS) {
362                slog(LOG_NEGOTIATE, "%s: socks_connecthost(%s) failed: %s",
363                     function,
364                     sockshost2string(&packet->req.host, NULL, 0),
365                     emsg);
366 
367                socks_set_responsevalue(&packet->res,
368                                        errno2reply(errno, PROXY_UPNP));
369                return -1;
370             }
371       }
372 
373       /* FALLTHROUGH */
374 
375       case SOCKS_UDPASSOCIATE: {
376          /*
377           * if it was a bind, it would be handled the same as the
378           * tcp bind, so this means the client starts by wanting
379           * to send a udp packet, or we just did a connect(2).
380           *
381           * Similarly to a connect, the only information we can provide
382           * here is the external ip address of the control device.
383           * Postponed for the same reason as for connect.
384           */
385          const int errno_s = errno;
386 
387          packet->res.host.atype = SOCKS_ADDR_IPV4;
388 
389 #if SOCKS_CLIENT
390          /*
391           * will be filled with real value if user ever does getsockname(2).
392           * Don't waste time on getting the address until it's needed.
393           */
394          slog(LOG_NEGOTIATE,
395               "%s: no need to contact UPNP IGD %s yet, "
396               "so just pretending all is ok so far",
397               function, sockshost2string(&gw->addr, NULL, 0));
398 
399          packet->res.host.addr.ipv4.s_addr = htonl(INADDR_ANY);
400 
401 #else /* SOCKS_SERVER.  Server needs to know now so it can tell client. */
402          if (socks_initupnp(gw, emsg, emsglen) != 0) {
403             swarnx("%s: socks_initupnp() could not init upnp state: %s",
404                    function, emsg);
405 
406             socks_set_responsevalue(&packet->res, UPNP_FAILURE);
407             return -1;
408          }
409 
410          if ((rc = UPNP_GetExternalIPAddress(gw->state.data.upnp.controlurl,
411                                              gw->state.data.upnp.servicetype,
412                                              straddr)) != UPNPCOMMAND_SUCCESS) {
413             snprintf(emsg, emsglen,
414                      "failed to get external ip address of UPNP device at %s, "
415                      "upnp-error: %d",
416                      str2vis(gw->state.data.upnp.controlurl,
417                              strlen(gw->state.data.upnp.controlurl),
418                              vbuf,
419                              sizeof(vbuf)),
420                      rc);
421 
422             swarnx("%s: %s", function, emsg);
423 
424             socks_set_responsevalue(&packet->res, UPNP_FAILURE);
425             return -1;
426          }
427          else
428             slog(LOG_NEGOTIATE, "%s: server will use address %s on our behalf",
429                  function,
430                  str2vis(straddr, strlen(straddr), vbuf, sizeof(vbuf)));
431 
432          if (socks_inet_pton(AF_INET,
433                              straddr,
434                              &packet->res.host.addr.ipv4,
435                              NULL) == 1)
436             packet->res.host.atype = SOCKS_ADDR_IPV4;
437          else if (socks_inet_pton(AF_INET6,
438                                   straddr,
439                                   &packet->res.host.addr.ipv6,
440                                   NULL) == 1)
441             packet->res.host.atype = SOCKS_ADDR_IPV6;
442          else
443             slog(LOG_NOTICE,
444                  "%s: could not convert string \"%s\" to address: %s",
445                  function,
446                  str2vis(straddr,
447                          strlen(straddr),
448                          vbuf,
449                          sizeof(vbuf)),
450                  strerror(errno));
451 #endif /* SOCKS_SERVER */
452 
453          addrlen = sizeof(addr);
454          if (getsockname(s, TOSA(&addr), &addrlen) != 0) {
455             snprintf(emsg, emsglen,
456                      "getsockname(s) on socket failed: %s", strerror(errno));
457 
458             break;
459          }
460 
461          slog(LOG_NEGOTIATE,
462               "%s: will never know for sure, but hoping IGD will use same "
463               "port as we: %u",
464               function, ntohs(packet->res.host.port));
465 
466          packet->res.host.port = GET_SOCKADDRPORT(&addr);
467          errno = errno_s;
468 
469          break;
470       }
471 
472       case SOCKS_BIND: {
473          /*
474           * Need tell the device to create a port mapping, mapping an
475           * address on it's side to the address we have bound.
476           * Then we need to get the ip address the device is using
477           * on the external side.
478           */
479 #if SOCKS_CLIENT
480          static int atexit_registered;
481 #endif /* SOCKS_CLIENT */
482          const int errnoval = EADDRNOTAVAIL;
483          struct sockaddr_storage extaddr;
484          socklen_t len;
485          char buf[256], protocol[16];
486          int val;
487 
488          addrlen = sizeof(addr);
489          if (getsockname(s, TOSA(&addr), &addrlen) != 0) {
490             snprintf(emsg, emsglen,
491                      "getsockname(s) on socket failed: %s", strerror(errno));
492 
493             socks_set_responsevalue(&packet->res, UPNP_FAILURE);
494             return -1;
495          }
496 
497          if (!PORTISBOUND(&addr)) {
498             slog(LOG_NEGOTIATE,
499                  "%s: port not yet bound locally, so need to bind it before "
500                  "we tell the UPNP-router to portforward to our bound port",
501                  function);
502 
503             if (socks_bind(s, &addr, 0) != 0) {
504                snprintf(emsg, emsglen,
505                         "could not bind local address %s on fd %d before "
506                         "portforward request to UPNP-router: %s",
507                          sockaddr2string(&addr, NULL, 0), s, strerror(errno));
508 
509                swarnx("%s: %s", function, emsg);
510 
511                socks_set_responsevalue(&packet->res, UPNP_FAILURE);
512                return -1;
513             }
514          }
515 
516          if (socks_initupnp(gw, emsg, emsglen) != 0) {
517             swarnx("%s: socks_initupnp() could not init upnp state: %s",
518                    function, emsg);
519 
520             socks_set_responsevalue(&packet->res, UPNP_FAILURE);
521 
522             errno = errnoval;
523             return -1;
524          }
525 
526          if ((rc = UPNP_GetExternalIPAddress(gw->state.data.upnp.controlurl,
527                                              gw->state.data.upnp.servicetype,
528                                              straddr)) != UPNPCOMMAND_SUCCESS) {
529             snprintf(emsg, emsglen,
530                      "failed to get external ip address of upnp device, "
531                      "error: %d",
532                      rc);
533 
534             socks_set_responsevalue(&packet->res, UPNP_FAILURE);
535 
536             errno = errnoval;
537             return -1;
538          }
539 
540          extaddr = addr; /* init. */
541 
542          if ((rc = socks_inet_pton(extaddr.ss_family,
543                                    straddr,
544                                    (void *)GET_SOCKADDRADDR(&extaddr),
545                                    NULL)) != 1) {
546             snprintf(emsg, emsglen,
547                      "strange.  UPNP-device said it's external IP-address is "
548                      "the %s \"%s\", but can't parse that with inet_pton(3).  "
549                      "Errorcode %d (%s)",
550                      safamily2string(extaddr.ss_family),
551                      str2vis(straddr, strlen(straddr), vbuf, sizeof(vbuf)),
552                      rc,
553                      strerror(errno));
554 
555             socks_set_responsevalue(&packet->res, UPNP_FAILURE);
556 
557             errno = errnoval;
558             return -1;
559          }
560 
561          sockaddr2sockshost(&extaddr, &packet->res.host);
562 
563          slog(LOG_NEGOTIATE,
564               "%s: upnp control point's external ip address is %s",
565               function,
566               str2vis(straddr, strlen(straddr), vbuf, sizeof(vbuf)));
567 
568          if (!ADDRISBOUND(&addr)) {
569             /*
570              * Address not bound.  Not bound is good enough for us if
571              * it it's good enough for caller, but we do need to tell the
572              * igd what address it should forward the connection to.
573              */
574              struct sockaddr_storage tmpaddr;
575 
576             switch (packet->gw.addr.atype) {
577                case SOCKS_ADDR_IFNAME: {
578                   struct sockaddr_storage mask;
579 
580                   if (ifname2sockaddr(packet->gw.addr.addr.ifname,
581                                       0,
582                                       &tmpaddr,
583                                       &mask) == NULL) {
584                      snprintf(emsg, emsglen,
585                               "ifname2sockaddr() could not resolve nic %s to "
586                               "an ipaddress",
587                               packet->gw.addr.addr.ifname);
588                      swarnx("%s: %s", function, emsg);
589 
590                      socks_set_responsevalue(&packet->res, UPNP_FAILURE);
591 
592                      errno = errnoval;
593                      return -1;
594                   }
595 
596                   /* just want the ipaddr.  Port number, etc., remains same. */
597                   SET_SOCKADDRADDR((&addr), GET_SOCKADDRADDR(&tmpaddr));
598                   break;
599                }
600 
601                case SOCKS_ADDR_URL: {
602                   socklen_t tmpaddrlen;
603                   char lemsg[256] /* local emsg */;
604                   int ss, gaierr;
605 
606                   if (urlstring2sockaddr(packet->gw.addr.addr.urlname,
607                                          &tmpaddr,
608                                          &gaierr,
609                                          lemsg,
610                                          sizeof(lemsg)) == NULL) {
611 
612                      log_resolvefailed(packet->gw.addr.addr.urlname,
613                                        EXTERNALIF,
614                                        gaierr);
615 
616                      snprintf(emsg, emsglen,
617                               "could not resolve url %s to an ipaddress: %s",
618                               str2vis(packet->gw.addr.addr.urlname,
619                                       strlen(packet->gw.addr.addr.urlname),
620                                       vbuf,
621                                       sizeof(vbuf)),
622 
623                               lemsg);
624 
625                      socks_set_responsevalue(&packet->res, UPNP_FAILURE);
626 
627                      errno = errnoval;
628                      return -1;
629                   }
630 
631                   /*
632                    * What we need to find out now is, what is the address
633                    * this host uses to communicate with the control point?
634                    * That is the address we need to tell it to forward
635                    * the connection to.  Could use getifa(), but we
636                    * are not guaranteed that always works as desired,
637                    * so do a regular connect(2) to know for sure.
638                    */
639 
640                   if ((ss = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
641                      snprintf(emsg, emsglen, "socket() failed: %s",
642                               strerror(errno));
643                      swarnx("%s: %s", function, emsg);
644 
645                      socks_set_responsevalue(&packet->res, UPNP_FAILURE);
646                      return -1;
647                   }
648 
649                   if (connect(ss, TOSA(&tmpaddr), salen(tmpaddr.ss_family))
650                   != 0) {
651                      snprintf(emsg, emsglen,
652                               "connect to proxy server %s failed: %s",
653                               sockaddr2string(&tmpaddr, NULL, 0),
654                               strerror(errno));
655                      swarnx("%s: %s", function, emsg);
656 
657                      socks_set_responsevalue(&packet->res, UPNP_FAILURE);
658                      close(ss);
659 
660 
661                      errno = errnoval;
662                      return -1;
663                   }
664 
665                   tmpaddrlen = sizeof(tmpaddr);
666                   if (getsockname(ss, TOSA(&tmpaddr), &tmpaddrlen) != 0) {
667                      snprintf(emsg, emsglen,
668                               "getsockname(ss) failed: %s", strerror(errno));
669 
670                      socks_set_responsevalue(&packet->res, UPNP_FAILURE);
671                      close(ss);
672 
673 
674                      errno = errnoval;
675                      return -1;
676                   }
677 
678                   close(ss);
679                   SET_SOCKADDRADDR((&addr), GET_SOCKADDRADDR(&tmpaddr));
680 
681                   break;
682                }
683 
684                default:
685                   SERRX(packet->gw.addr.atype);
686             }
687          }
688 
689          if (inet_ntop(addr.ss_family,
690                        GET_SOCKADDRADDR(&addr),
691                        straddr,
692                        sizeof(straddr)) == NULL) {
693             snprintf(emsg, emsglen,
694                      "inet_ntop(3) on address %s failed: %s",
695                      sockaddr2string(&addr, NULL, 0), strerror(errno));
696 
697             socks_set_responsevalue(&packet->res, UPNP_FAILURE);
698 
699             errno = errnoval;
700             return -1;
701          }
702 
703          len = sizeof(val);
704          if (getsockopt(s, SOL_SOCKET, SO_TYPE, &val, &len) != 0) {
705             snprintf(emsg, emsglen,
706                      "getsockopt(s) failed: %s", strerror(errno));
707             socks_set_responsevalue(&packet->res, UPNP_FAILURE);
708 
709             return -1;
710          }
711          switch (val) {
712             case SOCK_DGRAM:
713                snprintf(protocol, sizeof(protocol), PROTOCOL_UDPs);
714                break;
715 
716             case SOCK_STREAM:
717                snprintf(protocol, sizeof(protocol), PROTOCOL_TCPs);
718                break;
719 
720             default:
721                snprintf(emsg, emsglen, "unknown protocol type %d", val);
722                swarnx("%s: fd %d (socket s): %s", function, s, emsg);
723 
724                socks_set_responsevalue(&packet->res, UPNP_FAILURE);
725 
726                errno = errnoval;
727                return -1;
728          }
729 
730          snprintf(strport, sizeof(strport),
731                   "%d", ntohs(GET_SOCKADDRPORT(&addr)));
732 
733          snprintf(buf, sizeof(buf), "%s (%s/client v%s via libminiupnpc)",
734                  __progname, PRODUCT, VERSION);
735 
736          slog(LOG_NEGOTIATE,
737               "%s: trying to add %s port mapping for fd %d on upnp device at "
738               "%s: %s -> %s.%s",
739               function,
740               protocol,
741               s,
742               str2vis(gw->state.data.upnp.controlurl,
743                       strlen(gw->state.data.upnp.controlurl),
744                       vbuf,
745                       sizeof(vbuf)),
746               strport,
747               str2vis(straddr, strlen(straddr), vbuf, sizeof(vbuf)),
748               strport);
749 
750          str2upper(protocol); /* miniupnp-lib seems to fail if not. :-/ */
751          if ((rc = UPNP_AddPortMapping(gw->state.data.upnp.controlurl,
752                                        gw->state.data.upnp.servicetype,
753                                        strport,
754                                        strport,
755                                        straddr,
756                                        buf,
757                                        protocol,
758                                        NULL
759 #if HAVE_LIBMINIUPNP17
760                                        ,0
761 #endif /* HAVE_LIBMINIUPNP17 */
762                                        )) != UPNPCOMMAND_SUCCESS) {
763                snprintf(emsg, emsglen,
764                        "UPNP_AddPortMapping() failed: %s", strupnperror(rc));
765 
766                slog(LOG_NEGOTIATE, "%s: %s", function, emsg);
767 
768                socks_set_responsevalue(&packet->res, UPNP_FAILURE);
769 
770                errno = errnoval;
771                return -1;
772          }
773          else
774             slog(LOG_NEGOTIATE,
775                  "%s: addition of port mapping succeeded", function);
776 
777 #if SOCKS_CLIENT
778          if (!atexit_registered) {
779             struct sigaction oursig;
780             size_t i;
781             int signalv[] = { SIGINT };
782 
783             slog(LOG_DEBUG, "%s: registering cleanup function with atexit(3)",
784                  function);
785 
786             if (atexit(atexit_upnpcleanup) != 0) {
787                snprintf(emsg, emsglen,
788                         "atexit() failed to register upnp cleanup function: %s",
789                         strerror(errno));
790                swarnx("%s: %s", function, emsg);
791                break;
792             }
793 
794             atexit_registered = 1;
795 
796             for (i = 0; i < ELEMENTS(signalv); ++i) {
797                if (sigaction(signalv[i], NULL, &oldsig) != 0) {
798                   swarn("%s: sigaction(%d)", function, signalv[i]);
799                   continue;
800                }
801 
802                oursig = oldsig;
803                oursig.sa_handler = sighandler;
804                oursig.sa_flags  |= SA_SIGINFO;
805 
806                if (sigaction(signalv[i], &oursig, NULL) != 0) {
807                   swarn("%s: sigaction(%d)", function, signalv[i]);
808                   break;
809                }
810             }
811          }
812 #endif /* SOCKS_CLIENT */
813          break;
814       }
815 
816       default:
817          SERRX(packet->req.command);
818    }
819 
820    socks_set_responsevalue(&packet->res, UPNP_SUCCESS);
821 
822 #endif /* HAVE_LIBMINIUPNP */
823 
824    return 0;
825 }
826 
827 #if HAVE_LIBMINIUPNP
828 
829 #if SOCKS_CLIENT
830 static void
sighandler(sig)831 sighandler(sig)
832    int sig;
833 {
834    const char *function = "sighandler()";
835 
836    slog(LOG_DEBUG, "%s", function);
837 
838    upnpcleanup(-1);
839 
840    /* reinstall original signal handler. */
841    if (sigaction(SIGINT, &oldsig, NULL) != 0)
842       serr("%s: restoring old signal handler failed", function);
843 
844    raise(SIGINT);
845 }
846 #endif /* SOCKS_CLIENT */
847 
848 #if SOCKS_CLIENT
849 void
upnpcleanup(s)850 upnpcleanup(s)
851    const int s;
852 {
853    const char *function = "upnpcleanup()";
854    socksfd_t socksfd;
855    int rc, current, last;
856 
857    slog(LOG_DEBUG, "%s: fd %d", function, s);
858 
859    if (s == -1) {
860       current = 0;
861       last    = getmaxofiles(softlimit) - 1;
862    }
863    else {
864       current  = s;
865       last     = s;
866    }
867 
868    for (; current <= last; ++current) {
869       static int deleting;
870       char port[sizeof("65535")], protocol[sizeof("TCP")];
871 
872       if (deleting == current)
873          continue;
874 
875       if (socks_getaddr(current, &socksfd, 0 /* XXX */) == NULL)
876          continue;
877 
878       if (socksfd.state.version != PROXY_UPNP)
879          continue;
880 
881       slog(LOG_NEGOTIATE,
882            "%s: fd %d has upnp session set up for command %s, "
883            "accept pending: %s",
884            function,
885            current,
886            command2string(socksfd.state.command),
887            socksfd.state.acceptpending ? "yes" : "no");
888 
889       if (socksfd.state.command != SOCKS_BIND)
890          continue;
891 
892       /*
893        * Is this the socket we listened on?  Or just a client we accept(2)'ed?
894        * The port mapping is just created for the first case.
895        */
896       if (!socksfd.state.acceptpending)
897          continue; /* just a client we accepted. */
898 
899       snprintf(port, sizeof(port),
900                "%d", ntohs(GET_SOCKADDRPORT(&socksfd.remote)));
901 
902       if (socksfd.state.protocol.tcp)
903          snprintf(protocol, sizeof(protocol), PROTOCOL_TCPs);
904       else if (socksfd.state.protocol.udp)
905          snprintf(protocol, sizeof(protocol), PROTOCOL_UDPs);
906       else {
907          SWARNX(0);
908          continue;
909       }
910 
911       slog(LOG_NEGOTIATE, "%s: deleting port mapping for external %s port %s",
912            function, protocol, port);
913 
914       str2upper(protocol);
915 
916       /*
917        * needed to avoid recursion, as the below delete-call might
918        * very well end up calling us again, which makes us try
919        * to delete the port mapping twice.
920        */
921       deleting = current;
922 
923       rc = UPNP_DeletePortMapping(socksfd.route->gw.state.data.upnp.controlurl,
924                                   socksfd.route->gw.state.data.upnp.servicetype,
925                                   port,
926                                   protocol,
927                                   NULL);
928 
929       if (rc != UPNPCOMMAND_SUCCESS)
930          swarnx("%s: UPNP_DeletePortMapping(%s, %s) failed: %s",
931                 function, port, protocol, strupnperror(rc));
932       else
933          slog(LOG_NEGOTIATE, "%s: deleted port mapping for external %s port %s",
934               function, protocol, port);
935 
936       deleting = -1;
937    }
938 }
939 
940 static void
atexit_upnpcleanup(void)941 atexit_upnpcleanup(void)
942 {
943    const char *function = "atexit_upnpcleanup()";
944 
945    slog(LOG_DEBUG, "%s", function);
946 
947    upnpcleanup(-1);
948 }
949 #endif /* SOCKS_CLIENT */
950 #else /* !HAVE_LIBMINIUPNP */
951 void
upnpcleanup(s)952 upnpcleanup(s)
953    const int s;
954 {
955         return;
956 }
957 #endif /* !HAVE_LIBMINIUPNP */
958