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(×tarted));
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