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