Exemplo n.º 1
0
  /**
   * Parses a cookie header after the initial "Cookie:" [WS][$]token[WS]=[WS](token|QV)[;|,] RFC
   * 2965 JVK
   */
  protected final void processCookieHeader(byte bytes[], int off, int len) {
    if (len <= 0 || bytes == null) {
      return;
    }
    int end = off + len;
    int pos = off;
    int nameStart = 0;
    int nameEnd = 0;
    int valueStart = 0;
    int valueEnd = 0;
    int version = 0;
    ServerCookie sc = null;
    boolean isSpecial;
    boolean isQuoted;

    while (pos < end) {
      isSpecial = false;
      isQuoted = false;

      // Skip whitespace and non-token characters (separators)
      while (pos < end
          && (CookieSupport.isHttpSeparator((char) bytes[pos])
                  && !CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0
              || CookieSupport.isV0Separator((char) bytes[pos])
              || isWhiteSpace(bytes[pos]))) {
        pos++;
      }

      if (pos >= end) {
        return;
      }

      // Detect Special cookies
      if (bytes[pos] == '$') {
        isSpecial = true;
        pos++;
      }

      // Get the cookie/attribute name. This must be a token
      valueEnd = valueStart = nameStart = pos;
      pos = nameEnd = getTokenEndPosition(bytes, pos, end, version, true);

      // Skip whitespace
      while (pos < end && isWhiteSpace(bytes[pos])) {
        pos++;
      }

      // Check for an '=' -- This could also be a name-only
      // cookie at the end of the cookie header, so if we
      // are past the end of the header, but we have a name
      // skip to the name-only part.
      if (pos < (end - 1) && bytes[pos] == '=') {

        // Skip whitespace
        do {
          pos++;
        } while (pos < end && isWhiteSpace(bytes[pos]));

        if (pos >= end) {
          return;
        }

        // Determine what type of value this is, quoted value,
        // token, name-only with an '=', or other (bad)
        switch (bytes[pos]) {
          case '"': // Quoted Value
            isQuoted = true;
            valueStart = pos + 1; // strip "
            // getQuotedValue returns the position before
            // at the last quote. This must be dealt with
            // when the bytes are copied into the cookie
            valueEnd = getQuotedValueEndPosition(bytes, valueStart, end);
            // We need pos to advance
            pos = valueEnd;
            // Handles cases where the quoted value is
            // unterminated and at the end of the header,
            // e.g. [myname="value]
            if (pos >= end) {
              return;
            }
            break;
          case ';':
          case ',':
            // Name-only cookie with an '=' after the name token
            // This may not be RFC compliant
            valueStart = valueEnd = -1;
            // The position is OK (On a delimiter)
            break;
          default:
            if (version == 0
                    && !CookieSupport.isV0Separator((char) bytes[pos])
                    && CookieSupport.ALLOW_HTTP_SEPARATORS_IN_V0
                || !CookieSupport.isHttpSeparator((char) bytes[pos])
                || bytes[pos] == '=' && CookieSupport.ALLOW_EQUALS_IN_VALUE) {
              // Token
              valueStart = pos;
              // getToken returns the position at the delimiter
              // or other non-token character
              valueEnd = getTokenEndPosition(bytes, valueStart, end, version, false);
              // We need pos to advance
              pos = valueEnd;
            } else {
              // INVALID COOKIE, advance to next delimiter
              // The starting character of the cookie value was
              // not valid.
              UserDataHelper.Mode logMode = userDataLog.getNextMode();
              if (logMode != null) {
                String message = sm.getString("cookies.invalidCookieToken");
                switch (logMode) {
                  case INFO_THEN_DEBUG:
                    message += sm.getString("cookies.fallToDebug");
                    // $FALL-THROUGH$
                  case INFO:
                    log.info(message);
                    break;
                  case DEBUG:
                    log.debug(message);
                }
              }
              while (pos < end && bytes[pos] != ';' && bytes[pos] != ',') {
                pos++;
              }
              pos++;
              // Make sure no special avpairs can be attributed to
              // the previous cookie by setting the current cookie
              // to null
              sc = null;
              continue;
            }
        }
      } else {
        // Name only cookie
        valueStart = valueEnd = -1;
        pos = nameEnd;
      }

      // We should have an avpair or name-only cookie at this
      // point. Perform some basic checks to make sure we are
      // in a good state.

      // Skip whitespace
      while (pos < end && isWhiteSpace(bytes[pos])) {
        pos++;
      }

      // Make sure that after the cookie we have a separator. This
      // is only important if this is not the last cookie pair
      while (pos < end && bytes[pos] != ';' && bytes[pos] != ',') {
        pos++;
      }

      pos++;

      // All checks passed. Add the cookie, start with the
      // special avpairs first
      if (isSpecial) {
        isSpecial = false;
        // $Version must be the first avpair in the cookie header
        // (sc must be null)
        if (equals("Version", bytes, nameStart, nameEnd) && sc == null) {
          // Set version
          if (bytes[valueStart] == '1' && valueEnd == (valueStart + 1)) {
            version = 1;
          } else {
            // unknown version (Versioning is not very strict)
          }
          continue;
        }

        // We need an active cookie for Path/Port/etc.
        if (sc == null) {
          continue;
        }

        // Domain is more common, so it goes first
        if (equals("Domain", bytes, nameStart, nameEnd)) {
          sc.getDomain().setBytes(bytes, valueStart, valueEnd - valueStart);
          continue;
        }

        if (equals("Path", bytes, nameStart, nameEnd)) {
          sc.getPath().setBytes(bytes, valueStart, valueEnd - valueStart);
          continue;
        }

        // v2 cookie attributes - skip them
        if (equals("Port", bytes, nameStart, nameEnd)) {
          continue;
        }
        if (equals("CommentURL", bytes, nameStart, nameEnd)) {
          continue;
        }

        // Unknown cookie, complain
        UserDataHelper.Mode logMode = userDataLog.getNextMode();
        if (logMode != null) {
          String message = sm.getString("cookies.invalidSpecial");
          switch (logMode) {
            case INFO_THEN_DEBUG:
              message += sm.getString("cookies.fallToDebug");
              // $FALL-THROUGH$
            case INFO:
              log.info(message);
              break;
            case DEBUG:
              log.debug(message);
          }
        }
      } else { // Normal Cookie
        if (valueStart == -1 && !CookieSupport.ALLOW_NAME_ONLY) {
          // Skip name only cookies if not supported
          continue;
        }

        sc = addCookie();
        sc.setVersion(version);
        sc.getName().setBytes(bytes, nameStart, nameEnd - nameStart);

        if (valueStart != -1) { // Normal AVPair
          sc.getValue().setBytes(bytes, valueStart, valueEnd - valueStart);
          if (isQuoted) {
            // We know this is a byte value so this is safe
            unescapeDoubleQuotes(sc.getValue().getByteChunk());
          }
        } else {
          // Name Only
          sc.getValue().setString("");
        }
        continue;
      }
    }
  }
Exemplo n.º 2
0
  private void processParameters(byte bytes[], int start, int len, Charset charset) {

    if (log.isDebugEnabled()) {
      log.debug(sm.getString("parameters.bytes", new String(bytes, start, len, DEFAULT_CHARSET)));
    }

    int decodeFailCount = 0;

    int pos = start;
    int end = start + len;

    while (pos < end) {
      int nameStart = pos;
      int nameEnd = -1;
      int valueStart = -1;
      int valueEnd = -1;

      boolean parsingName = true;
      boolean decodeName = false;
      boolean decodeValue = false;
      boolean parameterComplete = false;

      do {
        switch (bytes[pos]) {
          case '=':
            if (parsingName) {
              // Name finished. Value starts from next character
              nameEnd = pos;
              parsingName = false;
              valueStart = ++pos;
            } else {
              // Equals character in value
              pos++;
            }
            break;
          case '&':
            if (parsingName) {
              // Name finished. No value.
              nameEnd = pos;
            } else {
              // Value finished
              valueEnd = pos;
            }
            parameterComplete = true;
            pos++;
            break;
          case '%':
          case '+':
            // Decoding required
            if (parsingName) {
              decodeName = true;
            } else {
              decodeValue = true;
            }
            pos++;
            break;
          default:
            pos++;
            break;
        }
      } while (!parameterComplete && pos < end);

      if (pos == end) {
        if (nameEnd == -1) {
          nameEnd = pos;
        } else if (valueStart > -1 && valueEnd == -1) {
          valueEnd = pos;
        }
      }

      if (log.isDebugEnabled() && valueStart == -1) {
        log.debug(
            sm.getString(
                "parameters.noequal",
                Integer.valueOf(nameStart),
                Integer.valueOf(nameEnd),
                new String(bytes, nameStart, nameEnd - nameStart, DEFAULT_CHARSET)));
      }

      if (nameEnd <= nameStart) {
        if (valueStart == -1) {
          // &&
          if (log.isDebugEnabled()) {
            log.debug(sm.getString("parameters.emptyChunk"));
          }
          // Do not flag as error
          continue;
        }
        // &=foo&
        UserDataHelper.Mode logMode = userDataLog.getNextMode();
        if (logMode != null) {
          String extract;
          if (valueEnd > nameStart) {
            extract = new String(bytes, nameStart, valueEnd - nameStart, DEFAULT_CHARSET);
          } else {
            extract = "";
          }
          String message =
              sm.getString(
                  "parameters.invalidChunk",
                  Integer.valueOf(nameStart),
                  Integer.valueOf(valueEnd),
                  extract);
          switch (logMode) {
            case INFO_THEN_DEBUG:
              message += sm.getString("parameters.fallToDebug");
              // $FALL-THROUGH$
            case INFO:
              log.info(message);
              break;
            case DEBUG:
              log.debug(message);
          }
        }
        parseFailed = true;
        continue;
        // invalid chunk - it's better to ignore
      }

      tmpName.setBytes(bytes, nameStart, nameEnd - nameStart);
      if (valueStart >= 0) {
        tmpValue.setBytes(bytes, valueStart, valueEnd - valueStart);
      } else {
        tmpValue.setBytes(bytes, 0, 0);
      }

      // Take copies as if anything goes wrong originals will be
      // corrupted. This means original values can be logged.
      // For performance - only done for debug
      if (log.isDebugEnabled()) {
        try {
          origName.append(bytes, nameStart, nameEnd - nameStart);
          if (valueStart >= 0) {
            origValue.append(bytes, valueStart, valueEnd - valueStart);
          } else {
            origValue.append(bytes, 0, 0);
          }
        } catch (IOException ioe) {
          // Should never happen...
          log.error(sm.getString("parameters.copyFail"), ioe);
        }
      }

      try {
        String name;
        String value;

        if (decodeName) {
          urlDecode(tmpName);
        }
        tmpName.setCharset(charset);
        name = tmpName.toString();

        if (valueStart >= 0) {
          if (decodeValue) {
            urlDecode(tmpValue);
          }
          tmpValue.setCharset(charset);
          value = tmpValue.toString();
        } else {
          value = "";
        }

        try {
          addParameter(name, value);
        } catch (IllegalStateException ise) {
          // Hitting limit stops processing further params but does
          // not cause request to fail.
          parseFailed = true;
          UserDataHelper.Mode logMode = maxParamCountLog.getNextMode();
          if (logMode != null) {
            String message = ise.getMessage();
            switch (logMode) {
              case INFO_THEN_DEBUG:
                message += sm.getString("parameters.maxCountFail.fallToDebug");
                // $FALL-THROUGH$
              case INFO:
                log.info(message);
                break;
              case DEBUG:
                log.debug(message);
            }
          }
          break;
        }
      } catch (IOException e) {
        parseFailed = true;
        decodeFailCount++;
        if (decodeFailCount == 1 || log.isDebugEnabled()) {
          if (log.isDebugEnabled()) {
            log.debug(
                sm.getString(
                    "parameters.decodeFail.debug", origName.toString(), origValue.toString()),
                e);
          } else if (log.isInfoEnabled()) {
            UserDataHelper.Mode logMode = userDataLog.getNextMode();
            if (logMode != null) {
              String message =
                  sm.getString(
                      "parameters.decodeFail.info", tmpName.toString(), tmpValue.toString());
              switch (logMode) {
                case INFO_THEN_DEBUG:
                  message += sm.getString("parameters.fallToDebug");
                  // $FALL-THROUGH$
                case INFO:
                  log.info(message);
                  break;
                case DEBUG:
                  log.debug(message);
              }
            }
          }
        }
      }

      tmpName.recycle();
      tmpValue.recycle();
      // Only recycle copies if we used them
      if (log.isDebugEnabled()) {
        origName.recycle();
        origValue.recycle();
      }
    }

    if (decodeFailCount > 1 && !log.isDebugEnabled()) {
      UserDataHelper.Mode logMode = userDataLog.getNextMode();
      if (logMode != null) {
        String message =
            sm.getString("parameters.multipleDecodingFail", Integer.valueOf(decodeFailCount));
        switch (logMode) {
          case INFO_THEN_DEBUG:
            message += sm.getString("parameters.fallToDebug");
            // $FALL-THROUGH$
          case INFO:
            log.info(message);
            break;
          case DEBUG:
            log.debug(message);
        }
      }
    }
  }