示例#1
0
static int parse_version(char *version_str, unsigned int *version,
    unsigned int *version_status) {
  register unsigned int i;
  char c, *ptr, *tmp;
  int have_suffix = FALSE;
  size_t revision_len = 0;

  /* Parse the given version string.  We expect to see:
   *
   *  major[.minor[.revision[suffix]]]
   *
   * Examples:
   *
   *  "1"
   *  "1.3"
   *  "1.3.3"
   *  "1.3.3rc1"
   *  "1.3.3a"
   */

  /* Quick sanity check */
  if (!PR_ISDIGIT(version_str[0])) {
    return -1;
  }

  /* Parse the major number */
  ptr = strchr(version_str, '.');
  if (ptr) {
    *ptr = '\0';

    tmp = NULL;
    version[0] = (int) strtoul(version_str, &tmp, 10);
    if (tmp && *tmp) {
      *ptr = '.';
      return -1;
    }

    *ptr = '.';

    /* Make sure that there is a character following the period. */
    if (*(ptr + 1) != '\0') {
      version_str = ptr + 1;

    } else {
      return -1;
    }

  } else {
    tmp = NULL;
    version[0] = (int) strtoul(version_str, &tmp, 10);
    if (tmp && *tmp) {
      return -1;
    }

    return 0;
  }

  /* Parse the minor number */
  ptr = strchr(version_str, '.');
  if (ptr) {
    *ptr = '\0';

    tmp = NULL;
    version[1] = (int) strtoul(version_str, &tmp, 10);
    if (tmp && *tmp) {
      *ptr = '.';
      return -1;
    }

    *ptr = '.';

    /* Make sure that there is a character following the period. */
    if (*(ptr + 1) != '\0') {
      version_str = ptr + 1;

    } else {
      return -1;
    }

  } else {
    tmp = NULL;
    version[1] = (int) strtoul(version_str, &tmp, 10);
    if (tmp && *tmp) {
      return -1;
    }

    return 0;
  }

  /* Parse the revision number.  This is trickier, since we have to also
   * account for the suffix, and there is no delimiter between the revision
   * number and the suffix characters.
   *
   * We thus scan every character from here on out.  If they are all digits,
   * then it is a "stable" release (no suffix).  Otherwise, it's either an
   * RC release, a maintenance release, or it's a badly formatted version
   * string.
   */

  for (i = 0; i < strlen(version_str); i++) {
    if (!PR_ISDIGIT(version_str[i])) {
      if (i > 0) {
        have_suffix = TRUE;
        break;
      }

      /* Syntax error */
      return -1;

    } else {
      /* Keep track of the number of characters in the revision number; this
       * is handy for afterwards, assuming we do have a suffix.
       */
      revision_len++;
    }
  }

  if (!have_suffix) {
    tmp = NULL;
    version[2] = (int) strtoul(version_str, &tmp, 10);
    if (tmp && *tmp) {
      return -1;
    }

    /* Stable release */
    *version_status = IFVERSION_STATUS_STABLE;
    return 0;
  }

  ptr = version_str + revision_len;
  c = *ptr;
  *ptr = '\0';

  tmp = NULL;
  version[2] = (int) strtoul(version_str, &tmp, 10);

  if (tmp && *tmp) {
    *ptr = c;
    return -1;
  }

  *ptr = c;

  /* We already know, based on the suffix check, that there are characters
   * after the revision number.
   */
  version_str = ptr;

  /* If the next two characters are "rc" (case-insensitive) followed by
   * digits, it's an RC release.  (If there are no digits, it's a syntax
   * error.)
   *
   * If there only a single character left, it is a maintenance release.
   */

  if (strlen(version_str) == 1) {
    if (!PR_ISALPHA(version_str[0])) {
      /* Syntax error */
      return -1;
    }

    /* Maintenance release. */
    c = toupper(version_str[0]);
    *version_status = IFVERSION_STATUS_STABLE + (c - 'A');
    return 0;
  }

  if (strncasecmp(version_str, "rc", 2) != 0) {
    return -1;
  }

  /* RC release */

  *version_status = IFVERSION_STATUS_RC;

  if (strlen(version_str) == 2) {
    return 0;
  }

  version_str += 2;

  for (i = 0; i < strlen(version_str); i++) {
    if (!PR_ISDIGIT(version_str[i])) {
      /* Syntax error */
      return -1;
    }
  }

  tmp = NULL;
  *version_status += strtoul(version_str, &tmp, 10);

  if (tmp && *tmp) {
    return -1;
  }

  return 0;
}
示例#2
0
const pr_netaddr_t *proxy_ftp_msg_parse_ext_addr(pool *p, const char *msg,
    const pr_netaddr_t *addr, int cmd_id, const char *net_proto) {
  pr_netaddr_t *res = NULL, na;
  int family = 0;
  unsigned short port = 0;
  char delim, *msg_str, *ptr;
  size_t msglen;

  if (p == NULL ||
      msg == NULL ||
      addr == NULL) {
    errno = EINVAL;
    return NULL;
  }

  if (cmd_id == PR_CMD_EPSV_ID) {
    /* First, find the opening '(' character. */
    ptr = strchr(msg, '(');
    if (ptr == NULL) {
      pr_trace_msg(trace_channel, 12,
        "missing starting '(' character for extended address in '%s'", msg);
      errno = EINVAL;
      return NULL;
    }

    /* Make sure that the last character is a closing ')'. */
    msglen = strlen(ptr);
    if (ptr[msglen-1] != ')') {
      pr_trace_msg(trace_channel, 12,
        "missing ending ')' character for extended address in '%s'", msg);
      errno = EINVAL;
      return NULL;
    }

    msg_str = pstrndup(p, ptr+1, msglen-2);

  } else {
    msg_str = pstrdup(p, msg);
  }

  /* Format is <d>proto<d>ip address<d>port<d> (ASCII in network order),
   * where <d> is an arbitrary delimiter character.
   */
  delim = *msg_str++;

  /* If the network protocol string (e.g. sent by client in EPSV command) is
   * null, then determine the protocol family from the address family we were
   * given.
   */

  /* XXX Hack to skip "all", e.g. "EPSV ALL" commands. */
  if (net_proto != NULL) {
    if (strncasecmp(net_proto, "all", 4) == 0) {
      net_proto = NULL;
    }
  }

  if (net_proto == NULL) {
    if (*msg_str == delim) {
      switch (pr_netaddr_get_family(addr)) {
        case AF_INET:
          family = 1;
          break;

#ifdef PR_USE_IPV6
        case AF_INET6:
          if (pr_netaddr_use_ipv6()) {
            family = 2;
            break;
          }
#endif /* PR_USE_IPV6 */

        default:
          break;
      }

    } else {
      family = atoi(msg_str);
    }

  } else {
    family = atoi(net_proto);
  }

  switch (family) {
    case 1:
      pr_trace_msg(trace_channel, 19, "parsed IPv4 address from '%s'", msg);
      break;

#ifdef PR_USE_IPV6
    case 2:
      pr_trace_msg(trace_channel, 19, "parsed IPv6 address from '%s'", msg);
      if (pr_netaddr_use_ipv6()) {
        break;
      }
#endif /* PR_USE_IPV6 */

    default:
      pr_trace_msg(trace_channel, 12,
        "unsupported network protocol %d", family);
      errno = EPROTOTYPE;
      return NULL;
  }

  /* Now, skip past those numeric characters that atoi() used. */
  while (PR_ISDIGIT(*msg_str)) {
    msg_str++;
  }

  /* If the next character is not the delimiter, it's a badly formatted
   * parameter.
   */
  if (*msg_str == delim) {
    msg_str++;

  } else {
    pr_trace_msg(trace_channel, 17, "rejecting badly formatted message '%s'",
      msg_str);
    errno = EPERM;
    return NULL;
  }

  pr_netaddr_clear(&na);

  /* If the next character IS the delimiter, then the address portion is
   * omitted (which is permissible).
   */
  if (*msg_str == delim) {
    pr_netaddr_set_family(&na, pr_netaddr_get_family(addr));
    pr_netaddr_set_sockaddr(&na, pr_netaddr_get_sockaddr(addr));
    msg_str++;

  } else {
    ptr = strchr(msg_str, delim);
    if (ptr == NULL) {
      /* Badly formatted message. */
      errno = EINVAL;
      return NULL;
    }

    /* Twiddle the string so that just the address portion will be processed
     * by pr_inet_pton().
     */
    *ptr = '\0';

    /* Use pr_inet_pton() to translate the address string into the address
     * value.
     */
    switch (family) {
      case 1: {
        struct sockaddr *sa = NULL;

        pr_netaddr_set_family(&na, AF_INET);
        sa = pr_netaddr_get_sockaddr(&na);
        if (sa) {
          sa->sa_family = AF_INET;
        }

        if (pr_inet_pton(AF_INET, msg_str, pr_netaddr_get_inaddr(&na)) <= 0) {
          pr_trace_msg(trace_channel, 2,
            "error converting IPv4 address '%s': %s", msg_str, strerror(errno));
          errno = EPERM;
          return NULL;
        }

        break;
      }

      case 2: {
        struct sockaddr *sa = NULL;

        pr_netaddr_set_family(&na, AF_INET6);
        sa = pr_netaddr_get_sockaddr(&na);
        if (sa) {
          sa->sa_family = AF_INET6;
        }

        if (pr_inet_pton(AF_INET6, msg_str, pr_netaddr_get_inaddr(&na)) <= 0) {
          pr_trace_msg(trace_channel, 2,
            "error converting IPv6 address '%s': %s", msg_str, strerror(errno));
          errno = EPERM;
          return NULL;
        }

        break;
      }
    }

    /* Advance past the address portion of the argument. */
    msg_str = ++ptr;
  }

  port = atoi(msg_str);

  while (PR_ISDIGIT(*msg_str)) {
    msg_str++;
  }

  /* If the next character is not the delimiter, it's a badly formatted
   * parameter.
   */
  if (*msg_str != delim) {
    pr_trace_msg(trace_channel, 17, "rejecting badly formatted message '%s'",
      msg_str);
    errno = EPERM;
    return NULL;
  }

  /* XXX Use a pool other than session.pool here, in the future. */ 
  res = pr_netaddr_dup(session.pool, &na);
  pr_netaddr_set_port(res, htons(port));

  return res;
}
示例#3
0
pr_response_t *proxy_ftp_ctrl_recv_resp(pool *p, conn_t *ctrl_conn,
                                        unsigned int *nlines) {
    char buf[PR_TUNABLE_BUFFER_SIZE];
    pr_response_t *resp = NULL;
    int multiline = FALSE;
    unsigned int count = 0;

    while (TRUE) {
        char c, *ptr;
        int resp_code;
        size_t buflen;

        pr_signals_handle();

        memset(buf, '\0', sizeof(buf));
        if (ftp_telnet_gets(buf, sizeof(buf)-1, ctrl_conn->instrm,
                            ctrl_conn) == NULL) {
            return NULL;
        }

        buflen = strlen(buf);

        /* Remove any trailing CRs, LFs. */
        while (buflen > 0 &&
                (buf[buflen-1] == '\r' || buf[buflen-1] == '\n')) {
            pr_signals_handle();

            buf[buflen-1] = '\0';
            buflen--;
        }

        /* If we are the first line of the response, the first three characters
         * MUST be numeric, followed by a hypen.  Anything else is nonconformant
         * with RFC 959.
         *
         * If we are NOT the first line of the response, then we are probably
         * handling a multiline response. If the first character is a space, then
         * this is a continuation line.  Otherwise, the first three characters
         * MUST be numeric, AND MUST match the numeric code from the first line.
         * This indicates the last line in the multiline response -- and the
         * character after the numerics MUST be a space.
         *
         */
        if (resp == NULL) {
            /* First line of a possibly multiline response (or just the only line). */
            if (buflen < 4) {
                pr_trace_msg(trace_channel, 12,
                             "read %lu characters of response, needed at least %d",
                             (unsigned long) buflen, 4);
                errno = EINVAL;
                return NULL;
            }

            if (!PR_ISDIGIT((int) buf[0]) ||
                    !PR_ISDIGIT((int) buf[1]) ||
                    !PR_ISDIGIT((int) buf[2])) {
                pr_trace_msg(trace_channel, 1,
                             "non-numeric characters in start of response data: '%c%c%c'",
                             buf[0], buf[1], buf[2]);
                errno = EINVAL;
                return NULL;
            }

            /* If this is a space, then we have a single line response.  If it
             * is a hyphen, then this is the first line of a multiline response.
             */
            if (buf[3] != ' ' &&
                    buf[3] != '-') {
                pr_trace_msg(trace_channel, 1,
                             "unexpected character '%c' following numeric response code", buf[3]);
                errno = EINVAL;
                return NULL;
            }

            if (buf[3] == '-') {
                multiline = TRUE;
            }

            count++;
            resp = (pr_response_t *) pcalloc(p, sizeof(pr_response_t));

        } else {
            if (buflen >= 1) {
                if (buf[0] == ' ') {
                    /* Continuation line; append it the existing response. */
                    if (buflen > 1) {
                        resp->msg = pstrcat(p, resp->msg, "\r\n", buf, NULL);
                    }
                    count++;
                    continue;

                } else {
                    /* Possible ending line of multiline response. */
                    if (buflen < 4) {
                        errno = EINVAL;
                        return NULL;
                    }

                    if (!PR_ISDIGIT((int) buf[0]) ||
                            !PR_ISDIGIT((int) buf[1]) ||
                            !PR_ISDIGIT((int) buf[2])) {
                        pr_trace_msg(trace_channel, 1,
                                     "non-numeric characters in end of response data: '%c%c%c'",
                                     buf[0], buf[1], buf[2]);
                        errno = EINVAL;
                        return NULL;
                    }

                    if (buf[3] != ' ') {
                        errno = EINVAL;
                        return NULL;
                    }

                    count++;
                }
            }
        }

        ptr = &(buf[3]);
        c = *ptr;
        *ptr = '\0';
        resp_code = atoi(buf);
        if (resp_code < 100 ||
                resp_code >= 700) {
            /* Outside of the expected/defined FTP response code range. */
            pr_trace_msg(trace_channel, 1,
                         "invalid FTP response code %d received", resp_code);
            errno = EINVAL;
            return NULL;
        }

        if (resp->num == NULL) {
            resp->num = pstrdup(p, buf);

        } else {
            /* Make sure the last line of the multiline response uses the same
             * response code.
             */
            if (strncmp(resp->num, buf, 3) != 0) {
                pr_trace_msg(trace_channel, 1,
                             "invalid multiline FTP response: mismatched starting response "
                             "code (%s) and ending response code (%s)", resp->num, buf);
                errno = EINVAL;
                return NULL;
            }
        }

        if (resp->msg == NULL) {
            if (buflen > 4) {
                if (multiline) {
                    *ptr = c;
                    resp->msg = pstrdup(p, ptr);
                    *ptr = '\0';

                } else {
                    resp->msg = pstrdup(p, ptr + 1);
                }

            } else {
                resp->msg = "";
            }

            /* If the character after the response code was a space, then this is
             * a single line response; we can be done now.
             */
            if (c == ' ') {
                break;
            }

        } else {
            if (buflen > 4) {
                if (multiline) {
                    *ptr = c;

                    /* This the last line of a multiline response, which means we
                     * need the ENTIRE line, including the response code.
                     */
                    resp->msg = pstrcat(p, resp->msg, "\r\n", buf, NULL);

                } else {
                    resp->msg = pstrcat(p, resp->msg, "\r\n", ptr + 1, NULL);
                }
            }

            break;
        }
    }

    *nlines = count;

    pr_trace_msg(trace_channel, 9,
                 "received '%s%s%s' response from backend to frontend",
                 resp->num, multiline ? "" : " ", resp->msg);
    return resp;
}