1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/filter/gzip_header.h"
6 
7 #include <string.h>
8 
9 #include <algorithm>
10 
11 #include "base/check_op.h"
12 #include "third_party/zlib/zlib.h"
13 
14 namespace net {
15 
16 const uint8_t GZipHeader::magic[] = {0x1f, 0x8b};
17 
GZipHeader()18 GZipHeader::GZipHeader() {
19   Reset();
20 }
21 
22 GZipHeader::~GZipHeader() = default;
23 
Reset()24 void GZipHeader::Reset() {
25   state_        = IN_HEADER_ID1;
26   flags_        = 0;
27   extra_length_ = 0;
28 }
29 
ReadMore(const char * inbuf,int inbuf_len,const char ** header_end)30 GZipHeader::Status GZipHeader::ReadMore(const char* inbuf, int inbuf_len,
31                                         const char** header_end) {
32   DCHECK_GE(inbuf_len, 0);
33   const uint8_t* pos = reinterpret_cast<const uint8_t*>(inbuf);
34   const uint8_t* const end = pos + inbuf_len;
35 
36   while ( pos < end ) {
37     switch ( state_ ) {
38       case IN_HEADER_ID1:
39         if ( *pos != magic[0] )  return INVALID_HEADER;
40         pos++;
41         state_++;
42         break;
43       case IN_HEADER_ID2:
44         if ( *pos != magic[1] )  return INVALID_HEADER;
45         pos++;
46         state_++;
47         break;
48       case IN_HEADER_CM:
49         if ( *pos != Z_DEFLATED )  return INVALID_HEADER;
50         pos++;
51         state_++;
52         break;
53       case IN_HEADER_FLG:
54         flags_ = (*pos) & (FLAG_FHCRC | FLAG_FEXTRA |
55                            FLAG_FNAME | FLAG_FCOMMENT);
56         pos++;
57         state_++;
58         break;
59 
60       case IN_HEADER_MTIME_BYTE_0:
61         pos++;
62         state_++;
63         break;
64       case IN_HEADER_MTIME_BYTE_1:
65         pos++;
66         state_++;
67         break;
68       case IN_HEADER_MTIME_BYTE_2:
69         pos++;
70         state_++;
71         break;
72       case IN_HEADER_MTIME_BYTE_3:
73         pos++;
74         state_++;
75         break;
76 
77       case IN_HEADER_XFL:
78         pos++;
79         state_++;
80         break;
81 
82       case IN_HEADER_OS:
83         pos++;
84         state_++;
85         break;
86 
87       case IN_XLEN_BYTE_0:
88         if ( !(flags_ & FLAG_FEXTRA) ) {
89           state_ = IN_FNAME;
90           break;
91         }
92         // We have a two-byte little-endian length, followed by a
93         // field of that length.
94         extra_length_ = *pos;
95         pos++;
96         state_++;
97         break;
98       case IN_XLEN_BYTE_1:
99         extra_length_ += *pos << 8;
100         pos++;
101         state_++;
102         // We intentionally fall through, because if we have a
103         // zero-length FEXTRA, we want to check to notice that we're
104         // done reading the FEXTRA before we exit this loop...
105         FALLTHROUGH;
106 
107       case IN_FEXTRA: {
108         // Grab the rest of the bytes in the extra field, or as many
109         // of them as are actually present so far.
110         const uint16_t num_extra_bytes = static_cast<uint16_t>(
111             std::min(static_cast<ptrdiff_t>(extra_length_), (end - pos)));
112         pos += num_extra_bytes;
113         extra_length_ -= num_extra_bytes;
114         if ( extra_length_ == 0 ) {
115           state_ = IN_FNAME;   // advance when we've seen extra_length_ bytes
116           flags_ &= ~FLAG_FEXTRA;   // we're done with the FEXTRA stuff
117         }
118         break;
119       }
120 
121       case IN_FNAME:
122         if ( !(flags_ & FLAG_FNAME) ) {
123           state_ = IN_FCOMMENT;
124           break;
125         }
126         // See if we can find the end of the \0-terminated FNAME field.
127         pos = reinterpret_cast<const uint8_t*>(memchr(pos, '\0', (end - pos)));
128         if (pos != nullptr) {
129           pos++;  // advance past the '\0'
130           flags_ &= ~FLAG_FNAME;   // we're done with the FNAME stuff
131           state_ = IN_FCOMMENT;
132         } else {
133           pos = end;  // everything we have so far is part of the FNAME
134         }
135         break;
136 
137       case IN_FCOMMENT:
138         if ( !(flags_ & FLAG_FCOMMENT) ) {
139           state_ = IN_FHCRC_BYTE_0;
140           break;
141         }
142         // See if we can find the end of the \0-terminated FCOMMENT field.
143         pos = reinterpret_cast<const uint8_t*>(memchr(pos, '\0', (end - pos)));
144         if (pos != nullptr) {
145           pos++;  // advance past the '\0'
146           flags_ &= ~FLAG_FCOMMENT;   // we're done with the FCOMMENT stuff
147           state_ = IN_FHCRC_BYTE_0;
148         } else {
149           pos = end;  // everything we have so far is part of the FNAME
150         }
151         break;
152 
153       case IN_FHCRC_BYTE_0:
154         if ( !(flags_ & FLAG_FHCRC) ) {
155           state_ = IN_DONE;
156           break;
157         }
158         pos++;
159         state_++;
160         break;
161 
162       case IN_FHCRC_BYTE_1:
163         pos++;
164         flags_ &= ~FLAG_FHCRC;   // we're done with the FHCRC stuff
165         state_++;
166         break;
167 
168       case IN_DONE:
169         *header_end = reinterpret_cast<const char*>(pos);
170         return COMPLETE_HEADER;
171     }
172   }
173 
174   if ( (state_ > IN_HEADER_OS) && (flags_ == 0) ) {
175     *header_end = reinterpret_cast<const char*>(pos);
176     return COMPLETE_HEADER;
177   } else {
178     return INCOMPLETE_HEADER;
179   }
180 }
181 
182 }  // namespace net
183