1 /*
2  * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2004, 2008, 2009, 2010, 2011,
3  *               2012, 2013
4  *      Inferno Nettverk A/S, Norway.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. The above copyright notice, this list of conditions and the following
10  *    disclaimer must appear in all copies of the software, derivative works
11  *    or modified versions, and any portions thereof, aswell as in all
12  *    supporting documentation.
13  * 2. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by
16  *      Inferno Nettverk A/S, Norway.
17  * 3. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  *
31  * Inferno Nettverk A/S requests users of this software to return to
32  *
33  *  Software Distribution Coordinator  or  sdc@inet.no
34  *  Inferno Nettverk A/S
35  *  Oslo Research Park
36  *  Gaustadall�en 21
37  *  NO-0349 Oslo
38  *  Norway
39  *
40  * any improvements or extensions that they make and grant Inferno Nettverk A/S
41  * the rights to redistribute these changes.
42  *
43  */
44 
45 #include "common.h"
46 
47 #include "upnp.h"
48 
49 static const char rcsid[] =
50 "$Id: Rgetsockname.c,v 1.93 2013/10/27 15:24:42 karls Exp $";
51 
52 int
Rgetsockname(s,name,namelen)53 Rgetsockname(s, name, namelen)
54    int s;
55    struct sockaddr *name;
56    socklen_t *namelen;
57 {
58    const char *function = "Rgetsockname()";
59    socksfd_t socksfd;
60    struct sockaddr_storage addr;
61 
62    clientinit();
63 
64    slog(LOG_DEBUG, "%s, fd %d", function, s);
65 
66    if (!socks_addrisours(s, &socksfd, 1)) {
67       socks_rmaddr(s, 1);
68       return getsockname(s, name, namelen);
69    }
70 
71    if (socksfd.state.version == PROXY_UPNP) {
72 #if HAVE_LIBMINIUPNP
73       if (ADDRISBOUND(&socksfd.remote))
74          addr = socksfd.remote; /* already have it. */
75       else {
76          proxystate_t *state = &socksfd.route->gw.state.data;
77          socksfd_t *p;
78          char straddr[INET_ADDRSTRLEN], emsg[1024];
79          int rc;
80 
81          p = socks_getaddr(s, &socksfd, 1);
82          SASSERTX(p != NULL);
83 
84          if (socks_initupnp(&socksfd.route->gw, emsg, sizeof(emsg)) != 0) {
85             swarnx("%s: socks_initupnp() failed to init upnp device: %s",
86                    function, emsg);
87 
88             errno = EOPNOTSUPP;
89             return -1;
90          }
91 
92          if ((rc = UPNP_GetExternalIPAddress(state->upnp.controlurl,
93                                              state->upnp.servicetype,
94                                              straddr)) != UPNPCOMMAND_SUCCESS) {
95             swarnx("%s: failed to get external ip address of upnp device: %s",
96                    function, strupnperror(rc));
97 
98             errno = EOPNOTSUPP;
99             return -1;
100          }
101 
102          slog(LOG_INFO, "%s: upnp control point's external ip address is %s",
103               function, straddr);
104 
105          if (socks_inet_pton(AF_INET,
106                              straddr,
107                              &TOIN(&socksfd.remote)->sin_addr,
108                              NULL) == 1)
109             SET_SOCKADDR(TOSS(&socksfd.remote), AF_INET);
110          else if (socks_inet_pton(AF_INET6,
111                                   straddr,
112                                   &TOIN(&socksfd.remote)->sin_addr,
113                                   &TOIN6(&socksfd.remote)->sin6_scope_id) == 1)
114             SET_SOCKADDR(TOSS(&socksfd.remote), AF_INET6);
115          else {
116             swarn("%s: could not convert string %s to network address",
117                   function, straddr);
118 
119             return -1;
120          }
121 
122          addr = socksfd.remote;
123          socks_addaddr(s, &socksfd, 1);
124       }
125 #else
126      SERRX(socksfd.state.version);
127 #endif /* HAVE_LIBMINIUPNP */
128    }
129    else {
130       switch (socksfd.state.command) {
131          case SOCKS_CONNECT: {
132             sigset_t set, oset;
133 
134             /* for non-blocking connect, we get a SIGIO upon completion. */
135             (void)sigemptyset(&set);
136             (void)sigaddset(&set, SIGIO);
137             if (sigprocmask(SIG_BLOCK, &set, &oset) != 0) {
138                swarn("%s: sigprocmask()", function);
139                return -1;
140             }
141 
142             if (socksfd.state.inprogress) { /* non-blocking connect. */
143                /*
144                 * this is bad.  We don't know what address the socks server
145                 * will use on our behalf yet.  Lets wait for a SIGIO
146                 * and then retry, unless client is blocking that signal,
147                 * then we can only hope the client will retry on ENOBUFS,
148                 * but we are probably screwed anyway.
149                */
150                if (sigismember(&oset, SIGIO)) {
151                   swarnx("%s: SIGIO is being blocked by client", function);
152 
153                   if (sigprocmask(SIG_BLOCK, &oset, NULL) != 0) {
154                      swarn("%s: sigprocmask()", function);
155                      return -1;
156                   }
157 
158                   errno = ENOBUFS;
159                   return -1;
160                }
161 
162 
163                slog(LOG_DEBUG, "%s: waiting for signal from child", function);
164                sigsuspend(&oset); /* wait for sigchld. */
165 
166                if (sigprocmask(SIG_BLOCK, &oset, NULL) != 0) {
167                   swarn("%s: sigprocmask()", function);
168                   return -1;
169                }
170 
171                return Rgetsockname(s, name, namelen);
172             }
173 
174             if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0)
175                swarn("%s: sigprocmask()", function);
176             addr = socksfd.remote;
177             break;
178          }
179 
180          case SOCKS_BIND:
181             addr = socksfd.remote;
182             break;
183 
184          case SOCKS_UDPASSOCIATE:
185             swarnx("%s: getsockname() on udp sockets is not supported by the "
186             "socks protocol, trying to fake it.", function);
187 
188             /*
189              * some clients might call this for no good reason, try to
190              * help them by returning a invalid address; if they are
191              * going to use it for anything, they will fail later though.
192              */
193 
194             addr = socksfd.remote;
195             SET_SOCKADDR(&addr, AF_INET);
196             TOIN(&addr)->sin_addr.s_addr = htonl(INADDR_ANY);
197             TOIN(&addr)->sin_port        = htons(0);
198             break;
199 
200          default:
201             SERRX(socksfd.state.command);
202       }
203    }
204 
205    *namelen = MIN(*namelen, salen(addr.ss_family));
206    sockaddrcpy(TOSS(name), &addr, (size_t)*namelen);
207 
208    return 0;
209 }
210