Annotation of embedaddon/miniupnpd/upnpsoap.c, revision 1.1.1.1
1.1 misho 1: /* $Id: upnpsoap.c,v 1.66 2011/01/01 20:17:44 nanard Exp $ */
2: /* MiniUPnP project
3: * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4: * (c) 2006-2011 Thomas Bernard
5: * This software is subject to the conditions detailed
6: * in the LICENCE file provided within the distribution */
7:
8: #include <stdio.h>
9: #include <stdlib.h>
10: #include <string.h>
11: #include <sys/socket.h>
12: #include <unistd.h>
13: #include <syslog.h>
14: #include <sys/types.h>
15: #include <arpa/inet.h>
16: #include <netinet/in.h>
17: #include <netdb.h>
18:
19: #include "config.h"
20: #include "upnpglobalvars.h"
21: #include "upnphttp.h"
22: #include "upnpsoap.h"
23: #include "upnpreplyparse.h"
24: #include "upnpredirect.h"
25: #include "getifaddr.h"
26: #include "getifstats.h"
27:
28: static void
29: BuildSendAndCloseSoapResp(struct upnphttp * h,
30: const char * body, int bodylen)
31: {
32: static const char beforebody[] =
33: "<?xml version=\"1.0\"?>\r\n"
34: "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
35: "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
36: "<s:Body>";
37:
38: static const char afterbody[] =
39: "</s:Body>"
40: "</s:Envelope>\r\n";
41:
42: BuildHeader_upnphttp(h, 200, "OK", sizeof(beforebody) - 1
43: + sizeof(afterbody) - 1 + bodylen );
44:
45: memcpy(h->res_buf + h->res_buflen, beforebody, sizeof(beforebody) - 1);
46: h->res_buflen += sizeof(beforebody) - 1;
47:
48: memcpy(h->res_buf + h->res_buflen, body, bodylen);
49: h->res_buflen += bodylen;
50:
51: memcpy(h->res_buf + h->res_buflen, afterbody, sizeof(afterbody) - 1);
52: h->res_buflen += sizeof(afterbody) - 1;
53:
54: SendResp_upnphttp(h);
55: CloseSocket_upnphttp(h);
56: }
57:
58: static void
59: GetConnectionTypeInfo(struct upnphttp * h, const char * action)
60: {
61: static const char resp[] =
62: "<u:GetConnectionTypeInfoResponse "
63: "xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">"
64: "<NewConnectionType>IP_Routed</NewConnectionType>"
65: "<NewPossibleConnectionTypes>IP_Routed</NewPossibleConnectionTypes>"
66: "</u:GetConnectionTypeInfoResponse>";
67: BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
68: }
69:
70: static void
71: GetTotalBytesSent(struct upnphttp * h, const char * action)
72: {
73: int r;
74:
75: static const char resp[] =
76: "<u:%sResponse "
77: "xmlns:u=\"%s\">"
78: "<NewTotalBytesSent>%lu</NewTotalBytesSent>"
79: "</u:%sResponse>";
80:
81: char body[512];
82: int bodylen;
83: struct ifdata data;
84:
85: r = getifstats(ext_if_name, &data);
86: bodylen = snprintf(body, sizeof(body), resp,
87: action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
88: r<0?0:data.obytes, action);
89: BuildSendAndCloseSoapResp(h, body, bodylen);
90: }
91:
92: static void
93: GetTotalBytesReceived(struct upnphttp * h, const char * action)
94: {
95: int r;
96:
97: static const char resp[] =
98: "<u:%sResponse "
99: "xmlns:u=\"%s\">"
100: "<NewTotalBytesReceived>%lu</NewTotalBytesReceived>"
101: "</u:%sResponse>";
102:
103: char body[512];
104: int bodylen;
105: struct ifdata data;
106:
107: r = getifstats(ext_if_name, &data);
108: bodylen = snprintf(body, sizeof(body), resp,
109: action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
110: r<0?0:data.ibytes, action);
111: BuildSendAndCloseSoapResp(h, body, bodylen);
112: }
113:
114: static void
115: GetTotalPacketsSent(struct upnphttp * h, const char * action)
116: {
117: int r;
118:
119: static const char resp[] =
120: "<u:%sResponse "
121: "xmlns:u=\"%s\">"
122: "<NewTotalPacketsSent>%lu</NewTotalPacketsSent>"
123: "</u:%sResponse>";
124:
125: char body[512];
126: int bodylen;
127: struct ifdata data;
128:
129: r = getifstats(ext_if_name, &data);
130: bodylen = snprintf(body, sizeof(body), resp,
131: action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
132: r<0?0:data.opackets, action);
133: BuildSendAndCloseSoapResp(h, body, bodylen);
134: }
135:
136: static void
137: GetTotalPacketsReceived(struct upnphttp * h, const char * action)
138: {
139: int r;
140:
141: static const char resp[] =
142: "<u:%sResponse "
143: "xmlns:u=\"%s\">"
144: "<NewTotalPacketsReceived>%lu</NewTotalPacketsReceived>"
145: "</u:%sResponse>";
146:
147: char body[512];
148: int bodylen;
149: struct ifdata data;
150:
151: r = getifstats(ext_if_name, &data);
152: bodylen = snprintf(body, sizeof(body), resp,
153: action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
154: r<0?0:data.ipackets, action);
155: BuildSendAndCloseSoapResp(h, body, bodylen);
156: }
157:
158: static void
159: GetCommonLinkProperties(struct upnphttp * h, const char * action)
160: {
161: /* WANAccessType : set depending on the hardware :
162: * DSL, POTS (plain old Telephone service), Cable, Ethernet */
163: static const char resp[] =
164: "<u:%sResponse "
165: "xmlns:u=\"%s\">"
166: /*"<NewWANAccessType>DSL</NewWANAccessType>"*/
167: "<NewWANAccessType>Cable</NewWANAccessType>"
168: "<NewLayer1UpstreamMaxBitRate>%lu</NewLayer1UpstreamMaxBitRate>"
169: "<NewLayer1DownstreamMaxBitRate>%lu</NewLayer1DownstreamMaxBitRate>"
170: "<NewPhysicalLinkStatus>%s</NewPhysicalLinkStatus>"
171: "</u:%sResponse>";
172:
173: char body[2048];
174: int bodylen;
175: struct ifdata data;
176: const char * status = "Up"; /* Up, Down (Required),
177: * Initializing, Unavailable (Optional) */
178: char ext_ip_addr[INET_ADDRSTRLEN];
179:
180: if((downstream_bitrate == 0) || (upstream_bitrate == 0))
181: {
182: if(getifstats(ext_if_name, &data) >= 0)
183: {
184: if(downstream_bitrate == 0) downstream_bitrate = data.baudrate;
185: if(upstream_bitrate == 0) upstream_bitrate = data.baudrate;
186: }
187: }
188: if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0) {
189: status = "Down";
190: }
191: bodylen = snprintf(body, sizeof(body), resp,
192: action, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1",
193: upstream_bitrate, downstream_bitrate,
194: status, action);
195: BuildSendAndCloseSoapResp(h, body, bodylen);
196: }
197:
198: static void
199: GetStatusInfo(struct upnphttp * h, const char * action)
200: {
201: static const char resp[] =
202: "<u:%sResponse "
203: "xmlns:u=\"%s\">"
204: "<NewConnectionStatus>%s</NewConnectionStatus>"
205: "<NewLastConnectionError>ERROR_NONE</NewLastConnectionError>"
206: "<NewUptime>%ld</NewUptime>"
207: "</u:%sResponse>";
208:
209: char body[512];
210: int bodylen;
211: time_t uptime;
212: const char * status = "Connected";
213: /* ConnectionStatus possible values :
214: * Unconfigured, Connecting, Connected, PendingDisconnect,
215: * Disconnecting, Disconnected */
216: char ext_ip_addr[INET_ADDRSTRLEN];
217:
218: if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0) {
219: status = "Disconnected";
220: }
221: uptime = (time(NULL) - startup_time);
222: bodylen = snprintf(body, sizeof(body), resp,
223: action, "urn:schemas-upnp-org:service:WANIPConnection:1",
224: status, (long)uptime, action);
225: BuildSendAndCloseSoapResp(h, body, bodylen);
226: }
227:
228: static void
229: GetNATRSIPStatus(struct upnphttp * h, const char * action)
230: {
231: static const char resp[] =
232: "<u:GetNATRSIPStatusResponse "
233: "xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">"
234: "<NewRSIPAvailable>0</NewRSIPAvailable>"
235: "<NewNATEnabled>1</NewNATEnabled>"
236: "</u:GetNATRSIPStatusResponse>";
237: /* 2.2.9. RSIPAvailable
238: * This variable indicates if Realm-specific IP (RSIP) is available
239: * as a feature on the InternetGatewayDevice. RSIP is being defined
240: * in the NAT working group in the IETF to allow host-NATing using
241: * a standard set of message exchanges. It also allows end-to-end
242: * applications that otherwise break if NAT is introduced
243: * (e.g. IPsec-based VPNs).
244: * A gateway that does not support RSIP should set this variable to 0. */
245: BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
246: }
247:
248: static void
249: GetExternalIPAddress(struct upnphttp * h, const char * action)
250: {
251: static const char resp[] =
252: "<u:%sResponse "
253: "xmlns:u=\"%s\">"
254: "<NewExternalIPAddress>%s</NewExternalIPAddress>"
255: "</u:%sResponse>";
256:
257: char body[512];
258: int bodylen;
259: char ext_ip_addr[INET_ADDRSTRLEN];
260:
261: #ifndef MULTIPLE_EXTERNAL_IP
262: if(use_ext_ip_addr)
263: {
264: strncpy(ext_ip_addr, use_ext_ip_addr, INET_ADDRSTRLEN);
265: }
266: else if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0)
267: {
268: syslog(LOG_ERR, "Failed to get ip address for interface %s",
269: ext_if_name);
270: strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
271: }
272: #else
273: int i;
274: strncpy(ext_ip_addr, "0.0.0.0", INET_ADDRSTRLEN);
275: for(i = 0; i<n_lan_addr; i++)
276: {
277: if( (h->clientaddr.s_addr & lan_addr[i].mask.s_addr)
278: == (lan_addr[i].addr.s_addr & lan_addr[i].mask.s_addr))
279: {
280: strncpy(ext_ip_addr, lan_addr[i].ext_ip_str, INET_ADDRSTRLEN);
281: break;
282: }
283: }
284: #endif
285: bodylen = snprintf(body, sizeof(body), resp,
286: action, "urn:schemas-upnp-org:service:WANIPConnection:1",
287: ext_ip_addr, action);
288: BuildSendAndCloseSoapResp(h, body, bodylen);
289: }
290:
291: static void
292: AddPortMapping(struct upnphttp * h, const char * action)
293: {
294: int r;
295:
296: static const char resp[] =
297: "<u:AddPortMappingResponse "
298: "xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\"/>";
299:
300: struct NameValueParserData data;
301: char * int_ip, * int_port, * ext_port, * protocol, * desc;
302: char * leaseduration;
303: unsigned short iport, eport;
304:
305: struct hostent *hp; /* getbyhostname() */
306: char ** ptr; /* getbyhostname() */
307: struct in_addr result_ip;/*unsigned char result_ip[16];*/ /* inet_pton() */
308:
309: ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
310: int_ip = GetValueFromNameValueList(&data, "NewInternalClient");
311:
312: if (!int_ip)
313: {
314: ClearNameValueList(&data);
315: SoapError(h, 402, "Invalid Args");
316: return;
317: }
318:
319: /* if ip not valid assume hostname and convert */
320: if (inet_pton(AF_INET, int_ip, &result_ip) <= 0)
321: {
322: hp = gethostbyname(int_ip);
323: if(hp && hp->h_addrtype == AF_INET)
324: {
325: for(ptr = hp->h_addr_list; ptr && *ptr; ptr++)
326: {
327: int_ip = inet_ntoa(*((struct in_addr *) *ptr));
328: result_ip = *((struct in_addr *) *ptr);
329: /* TODO : deal with more than one ip per hostname */
330: break;
331: }
332: }
333: else
334: {
335: syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip);
336: ClearNameValueList(&data);
337: SoapError(h, 402, "Invalid Args");
338: return;
339: }
340: }
341:
342: /* check if NewInternalAddress is the client address */
343: if(GETFLAG(SECUREMODEMASK))
344: {
345: if(h->clientaddr.s_addr != result_ip.s_addr)
346: {
347: syslog(LOG_INFO, "Client %s tried to redirect port to %s",
348: inet_ntoa(h->clientaddr), int_ip);
349: ClearNameValueList(&data);
350: SoapError(h, 718, "ConflictInMappingEntry");
351: return;
352: }
353: }
354:
355: int_port = GetValueFromNameValueList(&data, "NewInternalPort");
356: ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
357: protocol = GetValueFromNameValueList(&data, "NewProtocol");
358: desc = GetValueFromNameValueList(&data, "NewPortMappingDescription");
359: leaseduration = GetValueFromNameValueList(&data, "NewLeaseDuration");
360:
361: if (!int_port || !ext_port || !protocol)
362: {
363: ClearNameValueList(&data);
364: SoapError(h, 402, "Invalid Args");
365: return;
366: }
367:
368: eport = (unsigned short)atoi(ext_port);
369: iport = (unsigned short)atoi(int_port);
370:
371: if(leaseduration && atoi(leaseduration)) {
372: /* at the moment, lease duration is always infinite */
373: syslog(LOG_WARNING, "NewLeaseDuration=%s not supported, ignored. (ip=%s, desc='%s')", leaseduration, int_ip, desc);
374: }
375:
376: syslog(LOG_INFO, "%s: ext port %hu to %s:%hu protocol %s for: %s",
377: action, eport, int_ip, iport, protocol, desc);
378:
379: r = upnp_redirect(eport, int_ip, iport, protocol, desc);
380:
381: ClearNameValueList(&data);
382:
383: /* possible error codes for AddPortMapping :
384: * 402 - Invalid Args
385: * 501 - Action Failed
386: * 715 - Wildcard not permited in SrcAddr
387: * 716 - Wildcard not permited in ExtPort
388: * 718 - ConflictInMappingEntry
389: * 724 - SamePortValuesRequired
390: * 725 - OnlyPermanentLeasesSupported
391: The NAT implementation only supports permanent lease times on
392: port mappings
393: * 726 - RemoteHostOnlySupportsWildcard
394: RemoteHost must be a wildcard and cannot be a specific IP
395: address or DNS name
396: * 727 - ExternalPortOnlySupportsWildcard
397: ExternalPort must be a wildcard and cannot be a specific port
398: value
399: * 728 - NoPortMapsAvailable
400: There are not enough free prots available to complete the mapping
401: (added in IGD v2) */
402: switch(r)
403: {
404: case 0: /* success */
405: BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
406: break;
407: case -2: /* already redirected */
408: case -3: /* not permitted */
409: SoapError(h, 718, "ConflictInMappingEntry");
410: break;
411: default:
412: SoapError(h, 501, "ActionFailed");
413: }
414: }
415:
416: /* AddAnyPortMapping was added in WANIPConnection v2 */
417: static void
418: AddAnyPortMapping(struct upnphttp * h, const char * action)
419: {
420: int r;
421: static const char resp[] =
422: "<u:%sResponse "
423: "xmlns:u=\"%s\">"
424: "<NewReservedPort>%hu</NewReservedPort>"
425: "</u:%sResponse>";
426:
427: char body[512];
428: int bodylen;
429:
430: struct NameValueParserData data;
431: const char * int_ip, * int_port, * ext_port, * protocol, * desc;
432: const char * r_host;
433: unsigned short iport, eport;
434: unsigned int leaseduration;
435:
436: struct hostent *hp; /* getbyhostname() */
437: char ** ptr; /* getbyhostname() */
438: struct in_addr result_ip;/*unsigned char result_ip[16];*/ /* inet_pton() */
439:
440: ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
441: r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
442: ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
443: protocol = GetValueFromNameValueList(&data, "NewProtocol");
444: int_port = GetValueFromNameValueList(&data, "NewInternalPort");
445: int_ip = GetValueFromNameValueList(&data, "NewInternalClient");
446: /* NewEnabled */
447: desc = GetValueFromNameValueList(&data, "NewPortMappingDescription");
448: leaseduration = atoi(GetValueFromNameValueList(&data, "NewLeaseDuration"));
449: if(leaseduration == 0)
450: leaseduration = 604800;
451:
452: eport = (unsigned short)atoi(ext_port);
453: iport = (unsigned short)atoi(int_port);
454:
455: if (!int_ip)
456: {
457: ClearNameValueList(&data);
458: SoapError(h, 402, "Invalid Args");
459: return;
460: }
461:
462: /* if ip not valid assume hostname and convert */
463: if (inet_pton(AF_INET, int_ip, &result_ip) <= 0)
464: {
465: hp = gethostbyname(int_ip);
466: if(hp && hp->h_addrtype == AF_INET)
467: {
468: for(ptr = hp->h_addr_list; ptr && *ptr; ptr++)
469: {
470: int_ip = inet_ntoa(*((struct in_addr *) *ptr));
471: result_ip = *((struct in_addr *) *ptr);
472: /* TODO : deal with more than one ip per hostname */
473: break;
474: }
475: }
476: else
477: {
478: syslog(LOG_ERR, "Failed to convert hostname '%s' to ip address", int_ip);
479: ClearNameValueList(&data);
480: SoapError(h, 402, "Invalid Args");
481: return;
482: }
483: }
484:
485: /* check if NewInternalAddress is the client address */
486: if(GETFLAG(SECUREMODEMASK))
487: {
488: if(h->clientaddr.s_addr != result_ip.s_addr)
489: {
490: syslog(LOG_INFO, "Client %s tried to redirect port to %s",
491: inet_ntoa(h->clientaddr), int_ip);
492: ClearNameValueList(&data);
493: SoapError(h, 606, "Action not authorized");
494: return;
495: }
496: }
497:
498: /* TODO : accept a different external port */
499: r = upnp_redirect(eport, int_ip, iport, protocol, desc);
500:
501: ClearNameValueList(&data);
502:
503: switch(r)
504: {
505: case 0: /* success */
506: bodylen = snprintf(body, sizeof(body), resp,
507: action, "urn:schemas-upnp-org:service:WANIPConnection:2",
508: eport, action);
509: BuildSendAndCloseSoapResp(h, body, bodylen);
510: break;
511: case -2: /* already redirected */
512: SoapError(h, 718, "ConflictInMappingEntry");
513: break;
514: case -3: /* not permitted */
515: SoapError(h, 606, "Action not authorized");
516: break;
517: default:
518: SoapError(h, 501, "ActionFailed");
519: }
520: }
521:
522: static void
523: GetSpecificPortMappingEntry(struct upnphttp * h, const char * action)
524: {
525: int r;
526:
527: static const char resp[] =
528: "<u:%sResponse "
529: "xmlns:u=\"%s\">"
530: "<NewInternalPort>%u</NewInternalPort>"
531: "<NewInternalClient>%s</NewInternalClient>"
532: "<NewEnabled>1</NewEnabled>"
533: "<NewPortMappingDescription>%s</NewPortMappingDescription>"
534: "<NewLeaseDuration>0</NewLeaseDuration>"
535: "</u:%sResponse>";
536:
537: char body[1024];
538: int bodylen;
539: struct NameValueParserData data;
540: const char * r_host, * ext_port, * protocol;
541: unsigned short eport, iport;
542: char int_ip[32];
543: char desc[64];
544:
545: ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
546: r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
547: ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
548: protocol = GetValueFromNameValueList(&data, "NewProtocol");
549:
550: if(!ext_port || !protocol)
551: {
552: ClearNameValueList(&data);
553: SoapError(h, 402, "Invalid Args");
554: return;
555: }
556:
557: eport = (unsigned short)atoi(ext_port);
558:
559: r = upnp_get_redirection_infos(eport, protocol, &iport,
560: int_ip, sizeof(int_ip),
561: desc, sizeof(desc));
562:
563: if(r < 0)
564: {
565: SoapError(h, 714, "NoSuchEntryInArray");
566: }
567: else
568: {
569: syslog(LOG_INFO, "%s: rhost='%s' %s %s found => %s:%u desc='%s'",
570: action,
571: r_host, ext_port, protocol, int_ip, (unsigned int)iport, desc);
572: bodylen = snprintf(body, sizeof(body), resp,
573: action, "urn:schemas-upnp-org:service:WANIPConnection:1",
574: (unsigned int)iport, int_ip, desc,
575: action);
576: BuildSendAndCloseSoapResp(h, body, bodylen);
577: }
578:
579: ClearNameValueList(&data);
580: }
581:
582: static void
583: DeletePortMapping(struct upnphttp * h, const char * action)
584: {
585: int r;
586:
587: static const char resp[] =
588: "<u:DeletePortMappingResponse "
589: "xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">"
590: "</u:DeletePortMappingResponse>";
591:
592: struct NameValueParserData data;
593: const char * r_host, * ext_port, * protocol;
594: unsigned short eport;
595:
596: ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
597: r_host = GetValueFromNameValueList(&data, "NewRemoteHost");
598: ext_port = GetValueFromNameValueList(&data, "NewExternalPort");
599: protocol = GetValueFromNameValueList(&data, "NewProtocol");
600:
601: if(!ext_port || !protocol)
602: {
603: ClearNameValueList(&data);
604: SoapError(h, 402, "Invalid Args");
605: return;
606: }
607:
608: eport = (unsigned short)atoi(ext_port);
609:
610: /* TODO : if in secure mode, check the IP */
611:
612: syslog(LOG_INFO, "%s: external port: %hu, protocol: %s",
613: action, eport, protocol);
614:
615: r = upnp_delete_redirection(eport, protocol);
616:
617: if(r < 0)
618: {
619: SoapError(h, 714, "NoSuchEntryInArray");
620: }
621: else
622: {
623: BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
624: }
625:
626: ClearNameValueList(&data);
627: }
628:
629: /* DeletePortMappingRange was added in IGD spec v2 */
630: static void
631: DeletePortMappingRange(struct upnphttp * h, const char * action)
632: {
633: static const char resp[] =
634: "<u:DeletePortMappingRangeResponse "
635: "xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:2\">"
636: "</u:DeletePortMappingRangeResponse>";
637: struct NameValueParserData data;
638: const char * protocol;
639: unsigned short startport, endport;
640: int manage;
641:
642: ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
643: startport = (unsigned short)atoi(GetValueFromNameValueList(&data, "NewStartPort"));
644: endport = (unsigned short)atoi(GetValueFromNameValueList(&data, "NewEndPort"));
645: protocol = GetValueFromNameValueList(&data, "NewProtocol");
646: manage = atoi(GetValueFromNameValueList(&data, "NewManage"));
647:
648: /* TODO : implement the method ! */
649:
650: /* possible errors :
651: 606 - Action not authorized
652: 730 - PortMappingNotFound
653: 733 - InconsistentParameter
654: */
655: BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
656:
657: ClearNameValueList(&data);
658: }
659:
660: static void
661: GetGenericPortMappingEntry(struct upnphttp * h, const char * action)
662: {
663: int r;
664:
665: static const char resp[] =
666: "<u:%sResponse "
667: "xmlns:u=\"%s\">"
668: "<NewRemoteHost></NewRemoteHost>"
669: "<NewExternalPort>%u</NewExternalPort>"
670: "<NewProtocol>%s</NewProtocol>"
671: "<NewInternalPort>%u</NewInternalPort>"
672: "<NewInternalClient>%s</NewInternalClient>"
673: "<NewEnabled>1</NewEnabled>"
674: "<NewPortMappingDescription>%s</NewPortMappingDescription>"
675: "<NewLeaseDuration>0</NewLeaseDuration>"
676: "</u:%sResponse>";
677:
678: int index = 0;
679: unsigned short eport, iport;
680: const char * m_index;
681: char protocol[4], iaddr[32];
682: char desc[64];
683: struct NameValueParserData data;
684:
685: ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
686: m_index = GetValueFromNameValueList(&data, "NewPortMappingIndex");
687:
688: if(!m_index)
689: {
690: ClearNameValueList(&data);
691: SoapError(h, 402, "Invalid Args");
692: return;
693: }
694:
695: index = (int)atoi(m_index);
696:
697: syslog(LOG_INFO, "%s: index=%d", action, index);
698:
699: r = upnp_get_redirection_infos_by_index(index, &eport, protocol, &iport,
700: iaddr, sizeof(iaddr),
701: desc, sizeof(desc));
702:
703: if(r < 0)
704: {
705: SoapError(h, 713, "SpecifiedArrayIndexInvalid");
706: }
707: else
708: {
709: int bodylen;
710: char body[2048];
711: bodylen = snprintf(body, sizeof(body), resp,
712: action, "urn:schemas-upnp-org:service:WANIPConnection:1",
713: (unsigned int)eport, protocol, (unsigned int)iport, iaddr, desc,
714: action);
715: BuildSendAndCloseSoapResp(h, body, bodylen);
716: }
717:
718: ClearNameValueList(&data);
719: }
720:
721: /* GetListOfPortMappings was added in the IGD v2 specification */
722: static void
723: GetListOfPortMappings(struct upnphttp * h, const char * action)
724: {
725: static const char resp[] =
726: "<u:%sResponse "
727: "xmlns:u=\"%s\">"
728: "<NewPortListing><![CDATA[%s]]</NewPortListing>"
729: "</u:%sResponse>";
730:
731: char body[512];
732: int bodylen;
733:
734: struct NameValueParserData data;
735: unsigned short startport, endport;
736: const char * protocol;
737: int manage;
738: int number;
739:
740: ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
741: startport = (unsigned short)atoi(GetValueFromNameValueList(&data, "NewStartPort"));
742: endport = (unsigned short)atoi(GetValueFromNameValueList(&data, "NewEndPort"));
743: protocol = GetValueFromNameValueList(&data, "NewProtocol");
744: manage = atoi(GetValueFromNameValueList(&data, "NewManage"));
745: number = atoi(GetValueFromNameValueList(&data, "NewNumberOfPorts"));
746:
747: /*
748: TODO : build the PortMappingList xml document :
749:
750: <p:PortMappingList xmlns:p="urn:schemas-upnp-org:gw:WANIPConnection"
751: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
752: xsi:schemaLocation="urn:schemas-upnp-org:gw:WANIPConnection
753: http://www.upnp.org/schemas/gw/WANIPConnection-v2.xsd">
754: <p:PortMappingEntry>
755: <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
756: <p:NewExternalPort>2345</p:NewExternalPort>
757: <p:NewProtocol>TCP</p:NewProtocol>
758: <p:NewInternalPort>2345</p:NewInternalPort>
759: <p:NewInternalClient>192.168.1.137</p:NewInternalClient>
760: <p:NewEnabled>1</p:NewEnabled>
761: <p:NewDescription>dooom</p:NewDescription>
762: <p:NewLeaseTime>345</p:NewLeaseTime>
763: </p:PortMappingEntry>
764: </p:PortMappingList>
765: */
766: bodylen = snprintf(body, sizeof(body), resp,
767: action, "urn:schemas-upnp-org:service:WANIPConnection:2",
768: "", action);
769: BuildSendAndCloseSoapResp(h, body, bodylen);
770:
771: ClearNameValueList(&data);
772: }
773:
774: #ifdef ENABLE_L3F_SERVICE
775: static void
776: SetDefaultConnectionService(struct upnphttp * h, const char * action)
777: {
778: static const char resp[] =
779: "<u:SetDefaultConnectionServiceResponse "
780: "xmlns:u=\"urn:schemas-upnp-org:service:Layer3Forwarding:1\">"
781: "</u:SetDefaultConnectionServiceResponse>";
782: struct NameValueParserData data;
783: char * p;
784: ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
785: p = GetValueFromNameValueList(&data, "NewDefaultConnectionService");
786: if(p) {
787: syslog(LOG_INFO, "%s(%s) : Ignored", action, p);
788: }
789: ClearNameValueList(&data);
790: BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
791: }
792:
793: static void
794: GetDefaultConnectionService(struct upnphttp * h, const char * action)
795: {
796: static const char resp[] =
797: "<u:%sResponse "
798: "xmlns:u=\"urn:schemas-upnp-org:service:Layer3Forwarding:1\">"
799: "<NewDefaultConnectionService>%s:WANConnectionDevice:1,"
800: "urn:upnp-org:serviceId:WANIPConn1</NewDefaultConnectionService>"
801: "</u:%sResponse>";
802: /* example from UPnP_IGD_Layer3Forwarding 1.0.pdf :
803: * uuid:44f5824f-c57d-418c-a131-f22b34e14111:WANConnectionDevice:1,
804: * urn:upnp-org:serviceId:WANPPPConn1 */
805: char body[1024];
806: int bodylen;
807:
808: bodylen = snprintf(body, sizeof(body), resp,
809: action, uuidvalue, action);
810: BuildSendAndCloseSoapResp(h, body, bodylen);
811: }
812: #endif
813:
814: /* Added for compliance with WANIPConnection v2 */
815: static void
816: SetConnectionType(struct upnphttp * h, const char * action)
817: {
818: const char * connection_type;
819: struct NameValueParserData data;
820:
821: ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
822: connection_type = GetValueFromNameValueList(&data, "NewConnectionType");
823: /* Unconfigured, IP_Routed, IP_Bridged */
824: ClearNameValueList(&data);
825: /* always return a ReadOnly error */
826: SoapError(h, 731, "ReadOnly");
827: }
828:
829: /* Added for compliance with WANIPConnection v2 */
830: static void
831: RequestConnection(struct upnphttp * h, const char * action)
832: {
833: SoapError(h, 606, "Action not authorized");
834: }
835:
836: /* Added for compliance with WANIPConnection v2 */
837: static void
838: ForceTermination(struct upnphttp * h, const char * action)
839: {
840: SoapError(h, 606, "Action not authorized");
841: }
842:
843: /*
844: If a control point calls QueryStateVariable on a state variable that is not
845: buffered in memory within (or otherwise available from) the service,
846: the service must return a SOAP fault with an errorCode of 404 Invalid Var.
847:
848: QueryStateVariable remains useful as a limited test tool but may not be
849: part of some future versions of UPnP.
850: */
851: static void
852: QueryStateVariable(struct upnphttp * h, const char * action)
853: {
854: static const char resp[] =
855: "<u:%sResponse "
856: "xmlns:u=\"%s\">"
857: "<return>%s</return>"
858: "</u:%sResponse>";
859:
860: char body[512];
861: int bodylen;
862: struct NameValueParserData data;
863: const char * var_name;
864:
865: ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data);
866: /*var_name = GetValueFromNameValueList(&data, "QueryStateVariable"); */
867: /*var_name = GetValueFromNameValueListIgnoreNS(&data, "varName");*/
868: var_name = GetValueFromNameValueList(&data, "varName");
869:
870: /*syslog(LOG_INFO, "QueryStateVariable(%.40s)", var_name); */
871:
872: if(!var_name)
873: {
874: SoapError(h, 402, "Invalid Args");
875: }
876: else if(strcmp(var_name, "ConnectionStatus") == 0)
877: {
878: const char * status = "Connected";
879: char ext_ip_addr[INET_ADDRSTRLEN];
880: if(getifaddr(ext_if_name, ext_ip_addr, INET_ADDRSTRLEN) < 0) {
881: status = "Disconnected";
882: }
883: bodylen = snprintf(body, sizeof(body), resp,
884: action, "urn:schemas-upnp-org:control-1-0",
885: status, action);
886: BuildSendAndCloseSoapResp(h, body, bodylen);
887: }
888: #if 0
889: /* not usefull */
890: else if(strcmp(var_name, "ConnectionType") == 0)
891: {
892: bodylen = snprintf(body, sizeof(body), resp, "IP_Routed");
893: BuildSendAndCloseSoapResp(h, body, bodylen);
894: }
895: else if(strcmp(var_name, "LastConnectionError") == 0)
896: {
897: bodylen = snprintf(body, sizeof(body), resp, "ERROR_NONE");
898: BuildSendAndCloseSoapResp(h, body, bodylen);
899: }
900: #endif
901: else if(strcmp(var_name, "PortMappingNumberOfEntries") == 0)
902: {
903: char strn[10];
904: snprintf(strn, sizeof(strn), "%i",
905: upnp_get_portmapping_number_of_entries());
906: bodylen = snprintf(body, sizeof(body), resp,
907: action, "urn:schemas-upnp-org:control-1-0",
908: strn, action);
909: BuildSendAndCloseSoapResp(h, body, bodylen);
910: }
911: else
912: {
913: syslog(LOG_NOTICE, "%s: Unknown: %s", action, var_name?var_name:"");
914: SoapError(h, 404, "Invalid Var");
915: }
916:
917: ClearNameValueList(&data);
918: }
919:
920: /* Windows XP as client send the following requests :
921: * GetConnectionTypeInfo
922: * GetNATRSIPStatus
923: * ? GetTotalBytesSent - WANCommonInterfaceConfig
924: * ? GetTotalBytesReceived - idem
925: * ? GetTotalPacketsSent - idem
926: * ? GetTotalPacketsReceived - idem
927: * GetCommonLinkProperties - idem
928: * GetStatusInfo - WANIPConnection
929: * GetExternalIPAddress
930: * QueryStateVariable / ConnectionStatus!
931: */
932: static const struct
933: {
934: const char * methodName;
935: void (*methodImpl)(struct upnphttp *, const char *);
936: }
937: soapMethods[] =
938: {
939: { "GetConnectionTypeInfo", GetConnectionTypeInfo },
940: { "GetNATRSIPStatus", GetNATRSIPStatus},
941: { "GetExternalIPAddress", GetExternalIPAddress},
942: { "AddPortMapping", AddPortMapping},
943: { "DeletePortMapping", DeletePortMapping},
944: { "GetGenericPortMappingEntry", GetGenericPortMappingEntry},
945: { "GetSpecificPortMappingEntry", GetSpecificPortMappingEntry},
946: { "QueryStateVariable", QueryStateVariable},
947: { "GetTotalBytesSent", GetTotalBytesSent},
948: { "GetTotalBytesReceived", GetTotalBytesReceived},
949: { "GetTotalPacketsSent", GetTotalPacketsSent},
950: { "GetTotalPacketsReceived", GetTotalPacketsReceived},
951: { "GetCommonLinkProperties", GetCommonLinkProperties},
952: { "GetStatusInfo", GetStatusInfo},
953: /* Required in WANIPConnection:2 */
954: { "SetConnectionType", SetConnectionType},
955: { "RequestConnection", RequestConnection},
956: { "ForceTermination", ForceTermination},
957: { "AddAnyPortMapping", AddAnyPortMapping},
958: { "DeletePortMappingRange", DeletePortMappingRange},
959: { "GetListOfPortMappings", GetListOfPortMappings},
960: #ifdef ENABLE_L3F_SERVICE
961: { "SetDefaultConnectionService", SetDefaultConnectionService},
962: { "GetDefaultConnectionService", GetDefaultConnectionService},
963: #endif
964: { 0, 0 }
965: };
966:
967: void
968: ExecuteSoapAction(struct upnphttp * h, const char * action, int n)
969: {
970: char * p;
971: char * p2;
972: int i, len, methodlen;
973:
974: i = 0;
975: p = strchr(action, '#');
976:
977: if(p)
978: {
979: p++;
980: p2 = strchr(p, '"');
981: if(p2)
982: methodlen = p2 - p;
983: else
984: methodlen = n - (p - action);
985: /*syslog(LOG_DEBUG, "SoapMethod: %.*s", methodlen, p);*/
986: while(soapMethods[i].methodName)
987: {
988: len = strlen(soapMethods[i].methodName);
989: if(strncmp(p, soapMethods[i].methodName, len) == 0)
990: {
991: soapMethods[i].methodImpl(h, soapMethods[i].methodName);
992: return;
993: }
994: i++;
995: }
996:
997: syslog(LOG_NOTICE, "SoapMethod: Unknown: %.*s", methodlen, p);
998: }
999:
1000: SoapError(h, 401, "Invalid Action");
1001: }
1002:
1003: /* Standard Errors:
1004: *
1005: * errorCode errorDescription Description
1006: * -------- ---------------- -----------
1007: * 401 Invalid Action No action by that name at this service.
1008: * 402 Invalid Args Could be any of the following: not enough in args,
1009: * too many in args, no in arg by that name,
1010: * one or more in args are of the wrong data type.
1011: * 403 Out of Sync Out of synchronization.
1012: * 501 Action Failed May be returned in current state of service
1013: * prevents invoking that action.
1014: * 600-699 TBD Common action errors. Defined by UPnP Forum
1015: * Technical Committee.
1016: * 700-799 TBD Action-specific errors for standard actions.
1017: * Defined by UPnP Forum working committee.
1018: * 800-899 TBD Action-specific errors for non-standard actions.
1019: * Defined by UPnP vendor.
1020: */
1021: void
1022: SoapError(struct upnphttp * h, int errCode, const char * errDesc)
1023: {
1024: static const char resp[] =
1025: "<s:Envelope "
1026: "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
1027: "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
1028: "<s:Body>"
1029: "<s:Fault>"
1030: "<faultcode>s:Client</faultcode>"
1031: "<faultstring>UPnPError</faultstring>"
1032: "<detail>"
1033: "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">"
1034: "<errorCode>%d</errorCode>"
1035: "<errorDescription>%s</errorDescription>"
1036: "</UPnPError>"
1037: "</detail>"
1038: "</s:Fault>"
1039: "</s:Body>"
1040: "</s:Envelope>";
1041:
1042: char body[2048];
1043: int bodylen;
1044:
1045: syslog(LOG_INFO, "Returning UPnPError %d: %s", errCode, errDesc);
1046: bodylen = snprintf(body, sizeof(body), resp, errCode, errDesc);
1047: BuildResp2_upnphttp(h, 500, "Internal Server Error", body, bodylen);
1048: SendResp_upnphttp(h);
1049: CloseSocket_upnphttp(h);
1050: }
1051:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>