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