1 /* Copyright  (C) 2016-2020 The RetroArch team
2  *
3  * ---------------------------------------------------------------------------------------
4  * The following license statement only applies to this file (net_natt.c).
5  * ---------------------------------------------------------------------------------------
6  *
7  * Permission is hereby granted, free of charge,
8  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
11  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
16  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
19  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/types.h>
27 
28 #include <net/net_compat.h>
29 #include <net/net_ifinfo.h>
30 #include <retro_miscellaneous.h>
31 
32 #include <string/stdstring.h>
33 #include <net/net_natt.h>
34 
35 #if HAVE_MINIUPNPC
36 #include <miniupnpc/miniwget.h>
37 #include <miniupnpc/miniupnpc.h>
38 #include <miniupnpc/upnpcommands.h>
39 
40 #if MINIUPNPC_API_VERSION < 16
41 #undef HAVE_MINIUPNPC
42 #endif
43 #endif
44 
45 #if HAVE_MINIUPNPC
46 static struct UPNPUrls urls;
47 static struct IGDdatas data;
48 #endif
49 
natt_init(void)50 void natt_init(void)
51 {
52 #ifndef HAVE_SOCKET_LEGACY
53 #if HAVE_MINIUPNPC
54    struct UPNPDev * devlist;
55    struct UPNPDev * dev;
56    char * descXML;
57    int descXMLsize = 0;
58    int upnperror = 0;
59    memset(&urls, 0, sizeof(struct UPNPUrls));
60    memset(&data, 0, sizeof(struct IGDdatas));
61    devlist = upnpDiscover(2000, NULL, NULL, 0, 0, 2, &upnperror);
62    if (devlist)
63    {
64       dev = devlist;
65       while (dev)
66       {
67          if (strstr (dev->st, "InternetGatewayDevice"))
68             break;
69          dev = dev->pNext;
70       }
71       if (!dev)
72          dev = devlist;
73 
74       descXML = (char *) miniwget(dev->descURL, &descXMLsize, 0, NULL);
75       if (descXML)
76       {
77          parserootdesc(descXML, descXMLsize, &data);
78          free (descXML);
79          descXML = 0;
80          GetUPNPUrls (&urls, &data, dev->descURL, 0);
81       }
82       freeUPNPDevlist(devlist);
83    }
84 #endif
85 #endif
86 }
87 
natt_new(struct natt_status * status)88 bool natt_new(struct natt_status *status)
89 {
90    memset(status, 0, sizeof(struct natt_status));
91    return true;
92 }
93 
natt_free(struct natt_status * status)94 void natt_free(struct natt_status *status) { }
95 
natt_open_port(struct natt_status * status,struct sockaddr * addr,socklen_t addrlen,enum socket_protocol proto)96 static bool natt_open_port(struct natt_status *status,
97       struct sockaddr *addr, socklen_t addrlen, enum socket_protocol proto)
98 {
99 #ifndef HAVE_SOCKET_LEGACY
100 #if HAVE_MINIUPNPC
101    int r;
102    char host[PATH_MAX_LENGTH], ext_host[PATH_MAX_LENGTH],
103         port_str[6], ext_port_str[6];
104    struct addrinfo hints         = {0};
105    const char *proto_str         = NULL;
106    struct addrinfo *ext_addrinfo = NULL;
107 
108    /* if NAT traversal is uninitialized or unavailable, oh well */
109    if (!urls.controlURL || !urls.controlURL[0])
110       return false;
111 
112    /* figure out the internal info */
113    if (getnameinfo(addr, addrlen, host, PATH_MAX_LENGTH,
114             port_str, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0)
115       return false;
116 
117    proto_str = (proto == SOCKET_PROTOCOL_UDP) ? "UDP" : "TCP";
118 
119    /* add the port mapping */
120    r = UPNP_AddAnyPortMapping(urls.controlURL,
121          data.first.servicetype, port_str,
122          port_str, host, "retroarch",
123          proto_str, NULL, "3600", ext_port_str);
124 
125    if (r != 0)
126    {
127       /* try the older AddPortMapping */
128       memcpy(ext_port_str, port_str, 6);
129       r = UPNP_AddPortMapping(urls.controlURL,
130             data.first.servicetype, port_str,
131             port_str, host, "retroarch",
132             proto_str, NULL, "3600");
133    }
134    if (r != 0)
135       return false;
136 
137    /* get the external IP */
138    r = UPNP_GetExternalIPAddress(urls.controlURL,
139          data.first.servicetype, ext_host);
140    if (r != 0)
141       return false;
142 
143    /* update the status */
144    if (getaddrinfo_retro(ext_host,
145             ext_port_str, &hints, &ext_addrinfo) != 0)
146       return false;
147 
148    if (ext_addrinfo->ai_family == AF_INET &&
149        ext_addrinfo->ai_addrlen >= sizeof(struct sockaddr_in))
150    {
151       status->have_inet4     = true;
152       status->ext_inet4_addr = *((struct sockaddr_in *)
153             ext_addrinfo->ai_addr);
154    }
155 #if defined(AF_INET6) && !defined(HAVE_SOCKET_LEGACY)
156    else if (ext_addrinfo->ai_family == AF_INET6 &&
157             ext_addrinfo->ai_addrlen >= sizeof(struct sockaddr_in6))
158    {
159       status->have_inet6     = true;
160       status->ext_inet6_addr = *((struct sockaddr_in6 *)
161             ext_addrinfo->ai_addr);
162    }
163 #endif
164    else
165    {
166       freeaddrinfo_retro(ext_addrinfo);
167       return false;
168    }
169 
170    freeaddrinfo_retro(ext_addrinfo);
171    return true;
172 
173 #else
174    return false;
175 #endif
176 #else
177    return false;
178 #endif
179 }
180 
natt_open_port_any(struct natt_status * status,uint16_t port,enum socket_protocol proto)181 bool natt_open_port_any(struct natt_status *status,
182       uint16_t port, enum socket_protocol proto)
183 {
184 #if !defined(HAVE_SOCKET_LEGACY) && (!defined(SWITCH) || defined(SWITCH) && defined(HAVE_LIBNX))
185    size_t i;
186    char port_str[6];
187    struct net_ifinfo list;
188    struct addrinfo hints = {0}, *addr;
189    bool ret              = false;
190 
191    snprintf(port_str, sizeof(port_str), "%hu", port);
192 
193    /* get our interfaces */
194    if (!net_ifinfo_new(&list))
195       return false;
196 
197    /* loop through them */
198    for (i = 0; i < list.size; i++)
199    {
200       struct net_ifinfo_entry *entry = list.entries + i;
201 
202       /* ignore localhost */
203       if (  string_is_equal(entry->host, "127.0.0.1") ||
204             string_is_equal(entry->host, "::1"))
205          continue;
206 
207       /* make a request for this host */
208       if (getaddrinfo_retro(entry->host, port_str, &hints, &addr) == 0)
209       {
210          ret = natt_open_port(status, addr->ai_addr,
211                addr->ai_addrlen, proto) || ret;
212          freeaddrinfo_retro(addr);
213       }
214    }
215 
216    net_ifinfo_free(&list);
217 
218    return ret;
219 
220 #else
221    return false;
222 #endif
223 }
224 
natt_read(struct natt_status * status)225 bool natt_read(struct natt_status *status)
226 {
227    /* MiniUPNPC is always synchronous, so there's nothing to read here.
228     * Reserved for future backends. */
229    return false;
230 }
231 
232 #if 0
233 /* If we want to remove redirects in the future, this is a
234  * sample of how to do that. */
235 
236 void upnp_rem_redir (int port)
237 {
238    int t;
239    char port_str[16];
240 
241    printf("TB : upnp_rem_redir (%d)\n", port);
242 
243    if(urls.controlURL[0] == '\0')
244    {
245       printf("TB : the init was not done !\n");
246       return;
247    }
248 
249    snprintf(port_str, sizeof(port_str), "%d", port);
250    UPNP_DeletePortMapping(urls.controlURL,
251          data.first.servicetype, port_str, "TCP", NULL);
252 }
253 #endif
254