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