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