/// Parses GET or POST-style variable data. /// Saves to internal variable structure using HTTP::Parser::SetVar. void HTTP::Parser::parseVars(std::string data) { std::string varname; std::string varval; // position where a part start (e.g. after &) size_t pos = 0; while (pos < data.length()) { size_t nextpos = data.find('&', pos); if (nextpos == std::string::npos) { nextpos = data.length(); } size_t eq_pos = data.find('=', pos); if (eq_pos < nextpos) { // there is a key and value varname = data.substr(pos, eq_pos - pos); varval = data.substr(eq_pos + 1, nextpos - eq_pos - 1); } else { // no value, only a key varname = data.substr(pos, nextpos - pos); varval.clear(); } SetVar(urlunescape(varname), urlunescape(varval)); if (nextpos == std::string::npos) { // in case the string is gigantic break; } // erase & pos = nextpos + 1; } }
int add_ldap_test(testitem_t *t) { #ifdef BBGEN_LDAP ldap_data_t *req; LDAPURLDesc *ludp; char *urltotest; /* * t->testspec containts the full testspec * We need to remove any URL-encoding. */ urltotest = urlunescape(t->testspec); if (ldap_url_parse(urltotest, &ludp) != 0) { errprintf("Invalid LDAP URL %s\n", t->testspec); return 1; } /* Allocate the private data and initialize it */ t->privdata = (void *) malloc(sizeof(ldap_data_t)); req = (ldap_data_t *) t->privdata; req->ldapdesc = (void *) ludp; req->usetls = (strncmp(urltotest, "ldaps:", 6) == 0); #ifdef BBGEN_LDAP_USESTARTTLS if (req->usetls && (ludp->lud_port == LDAPS_PORT)) { dbgprintf("Forcing port %d for ldaps with STARTTLS\n", LDAP_PORT ); ludp->lud_port = LDAP_PORT; } #endif req->ldapstatus = 0; req->output = NULL; req->ldapcolor = -1; req->faileddeps = NULL; req->duration.tv_sec = req->duration.tv_nsec = 0; req->certinfo = NULL; req->certexpires = 0; #endif return 0; }
/* * Get an environment variable (eg: QUERY_STRING) and do CGI decoding of it. */ char *urldecode(char *envvar) { if (xgetenv(envvar) == NULL) return NULL; return urlunescape(xgetenv(envvar)); }
/* * Parse a URL into components, following the guidelines in RFC 1808. * This fills out a urlelem_t struct with the elements, and also * constructs a canonical form of the URL. */ void parse_url(char *inputurl, urlelem_t *url) { char *tempurl; char *fragment = NULL; char *netloc; char *startp, *p; int haveportspec = 0; char *canonurl; int canonurllen; memset(url, 0, sizeof(urlelem_t)); url->scheme = url->host = url->relurl = ""; /* Get a temp. buffer we can molest */ tempurl = strdup(inputurl); /* First cut off any fragment specifier */ fragment = strchr(tempurl, '#'); if (fragment) *fragment = '\0'; /* Get the scheme (protocol) */ startp = tempurl; p = strchr(startp, ':'); if (p) { *p = '\0'; if (strncmp(startp, "https", 5) == 0) { url->scheme = "https"; url->port = 443; if (strlen(startp) > 5) url->schemeopts = strdup(startp+5); } else if (strncmp(startp, "http", 4) == 0) { url->scheme = "http"; url->port = 80; if (strlen(startp) > 4) url->schemeopts = strdup(startp+4); } else if (strcmp(startp, "ftp") == 0) { url->scheme = "ftp"; url->port = 21; } else if (strcmp(startp, "ldap") == 0) { url->scheme = "ldap"; url->port = 389; } else if (strcmp(startp, "ldaps") == 0) { url->scheme = "ldaps"; url->port = 389; /* ldaps:// URL's are non-standard, and must use port 389+STARTTLS */ } else { /* Unknown scheme! */ errprintf("Unknown URL scheme '%s' in URL '%s'\n", startp, inputurl); url->scheme = strdup(startp); url->port = 0; } startp = (p+1); } else { errprintf("Malformed URL - no 'scheme:' in '%s'\n", inputurl); url->parseerror = 1; return; } if (strncmp(startp, "//", 2) == 0) { startp += 2; netloc = startp; p = strchr(startp, '/'); if (p) { *p = '\0'; startp = (p+1); } else startp += strlen(startp); } else { errprintf("Malformed URL missing '//' in '%s'\n", inputurl); url->parseerror = 2; return; } /* netloc is [username:password@]hostname[:port][=forcedIP] */ p = strchr(netloc, '@'); if (p) { *p = '\0'; url->auth = strdup(urlunescape(netloc)); netloc = (p+1); } p = strchr(netloc, '='); if (p) { url->ip = strdup(p+1); *p = '\0'; } p = strchr(netloc, ':'); if (p) { haveportspec = 1; *p = '\0'; url->port = atoi(p+1); } url->host = strdup(netloc); if (url->port == 0) { struct servent *svc = getservbyname(url->scheme, NULL); if (svc) url->port = ntohs(svc->s_port); else { errprintf("Unknown scheme (no port) '%s'\n", url->scheme); url->parseerror = 3; return; } } if (fragment) *fragment = '#'; url->relurl = malloc(strlen(startp) + 2); sprintf(url->relurl, "/%s", startp); if (url->auth == NULL) { /* See if we have it in the .netrc list */ loginlist_t *walk; load_netrc(); for (walk = loginhead; (walk && (strcmp(walk->host, url->host) != 0)); walk = walk->next) ; if (walk) url->auth = walk->auth; } /* Build the canonical form of this URL, free from all BB'isms */ canonurllen = 1; canonurllen += strlen(url->scheme)+3; /* Add room for the "://" */ canonurllen += strlen(url->host); canonurllen += 6; /* Max. length of a port spec. */ canonurllen += strlen(url->relurl); p = canonurl = (char *)malloc(canonurllen); p += sprintf(p, "%s://", url->scheme); /* * Dont include authentication here, since it * may show up in clear text on the info page. * And it is not used in URLs to access the site. * if (url->auth) p += sprintf(p, "%s@", url->auth); */ p += sprintf(p, "%s", url->host); if (haveportspec) p += sprintf(p, ":%d", url->port); p += sprintf(p, "%s", url->relurl); url->origform = canonurl; xfree(tempurl); return; }
int add_ldap_test(testitem_t *t) { #ifdef HAVE_LDAP testitem_t *basecheck; ldap_data_t *req; LDAPURLDesc *ludp; char *urltotest; int badurl; basecheck = (testitem_t *)t->privdata; /* * t->testspec containts the full testspec * We need to remove any URL-encoding. */ urltotest = urlunescape(t->testspec); badurl = (ldap_url_parse(urltotest, &ludp) != 0); /* Allocate the private data and initialize it */ t->privdata = (void *) calloc(1, sizeof(ldap_data_t)); req = (ldap_data_t *) t->privdata; req->ldapdesc = (void *) ludp; req->usetls = (strncmp(urltotest, "ldaps:", 6) == 0); if (req->usetls && (ludp->lud_port == LDAPS_PORT)) { dbgprintf("Forcing port %d for ldaps with STARTTLS\n", LDAP_PORT ); ludp->lud_port = LDAP_PORT; } req->ldapstatus = 0; req->output = NULL; req->ldapcolor = -1; req->faileddeps = NULL; req->duration.tv_sec = req->duration.tv_nsec = 0; req->certinfo = NULL; req->certissuer = NULL; req->certexpires = 0; req->certkeysz = 0; req->skiptest = 0; if (badurl) { errprintf("Invalid LDAP URL %s\n", t->testspec); req->skiptest = 1; req->ldapstatus = XYMON_LDAP_BINDFAIL; req->output = "Cannot parse LDAP URL"; } /* * At this point, the plain TCP checks have already run. * So we know from the test found in t->privdata whether * the LDAP port is open. * If it is not open, then dont run this check. */ if (basecheck->open == 0) { /* Cannot connect to LDAP port. */ req->skiptest = 1; req->ldapstatus = XYMON_LDAP_BINDFAIL; req->output = "Cannot connect to server"; } #endif return 0; }
int main(int argc, char *argv[]) { testitem_t item; testedhost_t host; service_t ldapservice; int argi = 1; int ldapdebug = 0; while ((argi < argc) && (strncmp(argv[argi], "--", 2) == 0)) { if (strcmp(argv[argi], "--debug") == 0) { debug = 1; } else if (strncmp(argv[argi], "--ldapdebug=", strlen("--ldapdebug=")) == 0) { char *p = strchr(argv[argi], '='); ldapdebug = atoi(p+1); } argi++; } /* For testing, dont crash in sendmsg when no XYMSRV defined */ dontsendmessages = 1; if (xgetenv("XYMSRV") == NULL) putenv("XYMSRV=127.0.0.1"); memset(&item, 0, sizeof(item)); memset(&host, 0, sizeof(host)); memset(&ldapservice, 0, sizeof(ldapservice)); ldapservice.portnum = 389; ldapservice.testname = "ldap"; ldapservice.namelen = strlen(ldapservice.testname); ldapservice.items = &item; item.host = &host; item.service = &ldapservice; item.dialup = item.reverse = item.silenttest = item.alwaystrue = 0; item.testspec = urlunescape(argv[argi]); host.firstldap = &item; host.hostname = "ldaptest.xymon"; host.ldapuser = NULL; host.ldappasswd = NULL; init_ldap_library(); if (ldapdebug) { #if defined(LBER_OPT_DEBUG_LEVEL) && defined(LDAP_OPT_DEBUG_LEVEL) ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL, &ldapdebug); ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &ldapdebug); #else printf("LDAP library does not support change of debug level\n"); #endif } if (add_ldap_test(&item) == 0) { run_ldap_tests(&ldapservice, 0, 10); combo_start(); send_ldap_results(&ldapservice, &host, "", 0); combo_end(); } shutdown_ldap_library(); return 0; }
/// Attempt to read a whole HTTP response or request from a data buffer. /// If succesful, fills its own fields with the proper data and removes the response/request /// from the data buffer. /// \param HTTPbuffer The data buffer to read from. /// \return True on success, false otherwise. bool HTTP::Parser::parse(std::string & HTTPbuffer) { size_t f; std::string tmpA, tmpB, tmpC; /// \todo Make this not resize HTTPbuffer in parts, but read all at once and then remove the entire request, like doxygen claims it does? while (!HTTPbuffer.empty()) { if (!seenHeaders) { f = HTTPbuffer.find('\n'); if (f == std::string::npos) return false; tmpA = HTTPbuffer.substr(0, f); if (f + 1 == HTTPbuffer.size()) { HTTPbuffer.clear(); } else { HTTPbuffer.erase(0, f + 1); } while (tmpA.find('\r') != std::string::npos) { tmpA.erase(tmpA.find('\r')); } if (!seenReq) { seenReq = true; f = tmpA.find(' '); if (f != std::string::npos) { if (tmpA.substr(0, 4) == "HTTP") { protocol = tmpA.substr(0, f); tmpA.erase(0, f + 1); f = tmpA.find(' '); if (f != std::string::npos) { url = tmpA.substr(0, f); tmpA.erase(0, f + 1); method = tmpA; if (url.find('?') != std::string::npos) { parseVars(url.substr(url.find('?') + 1)); //parse GET variables url.erase(url.find('?')); } url = urlunescape(url); } else { seenReq = false; } } else { method = tmpA.substr(0, f); tmpA.erase(0, f + 1); f = tmpA.find(' '); if (f != std::string::npos) { url = tmpA.substr(0, f); tmpA.erase(0, f + 1); protocol = tmpA; if (url.find('?') != std::string::npos) { parseVars(url.substr(url.find('?') + 1)); //parse GET variables url.erase(url.find('?')); } url = urlunescape(url); } else { seenReq = false; } } } else { seenReq = false; } } else { if (tmpA.size() == 0) { seenHeaders = true; body.clear(); if (GetHeader("Content-Length") != "") { length = atoi(GetHeader("Content-Length").c_str()); if (body.capacity() < length) { body.reserve(length); } } if (GetHeader("Transfer-Encoding") == "chunked") { getChunks = true; doingChunk = 0; } } else { f = tmpA.find(':'); if (f == std::string::npos) continue; tmpB = tmpA.substr(0, f); tmpC = tmpA.substr(f + 1); SetHeader(tmpB, tmpC); } } } if (seenHeaders) { if (length > 0) { if (headerOnly) { return true; } unsigned int toappend = length - body.length(); if (toappend > 0) { body.append(HTTPbuffer, 0, toappend); HTTPbuffer.erase(0, toappend); } if (length == body.length()) { parseVars(body); //parse POST variables return true; } else { return false; } } else { if (getChunks) { if (headerOnly) { return true; } if (doingChunk) { unsigned int toappend = HTTPbuffer.size(); if (toappend > doingChunk) { toappend = doingChunk; } body.append(HTTPbuffer, 0, toappend); HTTPbuffer.erase(0, toappend); doingChunk -= toappend; } else { f = HTTPbuffer.find('\n'); if (f == std::string::npos) return false; tmpA = HTTPbuffer.substr(0, f); while (tmpA.find('\r') != std::string::npos) { tmpA.erase(tmpA.find('\r')); } unsigned int chunkLen = 0; if (!tmpA.empty()) { for (unsigned int i = 0; i < tmpA.size(); ++i) { chunkLen = (chunkLen << 4) | unhex(tmpA[i]); } if (chunkLen == 0) { getChunks = false; return true; } doingChunk = chunkLen; } if (f + 1 == HTTPbuffer.size()) { HTTPbuffer.clear(); } else { HTTPbuffer.erase(0, f + 1); } } return false; } else { return true; } } } } return false; //empty input } //HTTPReader::parse
cgidata_t *cgi_request(void) { char *method = NULL; char *reqdata = NULL; char *conttype = NULL; char *token; cgidata_t *head = NULL, *tail = NULL; cgi_error_text = NULL; cgi_method = CGI_OTHER; method = getenv("REQUEST_METHOD"); if (!method) { lcgi_error("CGI violation - no REQUEST_METHOD\n"); return NULL; } conttype = getenv("CONTENT_TYPE"); if (strcasecmp(method, "POST") == 0) { char *contlen = getenv("CONTENT_LENGTH"); int postsize = 0; cgi_method = CGI_POST; if (contlen) { postsize = atoi(contlen); } else { lcgi_error("CGI violation - no CONTENT_LENGTH\n"); return NULL; } if (postsize < MAX_REQ_SIZE) { size_t n; reqdata = (char *)malloc(postsize+1); n = fread(reqdata, 1, postsize, stdin); if (n < postsize) { lcgi_error("Error reading POST data\n"); return NULL; } reqdata[n] = '\0'; } else { lcgi_error("Request too large\n"); return NULL; } } else if (strcasecmp(method, "GET") == 0) { char *q = getenv("QUERY_STRING"); cgi_method = CGI_GET; if (q) { if (strlen(q) < MAX_REQ_SIZE) { reqdata = strdup(q); } else { lcgi_error("Request too large\n"); return NULL; } } else { /* This is OK - we may not have any query */ return NULL; } } dbgprintf("CGI: Request method='%s', data='%s'\n", method, textornull(reqdata)); if ((cgi_method == CGI_GET) || (conttype && (strcasecmp(conttype, "application/x-www-form-urlencoded") == 0))) { token = strtok(reqdata, "&"); while (token) { cgidata_t *newitem = (cgidata_t *)calloc(1, sizeof(cgidata_t)); char *val; val = strchr(token, '='); if (val) { *val = '\0'; val = urlunescape(val+1); } newitem->name = strdup(token); newitem->value = strdup(val ? val : ""); if (!tail) { head = newitem; } else { tail->next = newitem; } tail = newitem; token = strtok(NULL, "&"); } } else if ((cgi_method == CGI_POST) && (conttype && (strcasecmp(conttype, "multipart/form-data") == 0))) { char *bol, *eoln, *delim; char eolnchar = '\n'; char *currelembegin = NULL, *currelemend = NULL; cgidata_t *newitem = NULL; delim = reqdata; eoln = strchr(delim, '\n'); if (!eoln) return NULL; *eoln = '\0'; delim = strdup(reqdata); *eoln = '\n'; if (*(delim + strlen(delim) - 1) == '\r') { eolnchar = '\r'; *(delim + strlen(delim) - 1) = '\0'; } bol = reqdata; do { eoln = strchr(bol, eolnchar); if (eoln) *eoln = '\0'; if (strncmp(bol, delim, strlen(delim)) == 0) { if (newitem && currelembegin && (currelemend >= currelembegin)) { /* Finish off the current item */ char savech; savech = *currelemend; *currelemend = '\0'; newitem->value = strdup(currelembegin); *currelemend = savech; currelembegin = currelemend = NULL; } if (strcmp(bol+strlen(delim), "--") != 0) { /* New element */ newitem = (cgidata_t *)calloc(1, sizeof(cgidata_t)); if (!tail) head = newitem; else tail->next = newitem; tail = newitem; } else { /* No more elements, end of input */ newitem = NULL; bol = NULL; continue; } } else if (newitem && (strncasecmp(bol, "Content-Disposition:", 20) == 0)) { char *tok; tok = strtok(bol, ";\t "); while (tok) { if (strncasecmp(tok, "name=", 5) == 0) { char *name; name = tok+5; if (*name == '\"') { name++; *(name + strlen(name) - 1) = '\0'; } newitem->name = strdup(name); } else if (strncasecmp(tok, "filename=", 9) == 0) { char *filename; filename = tok+9; if (*filename == '\"') { filename++; *(filename + strlen(filename) - 1) = '\0'; } newitem->filename = strdup(filename); } tok = strtok(NULL, ";\t "); } } else if (newitem && (strncasecmp(bol, "Content-Type:", 12) == 0)) { } else if (newitem && !currelembegin && (*bol == '\0')) { /* End of headers for one element */ if (eoln) { currelembegin = eoln+1; if ((eolnchar == '\r') && (*currelembegin == '\n')) currelembegin++; } currelemend = currelembegin; } else if (newitem && currelembegin) { currelemend = (eoln ? eoln+1 : bol + strlen(bol)); } if (eoln) { bol = eoln+1; if ((eolnchar == '\r') && (*bol == '\n')) bol++; } else { bol = NULL; } } while (bol && (*bol)); if (newitem) { if (!newitem->name) newitem->name = ""; if (!newitem->value) newitem->value = ""; } } else { /* Raw data - return a single record to caller */ head = (cgidata_t *)calloc(1, sizeof(cgidata_t)); head->name = strdup(""); head->value = reqdata ? strdup(reqdata) : NULL; } if (reqdata) xfree(reqdata); return head; }