1 /* Copyright (C) 2020 J.F.Dockes
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 *
6 * - Redistributions of source code must retain the above copyright notice,
7 * this list of conditions and the following disclaimer.
8 * - Redistributions in binary form must reproduce the above copyright notice,
9 * this list of conditions and the following disclaimer in the documentation
10 * and/or other materials provided with the distribution.
11 * - Neither name of Intel Corporation nor the names of its contributors
12 * may be used to endorse or promote products derived from this software
13 * without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 ******************************************************************************/
28
29 #include "config.h"
30
31 #include "ssdpparser.h"
32
33 #include <iostream>
34 #include <cstring>
35 #include <mutex>
36 #include <string>
37
38 #include "upnpdebug.h"
39 #include "UpnpGlobal.h"
40
41 #if defined(_WIN32) && defined(_MSC_VER)
42 #define strcasecmp _stricmp
43 #endif
44
45 #define CCRLF "\r\n"
46
47 static const char *notify_start = "NOTIFY * HTTP/1.1\r\n";
48 static const size_t notify_start_len = strlen(notify_start);
49 static const char *msearch_start = "M-SEARCH * HTTP/1.1\r\n";
50 static const size_t msearch_start_len = strlen(msearch_start);
51 static const char *response_start = "HTTP/1.1 200 OK\r\n";
52 static const size_t response_start_len = strlen(response_start);
53
54
trimright(char * cp,size_t len)55 void SSDPPacketParser::trimright(char *cp, size_t len) {
56 while (len > 0) {
57 if (cp[len-1] == ' ' || cp[len-1] == '\t') {
58 len--;
59 } else {
60 break;
61 }
62 }
63 cp[len] = 0;
64 }
65
dump(std::ostream & os) const66 void SSDPPacketParser::dump(std::ostream& os) const {
67 os <<
68 " bootid " << (bootid ? bootid : "(null)") <<
69 " configid " << (configid ? configid : "(null)") <<
70 " cache_control " << (cache_control ? cache_control : "(null)") <<
71 " date " << (date ? date : "(null)") <<
72 " ext " << (ext ? "true" : "false") <<
73 " host " << (host ? host : "(null)") <<
74 " location " << (location ? location : "(null)") <<
75 " man " << (man ? man : "(null)") <<
76 " method " << (method ? method : "(null)") <<
77 " mx " << (mx ? mx : "(null)") <<
78 " nt " << (nt ? nt : "(null)") <<
79 " nts " << (nts ? nts : "(null)") <<
80 " protocol " << (protocol ? protocol : "(null)") <<
81 " searchport " << (searchport ? searchport : "(null)") <<
82 " server " << (server ? server : "(null)") <<
83 " st " << (st ? st : "(null)") <<
84 " status " << (status ? status : "(null)") <<
85 " url " << (url ? url : "(null)") <<
86 " user_agent " << (user_agent ? user_agent : "(null)") <<
87 " usn " << (usn ? usn : "(null)") <<
88 " version " << (version ? version : "(null)") <<
89 std::endl;
90 }
91
parse()92 bool SSDPPacketParser::parse()
93 {
94 protocol = "HTTP";
95 version = "1.1";
96 char *cp;
97 if (!strncmp(m_packet, notify_start, notify_start_len)) {
98 method = "NOTIFY";
99 url = "*";
100 cp = m_packet + notify_start_len;
101 } else if (!strncmp(m_packet, msearch_start, msearch_start_len)) {
102 method = "M-SEARCH";
103 url = "*";
104 cp = m_packet + msearch_start_len;
105 } else if (!strncmp(m_packet, response_start, response_start_len)) {
106 isresponse = true;
107 status = "200";
108 cp = m_packet + response_start_len;
109 } else {
110 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
111 "SSDP parser: bad first line in [%s]\n", m_packet);
112 return false;
113 }
114
115
116 for (;;) {
117 char *nm = cp;
118 char *colon = strchr(cp, ':');
119 if (nullptr == colon) {
120 bool ret = strcmp(cp, "\r\n") == 0;
121 if (!ret) {
122 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__, "SSDP parser: "
123 "no empty line at end of packet: [%s]\n", cp);
124 }
125 return ret;
126 }
127
128 *colon = 0;
129 cp = colon + 1;
130 // Get rid of white space after colon.
131 while (*cp == ' ' || *cp == '\t') {
132 cp++;
133 }
134 char *eol = strstr(cp, "\r\n");
135 if (nullptr == eol) {
136 // This is an error
137 UpnpPrintf(UPNP_INFO, SSDP, __FILE__, __LINE__,
138 "SSDP parser: no EOL after: [%s]\n", cp);
139 break;
140 }
141 char *val = cp;
142 *eol = 0;
143 trimright(val, eol - val);
144 cp = eol + 2;
145
146 bool known{false};
147 switch (nm[0]) {
148 case 'b': case 'B':
149 if (!strcasecmp("BOOTID.UPNP.ORG", nm)) {
150 bootid = val; known = true;
151 }
152 break;
153 case 'c': case 'C':
154 if (!strcasecmp("CACHE-CONTROL", nm)) {
155 cache_control = val; known = true;
156 } else if (!strcasecmp("CONFIGID.UPNP.ORG", nm)) {
157 configid = val; known = true;
158 }
159 break;
160 case 'd': case 'D':
161 if (!strcasecmp("DATE", nm)) {
162 date = val; known = true;
163 }
164 break;
165 case 'e': case 'E':
166 if (!strcasecmp("EXT", nm)) {
167 ext = true; known = true;
168 }
169 break;
170 case 'h': case 'H':
171 if (!strcasecmp("HOST", nm)) {
172 host = val; known = true;
173 }
174 break;
175 case 'l': case 'L':
176 if (!strcasecmp("LOCATION", nm)) {
177 location = val; known = true;
178 }
179 break;
180 case 'm': case 'M':
181 if (!strcasecmp("MAN", nm)) {
182 man = val; known = true;
183 } else if (!strcasecmp("MX", nm)) {
184 mx = val; known = true;
185 }
186 break;
187 case 'n': case 'N':
188 if (!strcasecmp("NT", nm)) {
189 nt = val; known = true;
190 } else if (!strcasecmp("NTS", nm)) {
191 nts = val; known = true;
192 }
193 break;
194 case 's': case 'S':
195 if (!strcasecmp("SERVER", nm)) {
196 server = val; known = true;
197 } else if (!strcasecmp("ST", nm)) {
198 st = val; known = true;
199 } else if (!strcasecmp("SEARCHPORT.UPNP.ORG", nm)) {
200 searchport = val; known = true;
201 }
202 break;
203 case 'u': case 'U':
204 if (!strcasecmp("USER-AGENT", nm)) {
205 user_agent = val; known = true;
206 } else if (!strcasecmp("USN", nm)) {
207 usn = val; known = true;
208 }
209 break;
210 default:
211 break;
212 }
213 #if 0
214 {
215 static std::mutex mmut;
216 std::unique_lock<std::mutex> lock(mmut);
217 if (known) {
218 std::cerr << "NM [" << nm << "] VAL [" << val << "]\n";
219 } else {
220 std::cerr << "Unknown header name [" << nm
221 << "] in " << saved <<"\n";
222 }
223 }
224 #else
225 if (!known) {
226 UpnpPrintf(UPNP_ALL, SSDP, __FILE__, __LINE__,
227 "SSDP parser: unknown header name [%s]\n", nm);
228 }
229 #endif
230 }
231 return false;
232 }
233