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