1/* 2 * UPnP WPS Device - Web connections 3 * Copyright (c) 2000-2003 Intel Corporation 4 * Copyright (c) 2006-2007 Sony Corporation 5 * Copyright (c) 2008-2009 Atheros Communications 6 * Copyright (c) 2009, Jouni Malinen <j@w1.fi> 7 * 8 * See wps_upnp.c for more details on licensing and code history. 9 */ 10 11#include "includes.h" 12#include <fcntl.h> 13 14#include "common.h" 15#include "base64.h" 16#include "eloop.h" 17#include "uuid.h" 18#include "httpread.h" 19#include "wps_i.h" 20#include "wps_upnp.h" 21#include "wps_upnp_i.h" 22 23/*************************************************************************** 24 * Web connections (we serve pages of info about ourselves, handle 25 * requests, etc. etc.). 26 **************************************************************************/ 27 28#define WEB_CONNECTION_TIMEOUT_SEC 30 /* Drop web connection after t.o. */ 29#define WEB_CONNECTION_MAX_READ 8000 /* Max we'll read for TCP request */ 30#define MAX_WEB_CONNECTIONS 10 /* max simultaneous web connects */ 31 32 33static const char *urn_wfawlanconfig = 34 "urn:schemas-wifialliance-org:service:WFAWLANConfig:1"; 35static const char *http_server_hdr = 36 "Server: unspecified, UPnP/1.0, unspecified\r\n"; 37static const char *http_connection_close = 38 "Connection: close\r\n"; 39 40/* 41 * Incoming web connections are recorded in this struct. 42 * A web connection is a TCP connection to us, the server; 43 * it is called a "web connection" because we use http and serve 44 * data that looks like web pages. 45 * State information is need to track the connection until we figure 46 * out what they want and what we want to do about it. 47 */ 48struct web_connection { 49 /* double linked list */ 50 struct web_connection *next; 51 struct web_connection *prev; 52 struct upnp_wps_device_sm *sm; /* parent */ 53 int sd; /* socket to read from */ 54 struct sockaddr_in cli_addr; 55 int sd_registered; /* nonzero if we must cancel registration */ 56 struct httpread *hread; /* state machine for reading socket */ 57 int n_rcvd_data; /* how much data read so far */ 58 int done; /* internal flag, set when we've finished */ 59}; 60 61 62/* 63 * XML parsing and formatting 64 * 65 * XML is a markup language based on unicode; usually (and in our case, 66 * always!) based on utf-8. utf-8 uses a variable number of bytes per 67 * character. utf-8 has the advantage that all non-ASCII unicode characters are 68 * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII 69 * characters are single ascii bytes, thus we can use typical text processing. 70 * 71 * (One other interesting thing about utf-8 is that it is possible to look at 72 * any random byte and determine if it is the first byte of a character as 73 * versus a continuation byte). 74 * 75 * The base syntax of XML uses a few ASCII punctionation characters; any 76 * characters that would appear in the payload data are rewritten using 77 * sequences, e.g., & for ampersand(&) and < for left angle bracket (<). 78 * Five such escapes total (more can be defined but that does not apply to our 79 * case). Thus we can safely parse for angle brackets etc. 80 * 81 * XML describes tree structures of tagged data, with each element beginning 82 * with an opening tag <label> and ending with a closing tag </label> with 83 * matching label. (There is also a self-closing tag <label/> which is supposed 84 * to be equivalent to <label></label>, i.e., no payload, but we are unlikely 85 * to see it for our purpose). 86 * 87 * Actually the opening tags are a little more complicated because they can 88 * contain "attributes" after the label (delimited by ascii space or tab chars) 89 * of the form attribute_label="value" or attribute_label='value'; as it turns 90 * out we do not have to read any of these attributes, just ignore them. 91 * 92 * Labels are any sequence of chars other than space, tab, right angle bracket 93 * (and ?), but may have an inner structure of <namespace><colon><plain_label>. 94 * As it turns out, we can ignore the namespaces, in fact we can ignore the 95 * entire tree hierarchy, because the plain labels we are looking for will be 96 * unique (not in general, but for this application). We do however have to be 97 * careful to skip over the namespaces. 98 * 99 * In generating XML we have to be more careful, but that is easy because 100 * everything we do is pretty canned. The only real care to take is to escape 101 * any special chars in our payload. 102 */ 103 104/** 105 * xml_next_tag - Advance to next tag 106 * @in: Input 107 * @out: OUT: start of tag just after '<' 108 * @out_tagname: OUT: start of name of tag, skipping namespace 109 * @end: OUT: one after tag 110 * Returns: 0 on success, 1 on failure 111 * 112 * A tag has form: 113 * <left angle bracket><...><right angle bracket> 114 * Within the angle brackets, there is an optional leading forward slash (which 115 * makes the tag an ending tag), then an optional leading label (followed by 116 * colon) and then the tag name itself. 117 * 118 * Note that angle brackets present in the original data must have been encoded 119 * as < and > so they will not trouble us. 120 */ 121static int xml_next_tag(char *in, char **out, char **out_tagname, 122 char **end) 123{ 124 while (*in && *in != '<') 125 in++; 126 if (*in != '<') 127 return 1; 128 *out = ++in; 129 if (*in == '/') 130 in++; 131 *out_tagname = in; /* maybe */ 132 while (isalnum(*in) || *in == '-') 133 in++; 134 if (*in == ':') 135 *out_tagname = ++in; 136 while (*in && *in != '>') 137 in++; 138 if (*in != '>') 139 return 1; 140 *end = ++in; 141 return 0; 142} 143 144 145/* xml_data_encode -- format data for xml file, escaping special characters. 146 * 147 * Note that we assume we are using utf8 both as input and as output! 148 * In utf8, characters may be classed as follows: 149 * 0xxxxxxx(2) -- 1 byte ascii char 150 * 11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80 151 * 110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here) 152 * 1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here) 153 * 11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here) 154 * 10xxxxxx(2) -- extension byte (6 payload bits per byte) 155 * Some values implied by the above are however illegal because they 156 * do not represent unicode chars or are not the shortest encoding. 157 * Actually, we can almost entirely ignore the above and just do 158 * text processing same as for ascii text. 159 * 160 * XML is written with arbitrary unicode characters, except that five 161 * characters have special meaning and so must be escaped where they 162 * appear in payload data... which we do here. 163 */ 164static void xml_data_encode(struct wpabuf *buf, const char *data, int len) 165{ 166 int i; 167 for (i = 0; i < len; i++) { 168 u8 c = ((u8 *) data)[i]; 169 if (c == '<') { 170 wpabuf_put_str(buf, "<"); 171 continue; 172 } 173 if (c == '>') { 174 wpabuf_put_str(buf, ">"); 175 continue; 176 } 177 if (c == '&') { 178 wpabuf_put_str(buf, "&"); 179 continue; 180 } 181 if (c == '\'') { 182 wpabuf_put_str(buf, "'"); 183 continue; 184 } 185 if (c == '"') { 186 wpabuf_put_str(buf, """); 187 continue; 188 } 189 /* 190 * We could try to represent control characters using the 191 * sequence: &#x; where x is replaced by a hex numeral, but not 192 * clear why we would do this. 193 */ 194 wpabuf_put_u8(buf, c); 195 } 196} 197 198 199/* xml_add_tagged_data -- format tagged data as a new xml line. 200 * 201 * tag must not have any special chars. 202 * data may have special chars, which are escaped. 203 */ 204static void xml_add_tagged_data(struct wpabuf *buf, const char *tag, 205 const char *data) 206{ 207 wpabuf_printf(buf, "<%s>", tag); 208 xml_data_encode(buf, data, os_strlen(data)); 209 wpabuf_printf(buf, "</%s>\n", tag); 210} 211 212 213/* A POST body looks something like (per upnp spec): 214 * <?xml version="1.0"?> 215 * <s:Envelope 216 * xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" 217 * s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> 218 * <s:Body> 219 * <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v"> 220 * <argumentName>in arg value</argumentName> 221 * other in args and their values go here, if any 222 * </u:actionName> 223 * </s:Body> 224 * </s:Envelope> 225 * 226 * where : 227 * s: might be some other namespace name followed by colon 228 * u: might be some other namespace name followed by colon 229 * actionName will be replaced according to action requested 230 * schema following actionName will be WFA scheme instead 231 * argumentName will be actual argument name 232 * (in arg value) will be actual argument value 233 */ 234static int 235upnp_get_first_document_item(char *doc, const char *item, char **value) 236{ 237 const char *match = item; 238 int match_len = os_strlen(item); 239 char *tag; 240 char *tagname; 241 char *end; 242 243 *value = NULL; /* default, bad */ 244 245 /* 246 * This is crude: ignore any possible tag name conflicts and go right 247 * to the first tag of this name. This should be ok for the limited 248 * domain of UPnP messages. 249 */ 250 for (;;) { 251 if (xml_next_tag(doc, &tag, &tagname, &end)) 252 return 1; 253 doc = end; 254 if (!os_strncasecmp(tagname, match, match_len) && 255 *tag != '/' && 256 (tagname[match_len] == '>' || 257 !isgraph(tagname[match_len]))) { 258 break; 259 } 260 } 261 end = doc; 262 while (*end && *end != '<') 263 end++; 264 *value = os_zalloc(1 + (end - doc)); 265 if (*value == NULL) 266 return 1; 267 os_memcpy(*value, doc, end - doc); 268 return 0; 269} 270 271 272/* 273 * "Files" that we serve via HTTP. The format of these files is given by 274 * WFA WPS specifications. Extra white space has been removed to save space. 275 */ 276 277static const char wps_scpd_xml[] = 278"<?xml version=\"1.0\"?>\n" 279"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n" 280"<specVersion><major>1</major><minor>0</minor></specVersion>\n" 281"<actionList>\n" 282"<action>\n" 283"<name>GetDeviceInfo</name>\n" 284"<argumentList>\n" 285"<argument>\n" 286"<name>NewDeviceInfo</name>\n" 287"<direction>out</direction>\n" 288"<relatedStateVariable>DeviceInfo</relatedStateVariable>\n" 289"</argument>\n" 290"</argumentList>\n" 291"</action>\n" 292"<action>\n" 293"<name>PutMessage</name>\n" 294"<argumentList>\n" 295"<argument>\n" 296"<name>NewInMessage</name>\n" 297"<direction>in</direction>\n" 298"<relatedStateVariable>InMessage</relatedStateVariable>\n" 299"</argument>\n" 300"<argument>\n" 301"<name>NewOutMessage</name>\n" 302"<direction>out</direction>\n" 303"<relatedStateVariable>OutMessage</relatedStateVariable>\n" 304"</argument>\n" 305"</argumentList>\n" 306"</action>\n" 307"<action>\n" 308"<name>GetAPSettings</name>\n" 309"<argumentList>\n" 310"<argument>\n" 311"<name>NewMessage</name>\n" 312"<direction>in</direction>\n" 313"<relatedStateVariable>Message</relatedStateVariable>\n" 314"</argument>\n" 315"<argument>\n" 316"<name>NewAPSettings</name>\n" 317"<direction>out</direction>\n" 318"<relatedStateVariable>APSettings</relatedStateVariable>\n" 319"</argument>\n" 320"</argumentList>\n" 321"</action>\n" 322"<action>\n" 323"<name>SetAPSettings</name>\n" 324"<argumentList>\n" 325"<argument>\n" 326"<name>APSettings</name>\n" 327"<direction>in</direction>\n" 328"<relatedStateVariable>APSettings</relatedStateVariable>\n" 329"</argument>\n" 330"</argumentList>\n" 331"</action>\n" 332"<action>\n" 333"<name>DelAPSettings</name>\n" 334"<argumentList>\n" 335"<argument>\n" 336"<name>NewAPSettings</name>\n" 337"<direction>in</direction>\n" 338"<relatedStateVariable>APSettings</relatedStateVariable>\n" 339"</argument>\n" 340"</argumentList>\n" 341"</action>\n" 342"<action>\n" 343"<name>GetSTASettings</name>\n" 344"<argumentList>\n" 345"<argument>\n" 346"<name>NewMessage</name>\n" 347"<direction>in</direction>\n" 348"<relatedStateVariable>Message</relatedStateVariable>\n" 349"</argument>\n" 350"<argument>\n" 351"<name>NewSTASettings</name>\n" 352"<direction>out</direction>\n" 353"<relatedStateVariable>STASettings</relatedStateVariable>\n" 354"</argument>\n" 355"</argumentList>\n" 356"</action>\n" 357"<action>\n" 358"<name>SetSTASettings</name>\n" 359"<argumentList>\n" 360"<argument>\n" 361"<name>NewSTASettings</name>\n" 362"<direction>out</direction>\n" 363"<relatedStateVariable>STASettings</relatedStateVariable>\n" 364"</argument>\n" 365"</argumentList>\n" 366"</action>\n" 367"<action>\n" 368"<name>DelSTASettings</name>\n" 369"<argumentList>\n" 370"<argument>\n" 371"<name>NewSTASettings</name>\n" 372"<direction>in</direction>\n" 373"<relatedStateVariable>STASettings</relatedStateVariable>\n" 374"</argument>\n" 375"</argumentList>\n" 376"</action>\n" 377"<action>\n" 378"<name>PutWLANResponse</name>\n" 379"<argumentList>\n" 380"<argument>\n" 381"<name>NewMessage</name>\n" 382"<direction>in</direction>\n" 383"<relatedStateVariable>Message</relatedStateVariable>\n" 384"</argument>\n" 385"<argument>\n" 386"<name>NewWLANEventType</name>\n" 387"<direction>in</direction>\n" 388"<relatedStateVariable>WLANEventType</relatedStateVariable>\n" 389"</argument>\n" 390"<argument>\n" 391"<name>NewWLANEventMAC</name>\n" 392"<direction>in</direction>\n" 393"<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n" 394"</argument>\n" 395"</argumentList>\n" 396"</action>\n" 397"<action>\n" 398"<name>SetSelectedRegistrar</name>\n" 399"<argumentList>\n" 400"<argument>\n" 401"<name>NewMessage</name>\n" 402"<direction>in</direction>\n" 403"<relatedStateVariable>Message</relatedStateVariable>\n" 404"</argument>\n" 405"</argumentList>\n" 406"</action>\n" 407"<action>\n" 408"<name>RebootAP</name>\n" 409"<argumentList>\n" 410"<argument>\n" 411"<name>NewAPSettings</name>\n" 412"<direction>in</direction>\n" 413"<relatedStateVariable>APSettings</relatedStateVariable>\n" 414"</argument>\n" 415"</argumentList>\n" 416"</action>\n" 417"<action>\n" 418"<name>ResetAP</name>\n" 419"<argumentList>\n" 420"<argument>\n" 421"<name>NewMessage</name>\n" 422"<direction>in</direction>\n" 423"<relatedStateVariable>Message</relatedStateVariable>\n" 424"</argument>\n" 425"</argumentList>\n" 426"</action>\n" 427"<action>\n" 428"<name>RebootSTA</name>\n" 429"<argumentList>\n" 430"<argument>\n" 431"<name>NewSTASettings</name>\n" 432"<direction>in</direction>\n" 433"<relatedStateVariable>APSettings</relatedStateVariable>\n" 434"</argument>\n" 435"</argumentList>\n" 436"</action>\n" 437"<action>\n" 438"<name>ResetSTA</name>\n" 439"<argumentList>\n" 440"<argument>\n" 441"<name>NewMessage</name>\n" 442"<direction>in</direction>\n" 443"<relatedStateVariable>Message</relatedStateVariable>\n" 444"</argument>\n" 445"</argumentList>\n" 446"</action>\n" 447"</actionList>\n" 448"<serviceStateTable>\n" 449"<stateVariable sendEvents=\"no\">\n" 450"<name>Message</name>\n" 451"<dataType>bin.base64</dataType>\n" 452"</stateVariable>\n" 453"<stateVariable sendEvents=\"no\">\n" 454"<name>InMessage</name>\n" 455"<dataType>bin.base64</dataType>\n" 456"</stateVariable>\n" 457"<stateVariable sendEvents=\"no\">\n" 458"<name>OutMessage</name>\n" 459"<dataType>bin.base64</dataType>\n" 460"</stateVariable>\n" 461"<stateVariable sendEvents=\"no\">\n" 462"<name>DeviceInfo</name>\n" 463"<dataType>bin.base64</dataType>\n" 464"</stateVariable>\n" 465"<stateVariable sendEvents=\"no\">\n" 466"<name>APSettings</name>\n" 467"<dataType>bin.base64</dataType>\n" 468"</stateVariable>\n" 469"<stateVariable sendEvents=\"yes\">\n" 470"<name>APStatus</name>\n" 471"<dataType>ui1</dataType>\n" 472"</stateVariable>\n" 473"<stateVariable sendEvents=\"no\">\n" 474"<name>STASettings</name>\n" 475"<dataType>bin.base64</dataType>\n" 476"</stateVariable>\n" 477"<stateVariable sendEvents=\"yes\">\n" 478"<name>STAStatus</name>\n" 479"<dataType>ui1</dataType>\n" 480"</stateVariable>\n" 481"<stateVariable sendEvents=\"yes\">\n" 482"<name>WLANEvent</name>\n" 483"<dataType>bin.base64</dataType>\n" 484"</stateVariable>\n" 485"<stateVariable sendEvents=\"no\">\n" 486"<name>WLANEventType</name>\n" 487"<dataType>ui1</dataType>\n" 488"</stateVariable>\n" 489"<stateVariable sendEvents=\"no\">\n" 490"<name>WLANEventMAC</name>\n" 491"<dataType>string</dataType>\n" 492"</stateVariable>\n" 493"<stateVariable sendEvents=\"no\">\n" 494"<name>WLANResponse</name>\n" 495"<dataType>bin.base64</dataType>\n" 496"</stateVariable>\n" 497"</serviceStateTable>\n" 498"</scpd>\n" 499; 500 501 502static const char *wps_device_xml_prefix = 503 "<?xml version=\"1.0\"?>\n" 504 "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n" 505 "<specVersion>\n" 506 "<major>1</major>\n" 507 "<minor>0</minor>\n" 508 "</specVersion>\n" 509 "<device>\n" 510 "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1" 511 "</deviceType>\n"; 512 513static const char *wps_device_xml_postfix = 514 "<serviceList>\n" 515 "<service>\n" 516 "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1" 517 "</serviceType>\n" 518 "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>" 519 "\n" 520 "<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n" 521 "<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n" 522 "<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n" 523 "</service>\n" 524 "</serviceList>\n" 525 "</device>\n" 526 "</root>\n"; 527 528 529/* format_wps_device_xml -- produce content of "file" wps_device.xml 530 * (UPNP_WPS_DEVICE_XML_FILE) 531 */ 532static void format_wps_device_xml(struct upnp_wps_device_sm *sm, 533 struct wpabuf *buf) 534{ 535 const char *s; 536 char uuid_string[80]; 537 538 wpabuf_put_str(buf, wps_device_xml_prefix); 539 540 /* 541 * Add required fields with default values if not configured. Add 542 * optional and recommended fields only if configured. 543 */ 544 s = sm->wps->friendly_name; 545 s = ((s && *s) ? s : "WPS Access Point"); 546 xml_add_tagged_data(buf, "friendlyName", s); 547 548 s = sm->wps->dev.manufacturer; 549 s = ((s && *s) ? s : ""); 550 xml_add_tagged_data(buf, "manufacturer", s); 551 552 if (sm->wps->manufacturer_url) 553 xml_add_tagged_data(buf, "manufacturerURL", 554 sm->wps->manufacturer_url); 555 556 if (sm->wps->model_description) 557 xml_add_tagged_data(buf, "modelDescription", 558 sm->wps->model_description); 559 560 s = sm->wps->dev.model_name; 561 s = ((s && *s) ? s : ""); 562 xml_add_tagged_data(buf, "modelName", s); 563 564 if (sm->wps->dev.model_number) 565 xml_add_tagged_data(buf, "modelNumber", 566 sm->wps->dev.model_number); 567 568 if (sm->wps->model_url) 569 xml_add_tagged_data(buf, "modelURL", sm->wps->model_url); 570 571 if (sm->wps->dev.serial_number) 572 xml_add_tagged_data(buf, "serialNumber", 573 sm->wps->dev.serial_number); 574 575 uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string)); 576 s = uuid_string; 577 /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data() 578 * easily... 579 */ 580 wpabuf_put_str(buf, "<UDN>uuid:"); 581 xml_data_encode(buf, s, os_strlen(s)); 582 wpabuf_put_str(buf, "</UDN>\n"); 583 584 if (sm->wps->upc) 585 xml_add_tagged_data(buf, "UPC", sm->wps->upc); 586 587 wpabuf_put_str(buf, wps_device_xml_postfix); 588} 589 590 591void web_connection_stop(struct web_connection *c) 592{ 593 struct upnp_wps_device_sm *sm = c->sm; 594 595 httpread_destroy(c->hread); 596 c->hread = NULL; 597 close(c->sd); 598 c->sd = -1; 599 if (c->next == c) { 600 sm->web_connections = NULL; 601 } else { 602 if (sm->web_connections == c) 603 sm->web_connections = c->next; 604 c->next->prev = c->prev; 605 c->prev->next = c->next; 606 } 607 os_free(c); 608 sm->n_web_connections--; 609} 610 611 612static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code) 613{ 614 wpabuf_put_str(buf, "HTTP/1.1 "); 615 switch (code) { 616 case HTTP_OK: 617 wpabuf_put_str(buf, "200 OK\r\n"); 618 break; 619 case HTTP_BAD_REQUEST: 620 wpabuf_put_str(buf, "400 Bad request\r\n"); 621 break; 622 case HTTP_PRECONDITION_FAILED: 623 wpabuf_put_str(buf, "412 Precondition failed\r\n"); 624 break; 625 case HTTP_UNIMPLEMENTED: 626 wpabuf_put_str(buf, "501 Unimplemented\r\n"); 627 break; 628 case HTTP_INTERNAL_SERVER_ERROR: 629 default: 630 wpabuf_put_str(buf, "500 Internal server error\r\n"); 631 break; 632 } 633} 634 635 636static void http_put_date(struct wpabuf *buf) 637{ 638 wpabuf_put_str(buf, "Date: "); 639 format_date(buf); 640 wpabuf_put_str(buf, "\r\n"); 641} 642 643 644static void http_put_empty(struct wpabuf *buf, enum http_reply_code code) 645{ 646 http_put_reply_code(buf, code); 647 wpabuf_put_str(buf, http_server_hdr); 648 wpabuf_put_str(buf, http_connection_close); 649 wpabuf_put_str(buf, "Content-Length: 0\r\n" 650 "\r\n"); 651} 652 653 654/* Given that we have received a header w/ GET, act upon it 655 * 656 * Format of GET (case-insensitive): 657 * 658 * First line must be: 659 * GET /<file> HTTP/1.1 660 * Since we don't do anything fancy we just ignore other lines. 661 * 662 * Our response (if no error) which includes only required lines is: 663 * HTTP/1.1 200 OK 664 * Connection: close 665 * Content-Type: text/xml 666 * Date: <rfc1123-date> 667 * 668 * Header lines must end with \r\n 669 * Per RFC 2616, content-length: is not required but connection:close 670 * would appear to be required (given that we will be closing it!). 671 */ 672static void web_connection_parse_get(struct web_connection *c, char *filename) 673{ 674 struct upnp_wps_device_sm *sm = c->sm; 675 struct wpabuf *buf; /* output buffer, allocated */ 676 char *put_length_here; 677 char *body_start; 678 enum { 679 GET_DEVICE_XML_FILE, 680 GET_SCPD_XML_FILE 681 } req; 682 size_t extra_len = 0; 683 int body_length; 684 char len_buf[10]; 685 686 /* 687 * It is not required that filenames be case insensitive but it is 688 * allowed and cannot hurt here. 689 */ 690 if (filename == NULL) 691 filename = "(null)"; /* just in case */ 692 if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) { 693 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML"); 694 req = GET_DEVICE_XML_FILE; 695 extra_len = 3000; 696 if (sm->wps->friendly_name) 697 extra_len += os_strlen(sm->wps->friendly_name); 698 if (sm->wps->manufacturer_url) 699 extra_len += os_strlen(sm->wps->manufacturer_url); 700 if (sm->wps->model_description) 701 extra_len += os_strlen(sm->wps->model_description); 702 if (sm->wps->model_url) 703 extra_len += os_strlen(sm->wps->model_url); 704 if (sm->wps->upc) 705 extra_len += os_strlen(sm->wps->upc); 706 } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) { 707 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML"); 708 req = GET_SCPD_XML_FILE; 709 extra_len = os_strlen(wps_scpd_xml); 710 } else { 711 /* File not found */ 712 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s", 713 filename); 714 buf = wpabuf_alloc(200); 715 if (buf == NULL) 716 return; 717 wpabuf_put_str(buf, 718 "HTTP/1.1 404 Not Found\r\n" 719 "Connection: close\r\n"); 720 721 http_put_date(buf); 722 723 /* terminating empty line */ 724 wpabuf_put_str(buf, "\r\n"); 725 726 goto send_buf; 727 } 728 729 buf = wpabuf_alloc(1000 + extra_len); 730 if (buf == NULL) 731 return; 732 733 wpabuf_put_str(buf, 734 "HTTP/1.1 200 OK\r\n" 735 "Content-Type: text/xml; charset=\"utf-8\"\r\n"); 736 wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n"); 737 wpabuf_put_str(buf, "Connection: close\r\n"); 738 wpabuf_put_str(buf, "Content-Length: "); 739 /* 740 * We will paste the length in later, leaving some extra whitespace. 741 * HTTP code is supposed to be tolerant of extra whitespace. 742 */ 743 put_length_here = wpabuf_put(buf, 0); 744 wpabuf_put_str(buf, " \r\n"); 745 746 http_put_date(buf); 747 748 /* terminating empty line */ 749 wpabuf_put_str(buf, "\r\n"); 750 751 body_start = wpabuf_put(buf, 0); 752 753 switch (req) { 754 case GET_DEVICE_XML_FILE: 755 format_wps_device_xml(sm, buf); 756 break; 757 case GET_SCPD_XML_FILE: 758 wpabuf_put_str(buf, wps_scpd_xml); 759 break; 760 } 761 762 /* Now patch in the content length at the end */ 763 body_length = (char *) wpabuf_put(buf, 0) - body_start; 764 os_snprintf(len_buf, 10, "%d", body_length); 765 os_memcpy(put_length_here, len_buf, os_strlen(len_buf)); 766 767send_buf: 768 send_wpabuf(c->sd, buf); 769 wpabuf_free(buf); 770} 771 772 773static struct wpabuf * web_get_item(char *data, const char *name, 774 enum http_reply_code *ret) 775{ 776 char *msg; 777 struct wpabuf *buf; 778 unsigned char *decoded; 779 size_t len; 780 781 if (upnp_get_first_document_item(data, name, &msg)) { 782 *ret = UPNP_ARG_VALUE_INVALID; 783 return NULL; 784 } 785 786 decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len); 787 os_free(msg); 788 if (decoded == NULL) { 789 *ret = UPNP_OUT_OF_MEMORY; 790 return NULL; 791 } 792 793 buf = wpabuf_alloc_ext_data(decoded, len); 794 if (buf == NULL) { 795 os_free(decoded); 796 *ret = UPNP_OUT_OF_MEMORY; 797 return NULL; 798 } 799 return buf; 800} 801 802 803static enum http_reply_code 804web_process_get_device_info(struct upnp_wps_device_sm *sm, 805 struct wpabuf **reply, const char **replyname) 806{ 807 static const char *name = "NewDeviceInfo"; 808 809 wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo"); 810 if (sm->ctx->rx_req_get_device_info == NULL) 811 return HTTP_INTERNAL_SERVER_ERROR; 812 *reply = sm->ctx->rx_req_get_device_info(sm->priv, &sm->peer); 813 if (*reply == NULL) { 814 wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo"); 815 return HTTP_INTERNAL_SERVER_ERROR; 816 } 817 *replyname = name; 818 return HTTP_OK; 819} 820 821 822static enum http_reply_code 823web_process_put_message(struct upnp_wps_device_sm *sm, char *data, 824 struct wpabuf **reply, const char **replyname) 825{ 826 struct wpabuf *msg; 827 static const char *name = "NewOutMessage"; 828 enum http_reply_code ret; 829 830 /* 831 * PutMessage is used by external UPnP-based Registrar to perform WPS 832 * operation with the access point itself; as compared with 833 * PutWLANResponse which is for proxying. 834 */ 835 wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage"); 836 if (sm->ctx->rx_req_put_message == NULL) 837 return HTTP_INTERNAL_SERVER_ERROR; 838 msg = web_get_item(data, "NewInMessage", &ret); 839 if (msg == NULL) 840 return ret; 841 *reply = sm->ctx->rx_req_put_message(sm->priv, &sm->peer, msg); 842 wpabuf_free(msg); 843 if (*reply == NULL) 844 return HTTP_INTERNAL_SERVER_ERROR; 845 *replyname = name; 846 return HTTP_OK; 847} 848 849 850static enum http_reply_code 851web_process_get_ap_settings(struct upnp_wps_device_sm *sm, char *data, 852 struct wpabuf **reply, const char **replyname) 853{ 854 struct wpabuf *msg; 855 static const char *name = "NewAPSettings"; 856 enum http_reply_code ret; 857 858 wpa_printf(MSG_DEBUG, "WPS UPnP: GetAPSettings"); 859 if (sm->ctx->rx_req_get_ap_settings == NULL) 860 return HTTP_INTERNAL_SERVER_ERROR; 861 msg = web_get_item(data, "NewMessage", &ret); 862 if (msg == NULL) 863 return ret; 864 *reply = sm->ctx->rx_req_get_ap_settings(sm->priv, msg); 865 wpabuf_free(msg); 866 if (*reply == NULL) 867 return HTTP_INTERNAL_SERVER_ERROR; 868 *replyname = name; 869 return HTTP_OK; 870} 871 872 873static enum http_reply_code 874web_process_set_ap_settings(struct upnp_wps_device_sm *sm, char *data, 875 struct wpabuf **reply, const char **replyname) 876{ 877 struct wpabuf *msg; 878 enum http_reply_code ret; 879 880 wpa_printf(MSG_DEBUG, "WPS UPnP: SetAPSettings"); 881 msg = web_get_item(data, "NewAPSettings", &ret); 882 if (msg == NULL) 883 return ret; 884 if (!sm->ctx->rx_req_set_ap_settings || 885 sm->ctx->rx_req_set_ap_settings(sm->priv, msg)) { 886 wpabuf_free(msg); 887 return HTTP_INTERNAL_SERVER_ERROR; 888 } 889 wpabuf_free(msg); 890 *replyname = NULL; 891 *reply = NULL; 892 return HTTP_OK; 893} 894 895 896static enum http_reply_code 897web_process_del_ap_settings(struct upnp_wps_device_sm *sm, char *data, 898 struct wpabuf **reply, const char **replyname) 899{ 900 struct wpabuf *msg; 901 enum http_reply_code ret; 902 903 wpa_printf(MSG_DEBUG, "WPS UPnP: DelAPSettings"); 904 msg = web_get_item(data, "NewAPSettings", &ret); 905 if (msg == NULL) 906 return ret; 907 if (!sm->ctx->rx_req_del_ap_settings || 908 sm->ctx->rx_req_del_ap_settings(sm->priv, msg)) { 909 wpabuf_free(msg); 910 return HTTP_INTERNAL_SERVER_ERROR; 911 } 912 wpabuf_free(msg); 913 *replyname = NULL; 914 *reply = NULL; 915 return HTTP_OK; 916} 917 918 919static enum http_reply_code 920web_process_get_sta_settings(struct upnp_wps_device_sm *sm, char *data, 921 struct wpabuf **reply, const char **replyname) 922{ 923 struct wpabuf *msg; 924 static const char *name = "NewSTASettings"; 925 enum http_reply_code ret; 926 927 wpa_printf(MSG_DEBUG, "WPS UPnP: GetSTASettings"); 928 if (sm->ctx->rx_req_get_sta_settings == NULL) 929 return HTTP_INTERNAL_SERVER_ERROR; 930 msg = web_get_item(data, "NewMessage", &ret); 931 if (msg == NULL) 932 return ret; 933 *reply = sm->ctx->rx_req_get_sta_settings(sm->priv, msg); 934 wpabuf_free(msg); 935 if (*reply == NULL) 936 return HTTP_INTERNAL_SERVER_ERROR; 937 *replyname = name; 938 return HTTP_OK; 939} 940 941 942static enum http_reply_code 943web_process_set_sta_settings(struct upnp_wps_device_sm *sm, char *data, 944 struct wpabuf **reply, const char **replyname) 945{ 946 struct wpabuf *msg; 947 enum http_reply_code ret; 948 949 wpa_printf(MSG_DEBUG, "WPS UPnP: SetSTASettings"); 950 msg = web_get_item(data, "NewSTASettings", &ret); 951 if (msg == NULL) 952 return ret; 953 if (!sm->ctx->rx_req_set_sta_settings || 954 sm->ctx->rx_req_set_sta_settings(sm->priv, msg)) { 955 wpabuf_free(msg); 956 return HTTP_INTERNAL_SERVER_ERROR; 957 } 958 wpabuf_free(msg); 959 *replyname = NULL; 960 *reply = NULL; 961 return HTTP_OK; 962} 963 964 965static enum http_reply_code 966web_process_del_sta_settings(struct upnp_wps_device_sm *sm, char *data, 967 struct wpabuf **reply, const char **replyname) 968{ 969 struct wpabuf *msg; 970 enum http_reply_code ret; 971 972 wpa_printf(MSG_DEBUG, "WPS UPnP: DelSTASettings"); 973 msg = web_get_item(data, "NewSTASettings", &ret); 974 if (msg == NULL) 975 return ret; 976 if (!sm->ctx->rx_req_del_sta_settings || 977 sm->ctx->rx_req_del_sta_settings(sm->priv, msg)) { 978 wpabuf_free(msg); 979 return HTTP_INTERNAL_SERVER_ERROR; 980 } 981 wpabuf_free(msg); 982 *replyname = NULL; 983 *reply = NULL; 984 return HTTP_OK; 985} 986 987 988static enum http_reply_code 989web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, 990 struct wpabuf **reply, const char **replyname) 991{ 992 struct wpabuf *msg; 993 enum http_reply_code ret; 994 u8 macaddr[ETH_ALEN]; 995 int ev_type; 996 int type; 997 char *val; 998 999 /* 1000 * External UPnP-based Registrar is passing us a message to be proxied 1001 * over to a Wi-Fi -based client of ours. 1002 */ 1003 1004 wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse"); 1005 msg = web_get_item(data, "NewMessage", &ret); 1006 if (msg == NULL) 1007 return ret; 1008 if (upnp_get_first_document_item(data, "NewWLANEventType", &val)) { 1009 wpabuf_free(msg); 1010 return UPNP_ARG_VALUE_INVALID; 1011 } 1012 ev_type = atol(val); 1013 os_free(val); 1014 val = NULL; 1015 if (upnp_get_first_document_item(data, "NewWLANEventMAC", &val) || 1016 hwaddr_aton(val, macaddr)) { 1017 wpabuf_free(msg); 1018 os_free(val); 1019 return UPNP_ARG_VALUE_INVALID; 1020 } 1021 os_free(val); 1022 if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) { 1023 struct wps_parse_attr attr; 1024 if (wps_parse_msg(msg, &attr) < 0 || 1025 attr.msg_type == NULL) 1026 type = -1; 1027 else 1028 type = *attr.msg_type; 1029 wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type); 1030 } else 1031 type = -1; 1032 if (!sm->ctx->rx_req_put_wlan_response || 1033 sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg, 1034 type)) { 1035 wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->" 1036 "rx_req_put_wlan_response"); 1037 wpabuf_free(msg); 1038 return HTTP_INTERNAL_SERVER_ERROR; 1039 } 1040 wpabuf_free(msg); 1041 *replyname = NULL; 1042 *reply = NULL; 1043 return HTTP_OK; 1044} 1045 1046 1047static enum http_reply_code 1048web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, char *data, 1049 struct wpabuf **reply, 1050 const char **replyname) 1051{ 1052 struct wpabuf *msg; 1053 enum http_reply_code ret; 1054 1055 wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar"); 1056 msg = web_get_item(data, "NewMessage", &ret); 1057 if (msg == NULL) 1058 return ret; 1059 if (!sm->ctx->rx_req_set_selected_registrar || 1060 sm->ctx->rx_req_set_selected_registrar(sm->priv, msg)) { 1061 wpabuf_free(msg); 1062 return HTTP_INTERNAL_SERVER_ERROR; 1063 } 1064 wpabuf_free(msg); 1065 *replyname = NULL; 1066 *reply = NULL; 1067 return HTTP_OK; 1068} 1069 1070 1071static enum http_reply_code 1072web_process_reboot_ap(struct upnp_wps_device_sm *sm, char *data, 1073 struct wpabuf **reply, const char **replyname) 1074{ 1075 struct wpabuf *msg; 1076 enum http_reply_code ret; 1077 1078 wpa_printf(MSG_DEBUG, "WPS UPnP: RebootAP"); 1079 msg = web_get_item(data, "NewAPSettings", &ret); 1080 if (msg == NULL) 1081 return ret; 1082 if (!sm->ctx->rx_req_reboot_ap || 1083 sm->ctx->rx_req_reboot_ap(sm->priv, msg)) { 1084 wpabuf_free(msg); 1085 return HTTP_INTERNAL_SERVER_ERROR; 1086 } 1087 wpabuf_free(msg); 1088 *replyname = NULL; 1089 *reply = NULL; 1090 return HTTP_OK; 1091} 1092 1093 1094static enum http_reply_code 1095web_process_reset_ap(struct upnp_wps_device_sm *sm, char *data, 1096 struct wpabuf **reply, const char **replyname) 1097{ 1098 struct wpabuf *msg; 1099 enum http_reply_code ret; 1100 1101 wpa_printf(MSG_DEBUG, "WPS UPnP: ResetAP"); 1102 msg = web_get_item(data, "NewMessage", &ret); 1103 if (msg == NULL) 1104 return ret; 1105 if (!sm->ctx->rx_req_reset_ap || 1106 sm->ctx->rx_req_reset_ap(sm->priv, msg)) { 1107 wpabuf_free(msg); 1108 return HTTP_INTERNAL_SERVER_ERROR; 1109 } 1110 wpabuf_free(msg); 1111 *replyname = NULL; 1112 *reply = NULL; 1113 return HTTP_OK; 1114} 1115 1116 1117static enum http_reply_code 1118web_process_reboot_sta(struct upnp_wps_device_sm *sm, char *data, 1119 struct wpabuf **reply, const char **replyname) 1120{ 1121 struct wpabuf *msg; 1122 enum http_reply_code ret; 1123 1124 wpa_printf(MSG_DEBUG, "WPS UPnP: RebootSTA"); 1125 msg = web_get_item(data, "NewSTASettings", &ret); 1126 if (msg == NULL) 1127 return ret; 1128 if (!sm->ctx->rx_req_reboot_sta || 1129 sm->ctx->rx_req_reboot_sta(sm->priv, msg)) { 1130 wpabuf_free(msg); 1131 return HTTP_INTERNAL_SERVER_ERROR; 1132 } 1133 wpabuf_free(msg); 1134 *replyname = NULL; 1135 *reply = NULL; 1136 return HTTP_OK; 1137} 1138 1139 1140static enum http_reply_code 1141web_process_reset_sta(struct upnp_wps_device_sm *sm, char *data, 1142 struct wpabuf **reply, const char **replyname) 1143{ 1144 struct wpabuf *msg; 1145 enum http_reply_code ret; 1146 1147 wpa_printf(MSG_DEBUG, "WPS UPnP: ResetSTA"); 1148 msg = web_get_item(data, "NewMessage", &ret); 1149 if (msg == NULL) 1150 return ret; 1151 if (!sm->ctx->rx_req_reset_sta || 1152 sm->ctx->rx_req_reset_sta(sm->priv, msg)) { 1153 wpabuf_free(msg); 1154 return HTTP_INTERNAL_SERVER_ERROR; 1155 } 1156 wpabuf_free(msg); 1157 *replyname = NULL; 1158 *reply = NULL; 1159 return HTTP_OK; 1160} 1161 1162 1163static const char *soap_prefix = 1164 "<?xml version=\"1.0\"?>\n" 1165 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " 1166 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" 1167 "<s:Body>\n"; 1168static const char *soap_postfix = 1169 "</s:Body>\n</s:Envelope>\n"; 1170 1171static const char *soap_error_prefix = 1172 "<s:Fault>\n" 1173 "<faultcode>s:Client</faultcode>\n" 1174 "<faultstring>UPnPError</faultstring>\n" 1175 "<detail>\n" 1176 "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n"; 1177static const char *soap_error_postfix = 1178 "<errorDescription>Error</errorDescription>\n" 1179 "</UPnPError>\n" 1180 "</detail>\n" 1181 "</s:Fault>\n"; 1182 1183static void web_connection_send_reply(struct web_connection *c, 1184 enum http_reply_code ret, 1185 const char *action, int action_len, 1186 const struct wpabuf *reply, 1187 const char *replyname) 1188{ 1189 struct wpabuf *buf; 1190 char *replydata; 1191 char *put_length_here = NULL; 1192 char *body_start = NULL; 1193 1194 if (reply) { 1195 size_t len; 1196 replydata = (char *) base64_encode(wpabuf_head(reply), 1197 wpabuf_len(reply), &len); 1198 } else 1199 replydata = NULL; 1200 1201 /* Parameters of the response: 1202 * action(action_len) -- action we are responding to 1203 * replyname -- a name we need for the reply 1204 * replydata -- NULL or null-terminated string 1205 */ 1206 buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) + 1207 (action_len > 0 ? action_len * 2 : 0)); 1208 if (buf == NULL) { 1209 wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to " 1210 "POST"); 1211 wpabuf_free(buf); 1212 os_free(replydata); 1213 return; 1214 } 1215 1216 /* 1217 * Assuming we will be successful, put in the output header first. 1218 * Note: we do not keep connections alive (and httpread does 1219 * not support it)... therefore we must have Connection: close. 1220 */ 1221 if (ret == HTTP_OK) { 1222 wpabuf_put_str(buf, 1223 "HTTP/1.1 200 OK\r\n" 1224 "Content-Type: text/xml; " 1225 "charset=\"utf-8\"\r\n"); 1226 } else { 1227 wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret); 1228 } 1229 wpabuf_put_str(buf, http_connection_close); 1230 1231 wpabuf_put_str(buf, "Content-Length: "); 1232 /* 1233 * We will paste the length in later, leaving some extra whitespace. 1234 * HTTP code is supposed to be tolerant of extra whitespace. 1235 */ 1236 put_length_here = wpabuf_put(buf, 0); 1237 wpabuf_put_str(buf, " \r\n"); 1238 1239 http_put_date(buf); 1240 1241 /* terminating empty line */ 1242 wpabuf_put_str(buf, "\r\n"); 1243 1244 body_start = wpabuf_put(buf, 0); 1245 1246 if (ret == HTTP_OK) { 1247 wpabuf_put_str(buf, soap_prefix); 1248 wpabuf_put_str(buf, "<u:"); 1249 wpabuf_put_data(buf, action, action_len); 1250 wpabuf_put_str(buf, "Response xmlns:u=\""); 1251 wpabuf_put_str(buf, urn_wfawlanconfig); 1252 wpabuf_put_str(buf, "\">\n"); 1253 if (replydata && replyname) { 1254 /* TODO: might possibly need to escape part of reply 1255 * data? ... 1256 * probably not, unlikely to have ampersand(&) or left 1257 * angle bracket (<) in it... 1258 */ 1259 wpabuf_printf(buf, "<%s>", replyname); 1260 wpabuf_put_str(buf, replydata); 1261 wpabuf_printf(buf, "</%s>\n", replyname); 1262 } 1263 wpabuf_put_str(buf, "</u:"); 1264 wpabuf_put_data(buf, action, action_len); 1265 wpabuf_put_str(buf, "Response>\n"); 1266 wpabuf_put_str(buf, soap_postfix); 1267 } else { 1268 /* Error case */ 1269 wpabuf_put_str(buf, soap_prefix); 1270 wpabuf_put_str(buf, soap_error_prefix); 1271 wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret); 1272 wpabuf_put_str(buf, soap_error_postfix); 1273 wpabuf_put_str(buf, soap_postfix); 1274 } 1275 os_free(replydata); 1276 1277 /* Now patch in the content length at the end */ 1278 if (body_start && put_length_here) { 1279 int body_length = (char *) wpabuf_put(buf, 0) - body_start; 1280 char len_buf[10]; 1281 os_snprintf(len_buf, sizeof(len_buf), "%d", body_length); 1282 os_memcpy(put_length_here, len_buf, os_strlen(len_buf)); 1283 } 1284 1285 send_wpabuf(c->sd, buf); 1286 wpabuf_free(buf); 1287} 1288 1289 1290static const char * web_get_action(struct web_connection *c, 1291 const char *filename, size_t *action_len) 1292{ 1293 const char *match; 1294 int match_len; 1295 char *b; 1296 char *action; 1297 1298 *action_len = 0; 1299 if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) { 1300 wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s", 1301 filename); 1302 return NULL; 1303 } 1304 /* The SOAPAction line of the header tells us what we want to do */ 1305 b = httpread_hdr_line_get(c->hread, "SOAPAction:"); 1306 if (b == NULL) 1307 return NULL; 1308 if (*b == '"') 1309 b++; 1310 else 1311 return NULL; 1312 match = urn_wfawlanconfig; 1313 match_len = os_strlen(urn_wfawlanconfig) - 1; 1314 if (os_strncasecmp(b, match, match_len)) 1315 return NULL; 1316 b += match_len; 1317 /* skip over version */ 1318 while (isgraph(*b) && *b != '#') 1319 b++; 1320 if (*b != '#') 1321 return NULL; 1322 b++; 1323 /* Following the sharp(#) should be the action and a double quote */ 1324 action = b; 1325 while (isgraph(*b) && *b != '"') 1326 b++; 1327 if (*b != '"') 1328 return NULL; 1329 *action_len = b - action; 1330 return action; 1331} 1332 1333 1334/* Given that we have received a header w/ POST, act upon it 1335 * 1336 * Format of POST (case-insensitive): 1337 * 1338 * First line must be: 1339 * POST /<file> HTTP/1.1 1340 * Since we don't do anything fancy we just ignore other lines. 1341 * 1342 * Our response (if no error) which includes only required lines is: 1343 * HTTP/1.1 200 OK 1344 * Connection: close 1345 * Content-Type: text/xml 1346 * Date: <rfc1123-date> 1347 * 1348 * Header lines must end with \r\n 1349 * Per RFC 2616, content-length: is not required but connection:close 1350 * would appear to be required (given that we will be closing it!). 1351 */ 1352static void web_connection_parse_post(struct web_connection *c, 1353 const char *filename) 1354{ 1355 enum http_reply_code ret; 1356 struct upnp_wps_device_sm *sm = c->sm; 1357 char *data = httpread_data_get(c->hread); /* body of http msg */ 1358 const char *action; 1359 size_t action_len; 1360 const char *replyname = NULL; /* argument name for the reply */ 1361 struct wpabuf *reply = NULL; /* data for the reply */ 1362 1363 ret = UPNP_INVALID_ACTION; 1364 action = web_get_action(c, filename, &action_len); 1365 if (action == NULL) 1366 goto bad; 1367 1368 /* 1369 * There are quite a few possible actions. Although we appear to 1370 * support them all here, not all of them are necessarily supported by 1371 * callbacks at higher levels. 1372 */ 1373 if (!os_strncasecmp("GetDeviceInfo", action, action_len)) 1374 ret = web_process_get_device_info(sm, &reply, &replyname); 1375 else if (!os_strncasecmp("PutMessage", action, action_len)) 1376 ret = web_process_put_message(sm, data, &reply, &replyname); 1377 else if (!os_strncasecmp("GetAPSettings", action, action_len)) 1378 ret = web_process_get_ap_settings(sm, data, &reply, 1379 &replyname); 1380 else if (!os_strncasecmp("SetAPSettings", action, action_len)) 1381 ret = web_process_set_ap_settings(sm, data, &reply, 1382 &replyname); 1383 else if (!os_strncasecmp("DelAPSettings", action, action_len)) 1384 ret = web_process_del_ap_settings(sm, data, &reply, 1385 &replyname); 1386 else if (!os_strncasecmp("GetSTASettings", action, action_len)) 1387 ret = web_process_get_sta_settings(sm, data, &reply, 1388 &replyname); 1389 else if (!os_strncasecmp("SetSTASettings", action, action_len)) 1390 ret = web_process_set_sta_settings(sm, data, &reply, 1391 &replyname); 1392 else if (!os_strncasecmp("DelSTASettings", action, action_len)) 1393 ret = web_process_del_sta_settings(sm, data, &reply, 1394 &replyname); 1395 else if (!os_strncasecmp("PutWLANResponse", action, action_len)) 1396 ret = web_process_put_wlan_response(sm, data, &reply, 1397 &replyname); 1398 else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len)) 1399 ret = web_process_set_selected_registrar(sm, data, &reply, 1400 &replyname); 1401 else if (!os_strncasecmp("RebootAP", action, action_len)) 1402 ret = web_process_reboot_ap(sm, data, &reply, &replyname); 1403 else if (!os_strncasecmp("ResetAP", action, action_len)) 1404 ret = web_process_reset_ap(sm, data, &reply, &replyname); 1405 else if (!os_strncasecmp("RebootSTA", action, action_len)) 1406 ret = web_process_reboot_sta(sm, data, &reply, &replyname); 1407 else if (!os_strncasecmp("ResetSTA", action, action_len)) 1408 ret = web_process_reset_sta(sm, data, &reply, &replyname); 1409 else 1410 wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type"); 1411 1412bad: 1413 if (ret != HTTP_OK) 1414 wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret); 1415 web_connection_send_reply(c, ret, action, action_len, reply, 1416 replyname); 1417 wpabuf_free(reply); 1418} 1419 1420 1421/* Given that we have received a header w/ SUBSCRIBE, act upon it 1422 * 1423 * Format of SUBSCRIBE (case-insensitive): 1424 * 1425 * First line must be: 1426 * SUBSCRIBE /wps_event HTTP/1.1 1427 * 1428 * Our response (if no error) which includes only required lines is: 1429 * HTTP/1.1 200 OK 1430 * Server: xx, UPnP/1.0, xx 1431 * SID: uuid:xxxxxxxxx 1432 * Timeout: Second-<n> 1433 * Content-Length: 0 1434 * Date: xxxx 1435 * 1436 * Header lines must end with \r\n 1437 * Per RFC 2616, content-length: is not required but connection:close 1438 * would appear to be required (given that we will be closing it!). 1439 */ 1440static void web_connection_parse_subscribe(struct web_connection *c, 1441 const char *filename) 1442{ 1443 struct upnp_wps_device_sm *sm = c->sm; 1444 struct wpabuf *buf; 1445 char *b; 1446 char *hdr = httpread_hdr_get(c->hread); 1447 char *h; 1448 char *match; 1449 int match_len; 1450 char *end; 1451 int len; 1452 int got_nt = 0; 1453 u8 uuid[UUID_LEN]; 1454 int got_uuid = 0; 1455 char *callback_urls = NULL; 1456 struct subscription *s = NULL; 1457 enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR; 1458 1459 buf = wpabuf_alloc(1000); 1460 if (buf == NULL) 1461 return; 1462 1463 /* Parse/validate headers */ 1464 h = hdr; 1465 /* First line: SUBSCRIBE /wps_event HTTP/1.1 1466 * has already been parsed. 1467 */ 1468 if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) { 1469 ret = HTTP_PRECONDITION_FAILED; 1470 goto error; 1471 } 1472 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event"); 1473 end = os_strchr(h, '\n'); 1474 1475 for (; end != NULL; h = end + 1) { 1476 /* Option line by option line */ 1477 h = end + 1; 1478 end = os_strchr(h, '\n'); 1479 if (end == NULL) 1480 break; /* no unterminated lines allowed */ 1481 1482 /* NT assures that it is our type of subscription; 1483 * not used for a renewl. 1484 **/ 1485 match = "NT:"; 1486 match_len = os_strlen(match); 1487 if (os_strncasecmp(h, match, match_len) == 0) { 1488 h += match_len; 1489 while (*h == ' ' || *h == '\t') 1490 h++; 1491 match = "upnp:event"; 1492 match_len = os_strlen(match); 1493 if (os_strncasecmp(h, match, match_len) != 0) { 1494 ret = HTTP_BAD_REQUEST; 1495 goto error; 1496 } 1497 got_nt = 1; 1498 continue; 1499 } 1500 /* HOST should refer to us */ 1501#if 0 1502 match = "HOST:"; 1503 match_len = os_strlen(match); 1504 if (os_strncasecmp(h, match, match_len) == 0) { 1505 h += match_len; 1506 while (*h == ' ' || *h == '\t') 1507 h++; 1508 ..... 1509 } 1510#endif 1511 /* CALLBACK gives one or more URLs for NOTIFYs 1512 * to be sent as a result of the subscription. 1513 * Each URL is enclosed in angle brackets. 1514 */ 1515 match = "CALLBACK:"; 1516 match_len = os_strlen(match); 1517 if (os_strncasecmp(h, match, match_len) == 0) { 1518 h += match_len; 1519 while (*h == ' ' || *h == '\t') 1520 h++; 1521 len = end - h; 1522 os_free(callback_urls); 1523 callback_urls = os_malloc(len + 1); 1524 if (callback_urls == NULL) { 1525 ret = HTTP_INTERNAL_SERVER_ERROR; 1526 goto error; 1527 } 1528 os_memcpy(callback_urls, h, len); 1529 callback_urls[len] = 0; 1530 continue; 1531 } 1532 /* SID is only for renewal */ 1533 match = "SID:"; 1534 match_len = os_strlen(match); 1535 if (os_strncasecmp(h, match, match_len) == 0) { 1536 h += match_len; 1537 while (*h == ' ' || *h == '\t') 1538 h++; 1539 match = "uuid:"; 1540 match_len = os_strlen(match); 1541 if (os_strncasecmp(h, match, match_len) != 0) { 1542 ret = HTTP_BAD_REQUEST; 1543 goto error; 1544 } 1545 h += match_len; 1546 while (*h == ' ' || *h == '\t') 1547 h++; 1548 if (uuid_str2bin(h, uuid)) { 1549 ret = HTTP_BAD_REQUEST; 1550 goto error; 1551 } 1552 got_uuid = 1; 1553 continue; 1554 } 1555 /* TIMEOUT is requested timeout, but apparently we can 1556 * just ignore this. 1557 */ 1558 } 1559 1560 if (got_uuid) { 1561 /* renewal */ 1562 if (callback_urls) { 1563 ret = HTTP_BAD_REQUEST; 1564 goto error; 1565 } 1566 s = subscription_renew(sm, uuid); 1567 if (s == NULL) { 1568 ret = HTTP_PRECONDITION_FAILED; 1569 goto error; 1570 } 1571 } else if (callback_urls) { 1572 if (!got_nt) { 1573 ret = HTTP_PRECONDITION_FAILED; 1574 goto error; 1575 } 1576 s = subscription_start(sm, callback_urls); 1577 if (s == NULL) { 1578 ret = HTTP_INTERNAL_SERVER_ERROR; 1579 goto error; 1580 } 1581 } else { 1582 ret = HTTP_PRECONDITION_FAILED; 1583 goto error; 1584 } 1585 1586 /* success */ 1587 http_put_reply_code(buf, HTTP_OK); 1588 wpabuf_put_str(buf, http_server_hdr); 1589 wpabuf_put_str(buf, http_connection_close); 1590 wpabuf_put_str(buf, "Content-Length: 0\r\n"); 1591 wpabuf_put_str(buf, "SID: uuid:"); 1592 /* subscription id */ 1593 b = wpabuf_put(buf, 0); 1594 uuid_bin2str(s->uuid, b, 80); 1595 wpabuf_put(buf, os_strlen(b)); 1596 wpabuf_put_str(buf, "\r\n"); 1597 wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC); 1598 http_put_date(buf); 1599 /* And empty line to terminate header: */ 1600 wpabuf_put_str(buf, "\r\n"); 1601 1602 send_wpabuf(c->sd, buf); 1603 wpabuf_free(buf); 1604 os_free(callback_urls); 1605 return; 1606 1607error: 1608 /* Per UPnP spec: 1609 * Errors 1610 * Incompatible headers 1611 * 400 Bad Request. If SID header and one of NT or CALLBACK headers 1612 * are present, the publisher must respond with HTTP error 1613 * 400 Bad Request. 1614 * Missing or invalid CALLBACK 1615 * 412 Precondition Failed. If CALLBACK header is missing or does not 1616 * contain a valid HTTP URL, the publisher must respond with HTTP 1617 * error 412 Precondition Failed. 1618 * Invalid NT 1619 * 412 Precondition Failed. If NT header does not equal upnp:event, 1620 * the publisher must respond with HTTP error 412 Precondition 1621 * Failed. 1622 * [For resubscription, use 412 if unknown uuid]. 1623 * Unable to accept subscription 1624 * 5xx. If a publisher is not able to accept a subscription (such as 1625 * due to insufficient resources), it must respond with a 1626 * HTTP 500-series error code. 1627 * 599 Too many subscriptions (not a standard HTTP error) 1628 */ 1629 http_put_empty(buf, ret); 1630 send_wpabuf(c->sd, buf); 1631 wpabuf_free(buf); 1632 os_free(callback_urls); 1633} 1634 1635 1636/* Given that we have received a header w/ UNSUBSCRIBE, act upon it 1637 * 1638 * Format of UNSUBSCRIBE (case-insensitive): 1639 * 1640 * First line must be: 1641 * UNSUBSCRIBE /wps_event HTTP/1.1 1642 * 1643 * Our response (if no error) which includes only required lines is: 1644 * HTTP/1.1 200 OK 1645 * Content-Length: 0 1646 * 1647 * Header lines must end with \r\n 1648 * Per RFC 2616, content-length: is not required but connection:close 1649 * would appear to be required (given that we will be closing it!). 1650 */ 1651static void web_connection_parse_unsubscribe(struct web_connection *c, 1652 const char *filename) 1653{ 1654 struct upnp_wps_device_sm *sm = c->sm; 1655 struct wpabuf *buf; 1656 char *hdr = httpread_hdr_get(c->hread); 1657 char *h; 1658 char *match; 1659 int match_len; 1660 char *end; 1661 u8 uuid[UUID_LEN]; 1662 int got_uuid = 0; 1663 struct subscription *s = NULL; 1664 enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR; 1665 1666 /* Parse/validate headers */ 1667 h = hdr; 1668 /* First line: UNSUBSCRIBE /wps_event HTTP/1.1 1669 * has already been parsed. 1670 */ 1671 if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) { 1672 ret = HTTP_PRECONDITION_FAILED; 1673 goto send_msg; 1674 } 1675 wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event"); 1676 end = os_strchr(h, '\n'); 1677 1678 for (; end != NULL; h = end + 1) { 1679 /* Option line by option line */ 1680 h = end + 1; 1681 end = os_strchr(h, '\n'); 1682 if (end == NULL) 1683 break; /* no unterminated lines allowed */ 1684 1685 /* HOST should refer to us */ 1686#if 0 1687 match = "HOST:"; 1688 match_len = os_strlen(match); 1689 if (os_strncasecmp(h, match, match_len) == 0) { 1690 h += match_len; 1691 while (*h == ' ' || *h == '\t') 1692 h++; 1693 ..... 1694 } 1695#endif 1696 /* SID is only for renewal */ 1697 match = "SID:"; 1698 match_len = os_strlen(match); 1699 if (os_strncasecmp(h, match, match_len) == 0) { 1700 h += match_len; 1701 while (*h == ' ' || *h == '\t') 1702 h++; 1703 match = "uuid:"; 1704 match_len = os_strlen(match); 1705 if (os_strncasecmp(h, match, match_len) != 0) { 1706 ret = HTTP_BAD_REQUEST; 1707 goto send_msg; 1708 } 1709 h += match_len; 1710 while (*h == ' ' || *h == '\t') 1711 h++; 1712 if (uuid_str2bin(h, uuid)) { 1713 ret = HTTP_BAD_REQUEST; 1714 goto send_msg; 1715 } 1716 got_uuid = 1; 1717 continue; 1718 } 1719 } 1720 1721 if (got_uuid) { 1722 s = subscription_find(sm, uuid); 1723 if (s) { 1724 wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s", 1725 s, 1726 (s && s->addr_list && 1727 s->addr_list->domain_and_port) ? 1728 s->addr_list->domain_and_port : "-null-"); 1729 subscription_unlink(s); 1730 subscription_destroy(s); 1731 } 1732 } else { 1733 wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not " 1734 "found)"); 1735 ret = HTTP_PRECONDITION_FAILED; 1736 goto send_msg; 1737 } 1738 1739 ret = HTTP_OK; 1740 1741send_msg: 1742 buf = wpabuf_alloc(200); 1743 if (buf == NULL) 1744 return; 1745 http_put_empty(buf, ret); 1746 send_wpabuf(c->sd, buf); 1747 wpabuf_free(buf); 1748} 1749 1750 1751/* Send error in response to unknown requests */ 1752static void web_connection_unimplemented(struct web_connection *c) 1753{ 1754 struct wpabuf *buf; 1755 buf = wpabuf_alloc(200); 1756 if (buf == NULL) 1757 return; 1758 http_put_empty(buf, HTTP_UNIMPLEMENTED); 1759 send_wpabuf(c->sd, buf); 1760 wpabuf_free(buf); 1761} 1762 1763 1764 1765/* Called when we have gotten an apparently valid http request. 1766 */ 1767static void web_connection_check_data(struct web_connection *c) 1768{ 1769 struct httpread *hread = c->hread; 1770 enum httpread_hdr_type htype = httpread_hdr_type_get(hread); 1771 /* char *data = httpread_data_get(hread); */ 1772 char *filename = httpread_uri_get(hread); 1773 1774 c->done = 1; 1775 if (!filename) { 1776 wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI"); 1777 return; 1778 } 1779 /* Trim leading slashes from filename */ 1780 while (*filename == '/') 1781 filename++; 1782 1783 wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d", 1784 htype, inet_ntoa(c->cli_addr.sin_addr), 1785 htons(c->cli_addr.sin_port)); 1786 1787 switch (htype) { 1788 case HTTPREAD_HDR_TYPE_GET: 1789 web_connection_parse_get(c, filename); 1790 break; 1791 case HTTPREAD_HDR_TYPE_POST: 1792 web_connection_parse_post(c, filename); 1793 break; 1794 case HTTPREAD_HDR_TYPE_SUBSCRIBE: 1795 web_connection_parse_subscribe(c, filename); 1796 break; 1797 case HTTPREAD_HDR_TYPE_UNSUBSCRIBE: 1798 web_connection_parse_unsubscribe(c, filename); 1799 break; 1800 /* We are not required to support M-POST; just plain 1801 * POST is supposed to work, so we only support that. 1802 * If for some reason we need to support M-POST, it is 1803 * mostly the same as POST, with small differences. 1804 */ 1805 default: 1806 /* Send 501 for anything else */ 1807 web_connection_unimplemented(c); 1808 break; 1809 } 1810} 1811 1812 1813 1814/* called back when we have gotten request */ 1815static void web_connection_got_file_handler(struct httpread *handle, 1816 void *cookie, 1817 enum httpread_event en) 1818{ 1819 struct web_connection *c = cookie; 1820 1821 if (en == HTTPREAD_EVENT_FILE_READY) 1822 web_connection_check_data(c); 1823 web_connection_stop(c); 1824} 1825 1826 1827/* web_connection_start - Start web connection 1828 * @sm: WPS UPnP state machine from upnp_wps_device_init() 1829 * @sd: Socket descriptor 1830 * @addr: Client address 1831 * 1832 * The socket descriptor sd is handed over for ownership by the WPS UPnP 1833 * state machine. 1834 */ 1835static void web_connection_start(struct upnp_wps_device_sm *sm, 1836 int sd, struct sockaddr_in *addr) 1837{ 1838 struct web_connection *c = NULL; 1839 1840 /* if too many connections, bail */ 1841 if (sm->n_web_connections >= MAX_WEB_CONNECTIONS) { 1842 close(sd); 1843 return; 1844 } 1845 1846 c = os_zalloc(sizeof(*c)); 1847 if (c == NULL) 1848 return; 1849 os_memcpy(&c->cli_addr, addr, sizeof(c->cli_addr)); 1850 c->sm = sm; 1851 c->sd = sd; 1852#if 0 1853 /* 1854 * Setting non-blocking should not be necessary for read, and can mess 1855 * up sending where blocking might be better. 1856 */ 1857 if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) 1858 break; 1859#endif 1860 c->hread = httpread_create(c->sd, web_connection_got_file_handler, 1861 c /* cookie */, 1862 WEB_CONNECTION_MAX_READ, 1863 WEB_CONNECTION_TIMEOUT_SEC); 1864 if (c->hread == NULL) 1865 goto fail; 1866 if (sm->web_connections) { 1867 c->next = sm->web_connections; 1868 c->prev = c->next->prev; 1869 c->prev->next = c; 1870 c->next->prev = c; 1871 } else { 1872 sm->web_connections = c->next = c->prev = c; 1873 } 1874 sm->n_web_connections++; 1875 return; 1876 1877fail: 1878 if (c) 1879 web_connection_stop(c); 1880} 1881 1882 1883/* 1884 * Listening for web connections 1885 * We have a single TCP listening port, and hand off connections as we get 1886 * them. 1887 */ 1888 1889void web_listener_stop(struct upnp_wps_device_sm *sm) 1890{ 1891 if (sm->web_sd_registered) { 1892 sm->web_sd_registered = 0; 1893 eloop_unregister_sock(sm->web_sd, EVENT_TYPE_READ); 1894 } 1895 if (sm->web_sd >= 0) 1896 close(sm->web_sd); 1897 sm->web_sd = -1; 1898} 1899 1900 1901static void web_listener_handler(int sd, void *eloop_ctx, void *sock_ctx) 1902{ 1903 struct sockaddr_in addr; 1904 socklen_t addr_len = sizeof(addr); 1905 struct upnp_wps_device_sm *sm = sock_ctx; 1906 int new_sd; 1907 1908 /* Create state for new connection */ 1909 /* Remember so we can cancel if need be */ 1910 new_sd = accept(sm->web_sd, (struct sockaddr *) &addr, &addr_len); 1911 if (new_sd < 0) { 1912 wpa_printf(MSG_ERROR, "WPS UPnP: web listener accept " 1913 "errno=%d (%s) web_sd=%d", 1914 errno, strerror(errno), sm->web_sd); 1915 return; 1916 } 1917 web_connection_start(sm, new_sd, &addr); 1918} 1919 1920 1921int web_listener_start(struct upnp_wps_device_sm *sm) 1922{ 1923 struct sockaddr_in addr; 1924 int port; 1925 1926 sm->web_sd = socket(AF_INET, SOCK_STREAM, 0); 1927 if (sm->web_sd < 0) 1928 goto fail; 1929 if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0) 1930 goto fail; 1931 port = 49152; /* first non-reserved port */ 1932 for (;;) { 1933 os_memset(&addr, 0, sizeof(addr)); 1934 addr.sin_family = AF_INET; 1935 addr.sin_addr.s_addr = sm->ip_addr; 1936 addr.sin_port = htons(port); 1937 if (bind(sm->web_sd, (struct sockaddr *) &addr, 1938 sizeof(addr)) == 0) 1939 break; 1940 if (errno == EADDRINUSE) { 1941 /* search for unused port */ 1942 if (++port == 65535) 1943 goto fail; 1944 continue; 1945 } 1946 goto fail; 1947 } 1948 if (listen(sm->web_sd, 10 /* max backlog */) != 0) 1949 goto fail; 1950 if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0) 1951 goto fail; 1952 if (eloop_register_sock(sm->web_sd, EVENT_TYPE_READ, 1953 web_listener_handler, NULL, sm)) 1954 goto fail; 1955 sm->web_sd_registered = 1; 1956 sm->web_port = port; 1957 1958 return 0; 1959 1960fail: 1961 /* Error */ 1962 web_listener_stop(sm); 1963 return -1; 1964} 1965