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