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