Annotation of embedaddon/miniupnpd/upnpsoap.c, revision 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>