1 /* $Id: upnpc.c,v 1.126 2020/11/09 19:38:06 nanard Exp $ */
2 /* Project : miniupnp
3  * Author : Thomas Bernard
4  * Copyright (c) 2005-2020 Thomas Bernard
5  * This software is subject to the conditions detailed in the
6  * LICENCE file provided in this distribution. */
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <time.h>
12 #ifdef _WIN32
13 #include <winsock2.h>
14 #include "win32_snprintf.h"
15 #else
16 /* for IPPROTO_TCP / IPPROTO_UDP */
17 #include <netinet/in.h>
18 #endif
19 #include <ctype.h>
20 #include "miniwget.h"
21 #include "miniupnpc.h"
22 #include "upnpcommands.h"
23 #include "portlistingparse.h"
24 #include "upnperrors.h"
25 #include "miniupnpcstrings.h"
26 
27 /* protofix() checks if protocol is "UDP" or "TCP"
28  * returns NULL if not */
protofix(const char * proto)29 const char * protofix(const char * proto)
30 {
31 	static const char proto_tcp[4] = { 'T', 'C', 'P', 0};
32 	static const char proto_udp[4] = { 'U', 'D', 'P', 0};
33 	int i, b;
34 	for(i=0, b=1; i<4; i++)
35 		b = b && (   (proto[i] == proto_tcp[i])
36 		          || (proto[i] == (proto_tcp[i] | 32)) );
37 	if(b)
38 		return proto_tcp;
39 	for(i=0, b=1; i<4; i++)
40 		b = b && (   (proto[i] == proto_udp[i])
41 		          || (proto[i] == (proto_udp[i] | 32)) );
42 	if(b)
43 		return proto_udp;
44 	return 0;
45 }
46 
47 /* is_int() checks if parameter is an integer or not
48  * 1 for integer
49  * 0 for not an integer */
is_int(char const * s)50 int is_int(char const* s)
51 {
52 	if(s == NULL)
53 		return 0;
54 	while(*s) {
55 		/* #define isdigit(c) ((c) >= '0' && (c) <= '9') */
56 		if(!isdigit(*s))
57 			return 0;
58 		s++;
59 	}
60 	return 1;
61 }
62 
DisplayInfos(struct UPNPUrls * urls,struct IGDdatas * data)63 static void DisplayInfos(struct UPNPUrls * urls,
64                          struct IGDdatas * data)
65 {
66 	char externalIPAddress[40];
67 	char connectionType[64];
68 	char status[64];
69 	char lastconnerr[64];
70 	unsigned int uptime = 0;
71 	unsigned int brUp, brDown;
72 	time_t timenow, timestarted;
73 	int r;
74 	if(UPNP_GetConnectionTypeInfo(urls->controlURL,
75 	                              data->first.servicetype,
76 	                              connectionType) != UPNPCOMMAND_SUCCESS)
77 		printf("GetConnectionTypeInfo failed.\n");
78 	else
79 		printf("Connection Type : %s\n", connectionType);
80 	if(UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
81 	                      status, &uptime, lastconnerr) != UPNPCOMMAND_SUCCESS)
82 		printf("GetStatusInfo failed.\n");
83 	else
84 		printf("Status : %s, uptime=%us, LastConnectionError : %s\n",
85 		       status, uptime, lastconnerr);
86 	if(uptime > 0) {
87 		timenow = time(NULL);
88 		timestarted = timenow - uptime;
89 		printf("  Time started : %s", ctime(&timestarted));
90 	}
91 	if(UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype,
92 	                                &brDown, &brUp) != UPNPCOMMAND_SUCCESS) {
93 		printf("GetLinkLayerMaxBitRates failed.\n");
94 	} else {
95 		printf("MaxBitRateDown : %u bps", brDown);
96 		if(brDown >= 1000000) {
97 			printf(" (%u.%u Mbps)", brDown / 1000000, (brDown / 100000) % 10);
98 		} else if(brDown >= 1000) {
99 			printf(" (%u Kbps)", brDown / 1000);
100 		}
101 		printf("   MaxBitRateUp %u bps", brUp);
102 		if(brUp >= 1000000) {
103 			printf(" (%u.%u Mbps)", brUp / 1000000, (brUp / 100000) % 10);
104 		} else if(brUp >= 1000) {
105 			printf(" (%u Kbps)", brUp / 1000);
106 		}
107 		printf("\n");
108 	}
109 	r = UPNP_GetExternalIPAddress(urls->controlURL,
110 	                          data->first.servicetype,
111 							  externalIPAddress);
112 	if(r != UPNPCOMMAND_SUCCESS) {
113 		printf("GetExternalIPAddress failed. (errorcode=%d)\n", r);
114 	} else if(!externalIPAddress[0]) {
115 		printf("GetExternalIPAddress failed. (empty string)\n");
116 	} else {
117 		printf("ExternalIPAddress = %s\n", externalIPAddress);
118 	}
119 }
120 
GetConnectionStatus(struct UPNPUrls * urls,struct IGDdatas * data)121 static void GetConnectionStatus(struct UPNPUrls * urls,
122                                struct IGDdatas * data)
123 {
124 	unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
125 	DisplayInfos(urls, data);
126 	bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
127 	bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
128 	packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
129 	packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
130 	printf("Bytes:   Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
131 	printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
132 }
133 
ListRedirections(struct UPNPUrls * urls,struct IGDdatas * data)134 static void ListRedirections(struct UPNPUrls * urls,
135                              struct IGDdatas * data)
136 {
137 	int r;
138 	int i = 0;
139 	char index[6];
140 	char intClient[40];
141 	char intPort[6];
142 	char extPort[6];
143 	char protocol[4];
144 	char desc[80];
145 	char enabled[6];
146 	char rHost[64];
147 	char duration[16];
148 	/*unsigned int num=0;
149 	UPNP_GetPortMappingNumberOfEntries(urls->controlURL, data->servicetype, &num);
150 	printf("PortMappingNumberOfEntries : %u\n", num);*/
151 	printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n");
152 	do {
153 		snprintf(index, 6, "%d", i);
154 		rHost[0] = '\0'; enabled[0] = '\0';
155 		duration[0] = '\0'; desc[0] = '\0';
156 		extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0';
157 		r = UPNP_GetGenericPortMappingEntry(urls->controlURL,
158 		                               data->first.servicetype,
159 		                               index,
160 		                               extPort, intClient, intPort,
161 									   protocol, desc, enabled,
162 									   rHost, duration);
163 		if(r==0)
164 		/*
165 			printf("%02d - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n"
166 			       "     desc='%s' rHost='%s'\n",
167 			       i, protocol, extPort, intClient, intPort,
168 				   enabled, duration,
169 				   desc, rHost);
170 				   */
171 			printf("%2d %s %5s->%s:%-5s '%s' '%s' %s\n",
172 			       i, protocol, extPort, intClient, intPort,
173 			       desc, rHost, duration);
174 		else
175 			printf("GetGenericPortMappingEntry() returned %d (%s)\n",
176 			       r, strupnperror(r));
177 		i++;
178 	} while(r == 0 && i < 65536);
179 }
180 
NewListRedirections(struct UPNPUrls * urls,struct IGDdatas * data)181 static void NewListRedirections(struct UPNPUrls * urls,
182                                 struct IGDdatas * data)
183 {
184 	int r;
185 	int i = 0;
186 	struct PortMappingParserData pdata;
187 	struct PortMapping * pm;
188 
189 	memset(&pdata, 0, sizeof(struct PortMappingParserData));
190 	r = UPNP_GetListOfPortMappings(urls->controlURL,
191                                    data->first.servicetype,
192 	                               "0",
193 	                               "65535",
194 	                               "TCP",
195 	                               "1000",
196 	                               &pdata);
197 	if(r == UPNPCOMMAND_SUCCESS)
198 	{
199 		printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n");
200 		for(pm = pdata.l_head; pm != NULL; pm = pm->l_next)
201 		{
202 			printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
203 			       i, pm->protocol, pm->externalPort, pm->internalClient,
204 			       pm->internalPort,
205 			       pm->description, pm->remoteHost,
206 			       (unsigned)pm->leaseTime);
207 			i++;
208 		}
209 		FreePortListing(&pdata);
210 	}
211 	else
212 	{
213 		printf("GetListOfPortMappings() returned %d (%s)\n",
214 		       r, strupnperror(r));
215 	}
216 	r = UPNP_GetListOfPortMappings(urls->controlURL,
217                                    data->first.servicetype,
218 	                               "0",
219 	                               "65535",
220 	                               "UDP",
221 	                               "1000",
222 	                               &pdata);
223 	if(r == UPNPCOMMAND_SUCCESS)
224 	{
225 		for(pm = pdata.l_head; pm != NULL; pm = pm->l_next)
226 		{
227 			printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n",
228 			       i, pm->protocol, pm->externalPort, pm->internalClient,
229 			       pm->internalPort,
230 			       pm->description, pm->remoteHost,
231 			       (unsigned)pm->leaseTime);
232 			i++;
233 		}
234 		FreePortListing(&pdata);
235 	}
236 	else
237 	{
238 		printf("GetListOfPortMappings() returned %d (%s)\n",
239 		       r, strupnperror(r));
240 	}
241 }
242 
243 /* Test function
244  * 1 - get connection type
245  * 2 - get extenal ip address
246  * 3 - Add port mapping
247  * 4 - get this port mapping from the IGD */
SetRedirectAndTest(struct UPNPUrls * urls,struct IGDdatas * data,const char * iaddr,const char * iport,const char * eport,const char * proto,const char * leaseDuration,const char * remoteHost,const char * description,int addAny)248 static int SetRedirectAndTest(struct UPNPUrls * urls,
249 			       struct IGDdatas * data,
250 			       const char * iaddr,
251 			       const char * iport,
252 			       const char * eport,
253 			       const char * proto,
254 			       const char * leaseDuration,
255 			       const char * remoteHost,
256 			       const char * description,
257 			       int addAny)
258 {
259 	char externalIPAddress[40];
260 	char intClient[40];
261 	char intPort[6];
262 	char reservedPort[6];
263 	char duration[16];
264 	int r;
265 
266 	if(!iaddr || !iport || !eport || !proto)
267 	{
268 		fprintf(stderr, "Wrong arguments\n");
269 		return -1;
270 	}
271 	proto = protofix(proto);
272 	if(!proto)
273 	{
274 		fprintf(stderr, "invalid protocol\n");
275 		return -1;
276 	}
277 
278 	r = UPNP_GetExternalIPAddress(urls->controlURL,
279 				      data->first.servicetype,
280 				      externalIPAddress);
281 	if(r!=UPNPCOMMAND_SUCCESS)
282 		printf("GetExternalIPAddress failed.\n");
283 	else
284 		printf("ExternalIPAddress = %s\n", externalIPAddress);
285 
286 	if (addAny) {
287 		r = UPNP_AddAnyPortMapping(urls->controlURL, data->first.servicetype,
288 					   eport, iport, iaddr, description,
289 					   proto, remoteHost, leaseDuration, reservedPort);
290 		if(r==UPNPCOMMAND_SUCCESS)
291 			eport = reservedPort;
292 		else
293 			printf("AddAnyPortMapping(%s, %s, %s) failed with code %d (%s)\n",
294 			       eport, iport, iaddr, r, strupnperror(r));
295 	} else {
296 		r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype,
297 					eport, iport, iaddr, description,
298 					proto, remoteHost, leaseDuration);
299 		if(r!=UPNPCOMMAND_SUCCESS) {
300 			printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n",
301 			       eport, iport, iaddr, r, strupnperror(r));
302 			return -2;
303 	}
304 	}
305 
306 	r = UPNP_GetSpecificPortMappingEntry(urls->controlURL,
307 					     data->first.servicetype,
308 					     eport, proto, remoteHost,
309 					     intClient, intPort, NULL/*desc*/,
310 					     NULL/*enabled*/, duration);
311 	if(r!=UPNPCOMMAND_SUCCESS) {
312 		printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n",
313 		       r, strupnperror(r));
314 		return -2;
315 	} else {
316 		printf("InternalIP:Port = %s:%s\n", intClient, intPort);
317 		printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n",
318 		       externalIPAddress, eport, proto, intClient, intPort, duration);
319 	}
320 	return 0;
321 }
322 
323 static int
RemoveRedirect(struct UPNPUrls * urls,struct IGDdatas * data,const char * eport,const char * proto,const char * remoteHost)324 RemoveRedirect(struct UPNPUrls * urls,
325                struct IGDdatas * data,
326                const char * eport,
327                const char * proto,
328                const char * remoteHost)
329 {
330 	int r;
331 	if(!proto || !eport)
332 	{
333 		fprintf(stderr, "invalid arguments\n");
334 		return -1;
335 	}
336 	proto = protofix(proto);
337 	if(!proto)
338 	{
339 		fprintf(stderr, "protocol invalid\n");
340 		return -1;
341 	}
342 	r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, remoteHost);
343 	if(r!=UPNPCOMMAND_SUCCESS) {
344 		printf("UPNP_DeletePortMapping() failed with code : %d\n", r);
345 		return -2;
346 	}else {
347 		printf("UPNP_DeletePortMapping() returned : %d\n", r);
348 	}
349 	return 0;
350 }
351 
352 static int
RemoveRedirectRange(struct UPNPUrls * urls,struct IGDdatas * data,const char * ePortStart,char const * ePortEnd,const char * proto,const char * manage)353 RemoveRedirectRange(struct UPNPUrls * urls,
354 		    struct IGDdatas * data,
355 		    const char * ePortStart, char const * ePortEnd,
356 		    const char * proto, const char * manage)
357 {
358 	int r;
359 
360 	if (!manage)
361 		manage = "0";
362 
363 	if(!proto || !ePortStart || !ePortEnd)
364 	{
365 		fprintf(stderr, "invalid arguments\n");
366 		return -1;
367 	}
368 	proto = protofix(proto);
369 	if(!proto)
370 	{
371 		fprintf(stderr, "protocol invalid\n");
372 		return -1;
373 	}
374 	r = UPNP_DeletePortMappingRange(urls->controlURL, data->first.servicetype, ePortStart, ePortEnd, proto, manage);
375 	if(r!=UPNPCOMMAND_SUCCESS) {
376 		printf("UPNP_DeletePortMappingRange() failed with code : %d\n", r);
377 		return -2;
378 	}else {
379 		printf("UPNP_DeletePortMappingRange() returned : %d\n", r);
380 	}
381 	return 0;
382 }
383 
384 /* IGD:2, functions for service WANIPv6FirewallControl:1 */
GetFirewallStatus(struct UPNPUrls * urls,struct IGDdatas * data)385 static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data)
386 {
387 	unsigned int bytessent, bytesreceived, packetsreceived, packetssent;
388 	int firewallEnabled = 0, inboundPinholeAllowed = 0;
389 
390 	UPNP_GetFirewallStatus(urls->controlURL_6FC, data->IPv6FC.servicetype, &firewallEnabled, &inboundPinholeAllowed);
391 	printf("FirewallEnabled: %d & Inbound Pinhole Allowed: %d\n", firewallEnabled, inboundPinholeAllowed);
392 	printf("GetFirewallStatus:\n   Firewall Enabled: %s\n   Inbound Pinhole Allowed: %s\n", (firewallEnabled)? "Yes":"No", (inboundPinholeAllowed)? "Yes":"No");
393 
394 	bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype);
395 	bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype);
396 	packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype);
397 	packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype);
398 	printf("Bytes:   Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived);
399 	printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived);
400 }
401 
402 /* Test function
403  * 1 - Add pinhole
404  * 2 - Check if pinhole is working from the IGD side */
SetPinholeAndTest(struct UPNPUrls * urls,struct IGDdatas * data,const char * remoteaddr,const char * eport,const char * intaddr,const char * iport,const char * proto,const char * lease_time)405 static void SetPinholeAndTest(struct UPNPUrls * urls, struct IGDdatas * data,
406 					const char * remoteaddr, const char * eport,
407 					const char * intaddr, const char * iport,
408 					const char * proto, const char * lease_time)
409 {
410 	char uniqueID[8];
411 	/*int isWorking = 0;*/
412 	int r;
413 	char proto_tmp[8];
414 
415 	if(!intaddr || !remoteaddr || !iport || !eport || !proto || !lease_time)
416 	{
417 		fprintf(stderr, "Wrong arguments\n");
418 		return;
419 	}
420 	if(atoi(proto) == 0)
421 	{
422 		const char * protocol;
423 		protocol = protofix(proto);
424 		if(protocol && (strcmp("TCP", protocol) == 0))
425 		{
426 			snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_TCP);
427 			proto = proto_tmp;
428 		}
429 		else if(protocol && (strcmp("UDP", protocol) == 0))
430 		{
431 			snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_UDP);
432 			proto = proto_tmp;
433 		}
434 		else
435 		{
436 			fprintf(stderr, "invalid protocol\n");
437 			return;
438 		}
439 	}
440 	r = UPNP_AddPinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, lease_time, uniqueID);
441 	if(r!=UPNPCOMMAND_SUCCESS)
442 		printf("AddPinhole([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
443 		       remoteaddr, eport, intaddr, iport, r, strupnperror(r));
444 	else
445 	{
446 		printf("AddPinhole: ([%s]:%s -> [%s]:%s) / Pinhole ID = %s\n",
447 		       remoteaddr, eport, intaddr, iport, uniqueID);
448 		/*r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->servicetype_6FC, uniqueID, &isWorking);
449 		if(r!=UPNPCOMMAND_SUCCESS)
450 			printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
451 		printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");*/
452 	}
453 }
454 
455 /* Test function
456  * 1 - Check if pinhole is working from the IGD side
457  * 2 - Update pinhole */
GetPinholeAndUpdate(struct UPNPUrls * urls,struct IGDdatas * data,const char * uniqueID,const char * lease_time)458 static void GetPinholeAndUpdate(struct UPNPUrls * urls, struct IGDdatas * data,
459 					const char * uniqueID, const char * lease_time)
460 {
461 	int isWorking = 0;
462 	int r;
463 
464 	if(!uniqueID || !lease_time)
465 	{
466 		fprintf(stderr, "Wrong arguments\n");
467 		return;
468 	}
469 	r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
470 	printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
471 	if(r!=UPNPCOMMAND_SUCCESS)
472 		printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
473 	if(isWorking || r==709)
474 	{
475 		r = UPNP_UpdatePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, lease_time);
476 		printf("UpdatePinhole: Pinhole ID = %s with Lease Time: %s\n", uniqueID, lease_time);
477 		if(r!=UPNPCOMMAND_SUCCESS)
478 			printf("UpdatePinhole: ID (%s) failed with code %d (%s)\n", uniqueID, r, strupnperror(r));
479 	}
480 }
481 
482 /* Test function
483  * Get pinhole timeout
484  */
GetPinholeOutboundTimeout(struct UPNPUrls * urls,struct IGDdatas * data,const char * remoteaddr,const char * eport,const char * intaddr,const char * iport,const char * proto)485 static void GetPinholeOutboundTimeout(struct UPNPUrls * urls, struct IGDdatas * data,
486 					const char * remoteaddr, const char * eport,
487 					const char * intaddr, const char * iport,
488 					const char * proto)
489 {
490 	int timeout = 0;
491 	int r;
492 
493 	if(!intaddr || !remoteaddr || !iport || !eport || !proto)
494 	{
495 		fprintf(stderr, "Wrong arguments\n");
496 		return;
497 	}
498 
499 	r = UPNP_GetOutboundPinholeTimeout(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, &timeout);
500 	if(r!=UPNPCOMMAND_SUCCESS)
501 		printf("GetOutboundPinholeTimeout([%s]:%s -> [%s]:%s) failed with code %d (%s)\n",
502 		       intaddr, iport, remoteaddr, eport, r, strupnperror(r));
503 	else
504 		printf("GetOutboundPinholeTimeout: ([%s]:%s -> [%s]:%s) / Timeout = %d\n", intaddr, iport, remoteaddr, eport, timeout);
505 }
506 
507 static void
GetPinholePackets(struct UPNPUrls * urls,struct IGDdatas * data,const char * uniqueID)508 GetPinholePackets(struct UPNPUrls * urls,
509                struct IGDdatas * data, const char * uniqueID)
510 {
511 	int r, pinholePackets = 0;
512 	if(!uniqueID)
513 	{
514 		fprintf(stderr, "invalid arguments\n");
515 		return;
516 	}
517 	r = UPNP_GetPinholePackets(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &pinholePackets);
518 	if(r!=UPNPCOMMAND_SUCCESS)
519 		printf("GetPinholePackets() failed with code %d (%s)\n", r, strupnperror(r));
520 	else
521 		printf("GetPinholePackets: Pinhole ID = %s / PinholePackets = %d\n", uniqueID, pinholePackets);
522 }
523 
524 static void
CheckPinhole(struct UPNPUrls * urls,struct IGDdatas * data,const char * uniqueID)525 CheckPinhole(struct UPNPUrls * urls,
526                struct IGDdatas * data, const char * uniqueID)
527 {
528 	int r, isWorking = 0;
529 	if(!uniqueID)
530 	{
531 		fprintf(stderr, "invalid arguments\n");
532 		return;
533 	}
534 	r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking);
535 	if(r!=UPNPCOMMAND_SUCCESS)
536 		printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r));
537 	else
538 		printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");
539 }
540 
541 static void
RemovePinhole(struct UPNPUrls * urls,struct IGDdatas * data,const char * uniqueID)542 RemovePinhole(struct UPNPUrls * urls,
543                struct IGDdatas * data, const char * uniqueID)
544 {
545 	int r;
546 	if(!uniqueID)
547 	{
548 		fprintf(stderr, "invalid arguments\n");
549 		return;
550 	}
551 	r = UPNP_DeletePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID);
552 	printf("UPNP_DeletePinhole() returned : %d\n", r);
553 }
554 
555 
556 /* sample upnp client program */
main(int argc,char ** argv)557 int main(int argc, char ** argv)
558 {
559 	char command = 0;
560 	char ** commandargv = 0;
561 	int commandargc = 0;
562 	struct UPNPDev * devlist = 0;
563 	char lanaddr[64] = "unset";	/* my ip address on the LAN */
564 	int i;
565 	const char * rootdescurl = 0;
566 	const char * multicastif = 0;
567 	const char * minissdpdpath = 0;
568 	int localport = UPNP_LOCAL_PORT_ANY;
569 	int retcode = 0;
570 	int error = 0;
571 	int ipv6 = 0;
572 	int ignore = 0;
573 	unsigned char ttl = 2;	/* defaulting to 2 */
574 	const char * description = 0;
575 
576 #ifdef _WIN32
577 	WSADATA wsaData;
578 	int nResult = WSAStartup(MAKEWORD(2,2), &wsaData);
579 	if(nResult != NO_ERROR)
580 	{
581 		fprintf(stderr, "WSAStartup() failed.\n");
582 		return -1;
583 	}
584 #endif
585     printf("upnpc : miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING);
586 	printf(" (c) 2005-2020 Thomas Bernard.\n");
587     printf("Go to http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/\n"
588 	       "for more information.\n");
589 	/* command line processing */
590 	for(i=1; i<argc; i++)
591 	{
592 		if(0 == strcmp(argv[i], "--help") || 0 == strcmp(argv[i], "-h"))
593 		{
594 			command = 0;
595 			break;
596 		}
597 		if(argv[i][0] == '-')
598 		{
599 			if(argv[i][1] == 'u')
600 				rootdescurl = argv[++i];
601 			else if(argv[i][1] == 'm')
602 			{
603 				multicastif = argv[++i];
604 				minissdpdpath = "";	/* Disable usage of minissdpd */
605 			}
606 			else if(argv[i][1] == 'z')
607 			{
608 				char junk;
609 				if(sscanf(argv[++i], "%d%c", &localport, &junk)!=1 ||
610 					localport<0 || localport>65535 ||
611 				   (localport >1 && localport < 1024))
612 				{
613 					fprintf(stderr, "Invalid localport '%s'\n", argv[i]);
614 					localport = UPNP_LOCAL_PORT_ANY;
615 					break;
616 				}
617 			}
618 			else if(argv[i][1] == 'p')
619 				minissdpdpath = argv[++i];
620 			else if(argv[i][1] == '6')
621 				ipv6 = 1;
622 			else if(argv[i][1] == 'e')
623 				description = argv[++i];
624 			else if(argv[i][1] == 't')
625 				ttl = (unsigned char)atoi(argv[++i]);
626 			else if(argv[i][1] == 'i')
627 				ignore = 1;
628 			else
629 			{
630 				command = argv[i][1];
631 				i++;
632 				commandargv = argv + i;
633 				commandargc = argc - i;
634 				break;
635 			}
636 		}
637 		else
638 		{
639 			fprintf(stderr, "option '%s' invalid\n", argv[i]);
640 		}
641 	}
642 
643 	if(!command
644 	   || (command == 'a' && commandargc<4)
645 	   || (command == 'd' && argc<2)
646 	   || (command == 'r' && argc<2)
647 	   || (command == 'A' && commandargc<6)
648 	   || (command == 'U' && commandargc<2)
649 	   || (command == 'D' && commandargc<1))
650 	{
651 		fprintf(stderr, "Usage :\t%s [options] -a ip port external_port protocol [duration] [remote host]\n\t\tAdd port redirection\n", argv[0]);
652 		fprintf(stderr, "       \t%s [options] -d external_port protocol [remote host]\n\t\tDelete port redirection\n", argv[0]);
653 		fprintf(stderr, "       \t%s [options] -s\n\t\tGet Connection status\n", argv[0]);
654 		fprintf(stderr, "       \t%s [options] -l\n\t\tList redirections\n", argv[0]);
655 		fprintf(stderr, "       \t%s [options] -L\n\t\tList redirections (using GetListOfPortMappings (for IGD:2 only)\n", argv[0]);
656 		fprintf(stderr, "       \t%s [options] -n ip port external_port protocol [duration] [remote host]\n\t\tAdd (any) port redirection allowing IGD to use alternative external_port (for IGD:2 only)\n", argv[0]);
657 		fprintf(stderr, "       \t%s [options] -N external_port_start external_port_end protocol [manage]\n\t\tDelete range of port redirections (for IGD:2 only)\n", argv[0]);
658 		fprintf(stderr, "       \t%s [options] -r port1 [external_port1] protocol1 [port2 [external_port2] protocol2] [...]\n\t\tAdd all redirections to the current host\n", argv[0]);
659 		fprintf(stderr, "       \t%s [options] -A remote_ip remote_port internal_ip internal_port protocol lease_time\n\t\tAdd Pinhole (for IGD:2 only)\n", argv[0]);
660 		fprintf(stderr, "       \t%s [options] -U uniqueID new_lease_time\n\t\tUpdate Pinhole (for IGD:2 only)\n", argv[0]);
661 		fprintf(stderr, "       \t%s [options] -C uniqueID\n\t\tCheck if Pinhole is Working (for IGD:2 only)\n", argv[0]);
662 		fprintf(stderr, "       \t%s [options] -K uniqueID\n\t\tGet Number of packets going through the rule (for IGD:2 only)\n", argv[0]);
663 		fprintf(stderr, "       \t%s [options] -D uniqueID\n\t\tDelete Pinhole (for IGD:2 only)\n", argv[0]);
664 		fprintf(stderr, "       \t%s [options] -S\n\t\tGet Firewall status (for IGD:2 only)\n", argv[0]);
665 		fprintf(stderr, "       \t%s [options] -G remote_ip remote_port internal_ip internal_port protocol\n\t\tGet Outbound Pinhole Timeout (for IGD:2 only)\n", argv[0]);
666 		fprintf(stderr, "       \t%s [options] -P\n\t\tGet Presentation url\n", argv[0]);
667 		fprintf(stderr, "\nprotocol is UDP or TCP\n");
668 		fprintf(stderr, "Options:\n");
669 		fprintf(stderr, "  -e description : set description for port mapping.\n");
670 		fprintf(stderr, "  -6 : use ip v6 instead of ip v4.\n");
671 		fprintf(stderr, "  -u url : bypass discovery process by providing the XML root description url.\n");
672 		fprintf(stderr, "  -m address/interface : provide ip address (ip v4) or interface name (ip v4 or v6) to use for sending SSDP multicast packets.\n");
673 		fprintf(stderr, "  -z localport : SSDP packets local (source) port (1024-65535).\n");
674 		fprintf(stderr, "  -p path : use this path for MiniSSDPd socket.\n");
675 		fprintf(stderr, "  -t ttl : set multicast TTL. Default value is 2.\n");
676 		fprintf(stderr, "  -i : ignore errors and try to use also disconnected IGD or non-IGD device.\n");
677 		return 1;
678 	}
679 
680 	if( rootdescurl
681 	  || (devlist = upnpDiscover(2000, multicastif, minissdpdpath,
682 	                             localport, ipv6, ttl, &error)))
683 	{
684 		struct UPNPDev * device;
685 		struct UPNPUrls urls;
686 		struct IGDdatas data;
687 		if(devlist)
688 		{
689 			printf("List of UPNP devices found on the network :\n");
690 			for(device = devlist; device; device = device->pNext)
691 			{
692 				printf(" desc: %s\n st: %s\n\n",
693 					   device->descURL, device->st);
694 			}
695 		}
696 		else if(!rootdescurl)
697 		{
698 			printf("upnpDiscover() error code=%d\n", error);
699 		}
700 		i = 1;
701 		if( (rootdescurl && UPNP_GetIGDFromUrl(rootdescurl, &urls, &data, lanaddr, sizeof(lanaddr)))
702 		  || (i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr))))
703 		{
704 			switch(i) {
705 			case 1:
706 				printf("Found valid IGD : %s\n", urls.controlURL);
707 				break;
708 			case 2:
709 				printf("Found a (not connected?) IGD : %s\n", urls.controlURL);
710 				if (ignore) printf("Trying to continue anyway\n");
711 				break;
712 			case 3:
713 				printf("UPnP device found. Is it an IGD ? : %s\n", urls.controlURL);
714 				if (ignore) printf("Trying to continue anyway\n");
715 				break;
716 			default:
717 				printf("Found device (igd ?) : %s\n", urls.controlURL);
718 				if (ignore) printf("Trying to continue anyway\n");
719 			}
720 			if(i==1 || ignore) {
721 
722 			printf("Local LAN ip address : %s\n", lanaddr);
723 			#if 0
724 			printf("getting \"%s\"\n", urls.ipcondescURL);
725 			descXML = miniwget(urls.ipcondescURL, &descXMLsize);
726 			if(descXML)
727 			{
728 				/*fwrite(descXML, 1, descXMLsize, stdout);*/
729 				free(descXML); descXML = NULL;
730 			}
731 			#endif
732 
733 			switch(command)
734 			{
735 			case 'l':
736 				DisplayInfos(&urls, &data);
737 				ListRedirections(&urls, &data);
738 				break;
739 			case 'L':
740 				NewListRedirections(&urls, &data);
741 				break;
742 			case 'a':
743 				if (SetRedirectAndTest(&urls, &data,
744 						   commandargv[0], commandargv[1],
745 						   commandargv[2], commandargv[3],
746 						   (commandargc > 4) && is_int(commandargv[4]) ? commandargv[4] : "0",
747 						   (commandargc > 4) && !is_int(commandargv[4]) ? commandargv[4] : (commandargc > 5) ? commandargv[5] : NULL,
748 						   description, 0) < 0)
749 					retcode = 2;
750 				break;
751 			case 'd':
752 				if (RemoveRedirect(&urls, &data, commandargv[0], commandargv[1],
753 				               commandargc > 2 ? commandargv[2] : NULL) < 0)
754 					retcode = 2;
755 				break;
756 			case 'n':	/* aNy */
757 				if (SetRedirectAndTest(&urls, &data,
758 						   commandargv[0], commandargv[1],
759 						   commandargv[2], commandargv[3],
760 						   (commandargc > 4) && is_int(commandargv[4]) ? commandargv[4] : "0",
761 						   (commandargc > 4) && !is_int(commandargv[4]) ? commandargv[4] : (commandargc > 5) ? commandargv[5] : NULL,
762 						   description, 1) < 0)
763 					retcode = 2;
764 				break;
765 			case 'N':
766 				if (commandargc < 3)
767 					fprintf(stderr, "too few arguments\n");
768 
769 				if (RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2],
770 						    commandargc > 3 ? commandargv[3] : NULL) < 0)
771 					retcode = 2;
772 				break;
773 			case 's':
774 				GetConnectionStatus(&urls, &data);
775 				break;
776 			case 'r':
777 				i = 0;
778 				while(i<commandargc)
779 				{
780 					if(!is_int(commandargv[i])) {
781 						/* 1st parameter not an integer : error */
782 						fprintf(stderr, "command -r : %s is not an port number\n", commandargv[i]);
783 						retcode = 1;
784 						break;
785 					} else if(is_int(commandargv[i+1])){
786 						/* 2nd parameter is an integer : <port> <external_port> <protocol> */
787 						if (SetRedirectAndTest(&urls, &data,
788 								   lanaddr, commandargv[i],
789 								   commandargv[i+1], commandargv[i+2], "0", NULL,
790 								   description, 0) < 0)
791 							retcode = 2;
792 						i+=3;	/* 3 parameters parsed */
793 					} else {
794 						/* 2nd parameter not an integer : <port> <protocol> */
795 						if (SetRedirectAndTest(&urls, &data,
796 								   lanaddr, commandargv[i],
797 								   commandargv[i], commandargv[i+1], "0", NULL,
798 								   description, 0) < 0)
799 							retcode = 2;
800 						i+=2;	/* 2 parameters parsed */
801 					}
802 				}
803 				break;
804 			case 'A':
805 				SetPinholeAndTest(&urls, &data,
806 				                  commandargv[0], commandargv[1],
807 				                  commandargv[2], commandargv[3],
808 				                  commandargv[4], commandargv[5]);
809 				break;
810 			case 'U':
811 				GetPinholeAndUpdate(&urls, &data,
812 				                   commandargv[0], commandargv[1]);
813 				break;
814 			case 'C':
815 				for(i=0; i<commandargc; i++)
816 				{
817 					CheckPinhole(&urls, &data, commandargv[i]);
818 				}
819 				break;
820 			case 'K':
821 				for(i=0; i<commandargc; i++)
822 				{
823 					GetPinholePackets(&urls, &data, commandargv[i]);
824 				}
825 				break;
826 			case 'D':
827 				for(i=0; i<commandargc; i++)
828 				{
829 					RemovePinhole(&urls, &data, commandargv[i]);
830 				}
831 				break;
832 			case 'S':
833 				GetFirewallStatus(&urls, &data);
834 				break;
835 			case 'G':
836 				GetPinholeOutboundTimeout(&urls, &data,
837 							commandargv[0], commandargv[1],
838 							commandargv[2], commandargv[3],
839 							commandargv[4]);
840 				break;
841 			case 'P':
842 				printf("Presentation URL found:\n");
843 				printf("            %s\n", data.presentationurl);
844 				break;
845 			default:
846 				fprintf(stderr, "Unknown switch -%c\n", command);
847 				retcode = 1;
848 			}
849 
850 			} else {
851 				fprintf(stderr, "No valid UPNP Internet Gateway Device found.\n");
852 				retcode = 1;
853 			}
854 			FreeUPNPUrls(&urls);
855 		}
856 		else
857 		{
858 			fprintf(stderr, "No valid UPNP Internet Gateway Device found.\n");
859 			retcode = 1;
860 		}
861 		freeUPNPDevlist(devlist); devlist = 0;
862 	}
863 	else
864 	{
865 		fprintf(stderr, "No IGD UPnP Device found on the network !\n");
866 		retcode = 1;
867 	}
868 #ifdef _WIN32
869 	nResult = WSACleanup();
870 	if(nResult != NO_ERROR) {
871 		fprintf(stderr, "WSACleanup() failed.\n");
872 	}
873 #endif /* _WIN32 */
874 	return retcode;
875 }
876 
877