Exemple #1
0
char *pr_netio_telnet_gets(char *buf, size_t buflen,
    pr_netio_stream_t *in_nstrm, pr_netio_stream_t *out_nstrm) {
  char *bp = buf;
  unsigned char cp;
  int toread, handle_iac = TRUE, saw_newline = FALSE;
  pr_buffer_t *pbuf = NULL;

  if (buflen == 0 ||
      in_nstrm == NULL ||
      out_nstrm == NULL) {
    errno = EINVAL;
    return NULL;
  }

#ifdef PR_USE_NLS
  handle_iac = pr_encode_supports_telnet_iac();
#endif /* PR_USE_NLS */

  buflen--;

  if (in_nstrm->strm_buf) {
    pbuf = in_nstrm->strm_buf;

  } else {
    pbuf = pr_netio_buffer_alloc(in_nstrm);
  }

  while (buflen > 0) {
    pr_signals_handle();

    /* Is the buffer empty? */
    if (pbuf->current == NULL ||
        pbuf->remaining == pbuf->buflen) {

      toread = pr_netio_read(in_nstrm, pbuf->buf,
        (buflen < pbuf->buflen ? buflen : pbuf->buflen), 1);

      if (toread <= 0) {
        if (bp != buf) {
          *bp = '\0';
          return buf;
        }

        return NULL;
      }

      pbuf->remaining = pbuf->buflen - toread;
      pbuf->current = pbuf->buf;

      /* Before we begin iterating through the data read in from the
       * network, handing any Telnet characters and such, generate an event
       * for any listeners which may want to examine this data as well.
       */
      pr_event_generate("core.ctrl-read", pbuf);
    }

    toread = pbuf->buflen - pbuf->remaining;

    while (buflen > 0 &&
           toread > 0 &&
           *pbuf->current != '\n' &&
           toread--) {
      pr_signals_handle();

      cp = *pbuf->current++;
      pbuf->remaining++;

      if (handle_iac == TRUE) {
        switch (telnet_mode) {
          case TELNET_IAC:
            switch (cp) {
              case TELNET_WILL:
              case TELNET_WONT:
              case TELNET_DO:
              case TELNET_DONT:
              case TELNET_IP:
              case TELNET_DM:
                /* Why do we do this crazy thing where we set the "telnet mode"
                 * to be the action, and let the while loop, on the next pass,
                 * handle that action?  It's because we don't know, right now,
                 * whether there actually a "next byte" in the input buffer.
                 * There _should_ be -- but we can't be sure.  And that next
                 * byte is needed for properly responding with WONT/DONT
                 * responses.
                 */
                telnet_mode = cp;
                continue;

              case TELNET_IAC:
                /* In this case, we know that the previous byte was TELNET_IAC,
                 * and that the current byte is another TELNET_IAC.  The
                 * first TELNET_IAC thus "escapes" the second, telling us
                 * that the current byte (TELNET_IAC) should be written out
                 * as is (Bug#3697).
                 */
                telnet_mode = 0;
                break;

              default:
                /* In this case, we know that the previous byte was TELNET_IAC,
                 * but the current byte is not a value we care about.  So
                 * write the TELNET_IAC into the output buffer, break out of
                 * of the switch, and let that handle the writing of the
                 * current byte into the output buffer.
                 */
                *bp++ = TELNET_IAC;
                buflen--;

                telnet_mode = 0;
                break;
            }
            break;

          case TELNET_WILL:
          case TELNET_WONT:
            pr_netio_printf(out_nstrm, "%c%c%c", TELNET_IAC, TELNET_DONT, cp);
            telnet_mode = 0;
            continue;

          case TELNET_DO:
          case TELNET_DONT:
            pr_netio_printf(out_nstrm, "%c%c%c", TELNET_IAC, TELNET_WONT, cp);
            telnet_mode = 0;
            continue;

          case TELNET_IP:
          case TELNET_DM:
          default:
            if (cp == TELNET_IAC) {
              telnet_mode = cp;
              continue;
            }
            break;
        }
      }

      /* In the situation where the previous byte was an IAC, we wrote IAC
       * into the output buffer, and decremented buflen (size of the output
       * buffer remaining).  Thus we need to check here if buflen is zero,
       * before trying to decrement buflen again (and possibly underflowing
       * the buflen size_t data type).
       */
      if (buflen == 0) {
        break;
      }

      *bp++ = cp;
      buflen--;
    }

    if (buflen > 0 &&
        toread > 0 &&
        *pbuf->current == '\n') {
      buflen--;
      toread--;
      *bp++ = *pbuf->current++;
      pbuf->remaining++;

      saw_newline = TRUE;
      break;
    }

    if (toread == 0) {
      /* No more input?  Set pbuf->current to null, so that at the top of
       * the loop, we read more.
       */
      pbuf->current = NULL;
    }
  }

  if (!saw_newline) {
    /* If we haven't seen a newline, then assume the client is deliberately
     * sending a too-long command, trying to exploit buffer sizes and make
     * the server make some possibly bad assumptions.
     */

    properly_terminated_prev_command = FALSE;
    errno = E2BIG;
    return NULL;
  }

  if (!properly_terminated_prev_command) {
    properly_terminated_prev_command = TRUE;
    pr_log_pri(PR_LOG_NOTICE, "client sent too-long command, ignoring");
    errno = E2BIG;
    return NULL;
  }

  properly_terminated_prev_command = TRUE;
  *bp = '\0';
  return buf;
}
Exemple #2
0
static char *ftp_telnet_gets(char *buf, size_t buflen,
                             pr_netio_stream_t *nstrm, conn_t *conn) {
    char *buf_ptr = buf;
    unsigned char cp;
    int nread, saw_newline = FALSE;
    pr_buffer_t *pbuf = NULL;

    if (buflen == 0) {
        errno = EINVAL;
        return NULL;
    }

    buflen--;

    if (nstrm->strm_buf != NULL) {
        pbuf = nstrm->strm_buf;

    } else {
        pbuf = pr_netio_buffer_alloc(nstrm);
    }

    while (buflen > 0) {
        /* Is the buffer empty? */
        if (pbuf->current == NULL ||
                pbuf->remaining == pbuf->buflen) {

            nread = proxy_netio_read(nstrm, pbuf->buf,
                                     (buflen < pbuf->buflen ? buflen : pbuf->buflen), 4);
            if (nread <= 0) {
                if (buf_ptr != buf) {
                    *buf_ptr = '\0';
                    return buf;
                }

                if (nread == 0) {
                    (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
                                            "read EOF from %s", conn->remote_name);
                    errno = EPERM;
                }

                return NULL;
            }

            pbuf->remaining = pbuf->buflen - nread;
            pbuf->current = pbuf->buf;

            pr_event_generate("mod_proxy.ctrl-read", pbuf);
        }

        nread = pbuf->buflen - pbuf->remaining;

        /* Expensive copying of bytes while we look for the trailing LF. */
        while (buflen > 0 &&
                nread > 0 &&
                *pbuf->current != '\n' &&
                nread--) {
            pr_signals_handle();

            cp = *pbuf->current++;
            pbuf->remaining++;
            *buf_ptr++ = cp;
            buflen--;
        }

        if (buflen > 0 &&
                nread > 0 &&
                *pbuf->current == '\n') {
            buflen--;
            nread--;
            *buf_ptr++ = *pbuf->current++;
            pbuf->remaining++;

            saw_newline = TRUE;
            break;
        }

        if (nread == 0) {
            pbuf->current = NULL;
        }
    }

    if (saw_newline == FALSE) {
        /* If we haven't seen a newline, then assume the server is deliberately
         * sending a too-long response, trying to exploit buffer sizes and make
         * the proxy make some possibly bad assumptions.
         */

        errno = E2BIG;
        return NULL;
    }

    *buf_ptr = '\0';
    return buf;
}
Exemple #3
0
char *pr_netio_gets(char *buf, size_t buflen, pr_netio_stream_t *nstrm) {
  char *bp = buf;
  int toread;
  pr_buffer_t *pbuf = NULL;

  if (buflen == 0) {
    errno = EINVAL;
    return NULL;
  }

  buflen--;

  if (nstrm->strm_buf) {
    pbuf = nstrm->strm_buf;

  } else {
    pbuf = pr_netio_buffer_alloc(nstrm);
  }

  while (buflen) {

    /* Is the buffer empty? */
    if (!pbuf->current ||
        pbuf->remaining == pbuf->buflen) {

      toread = pr_netio_read(nstrm, pbuf->buf,
        (buflen < pbuf->buflen ?  buflen : pbuf->buflen), 1);

      if (toread <= 0) {
        if (bp != buf) {
          *bp = '\0';
          return buf;

        } else
          return NULL;
      }

      pbuf->remaining = pbuf->buflen - toread;
      pbuf->current = pbuf->buf;

      pbuf->remaining = pbuf->buflen - toread;
      pbuf->current = pbuf->buf;

      /* Before we begin iterating through the data read in from the
       * network, generate an event for any listeners which may want to
       * examine this data as well.
       */
      pr_event_generate("core.othr-read", pbuf);
    }

    toread = pbuf->buflen - pbuf->remaining;

    while (buflen && *pbuf->current != '\n' && toread--) {
      if (*pbuf->current & 0x80)
        pbuf->current++;

      else {
        *bp++ = *pbuf->current++;
        buflen--;
      }
      pbuf->remaining++;
    }

    if (buflen && toread && *pbuf->current == '\n') {
      buflen--;
      toread--;
      *bp++ = *pbuf->current++;
      pbuf->remaining++;
      break;
    }

    if (!toread)
      pbuf->current = NULL;
  }

  *bp = '\0';
  return buf;
}