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