/** * A simple Postfix SMTP access policy delegation protocol test * * To not affect real traffic, we send the following request with * fixed virtual triplet values to the server: * request=smtpd_access_policy * protocol_state=RCPT * protocol_name=SMTP * [email protected] * [email protected] * client_address=1.2.3.4 * client_name=mx.foo.tld * and check that the server replies with some action. * * @file */ int check_postfix_policy(Socket_T socket) { char buf[STRLEN]; ASSERT(socket); if(socket_print(socket, "request=smtpd_access_policy\n" "protocol_state=RCPT\n" "protocol_name=SMTP\n" "[email protected]\n" "[email protected]\n" "client_address=1.2.3.4\n" "client_name=mx.foo.tld\n" "\n") < 0) { socket_setError(socket, "POSTFIX-POLICY: error sending data -- %s", STRERROR); return FALSE; } if(! socket_readln(socket, buf, sizeof(buf))) { socket_setError(socket, "POSTFIX-POLICY: error receiving data -- %s", STRERROR); return FALSE; } Str_chomp(buf); if( (strlen(buf) <= 7) || strncasecmp(buf, "action=", 7) ) { socket_setError(socket, "POSTFIX-POLICY error: %s", *buf?buf:"no action returned"); return FALSE; } return TRUE; }
/** * A simple DWP (database wire protocol) test. * * We send the following request to the server: * 'HEAD / HTTP/1.1' * and check the server's status code. * * If the status code is >= 400, an error has occurred. * Return TRUE if the status code is 200, otherwise FALSE. * * @file */ int check_dwp(Socket_T socket) { #define REQ_LENGTH 1024 int n; int status; char buf[STRLEN]; char proto[STRLEN]; ASSERT(socket); if(socket_print(socket, "HEAD / HTTP/1.1\r\n" "Connection: close\r\n\r\n") < 0) { socket_setError(socket, "DWP: error sending data -- %s\n", STRERROR); return FALSE; } if(! socket_readln(socket, buf, sizeof(buf))) { socket_setError(socket, "DWP: error receiving data -- %s\n", STRERROR); return FALSE; } Str_chomp(buf); n= sscanf(buf, "%255s %d", proto, &status); if(n!=2 || (status >= 400)) { socket_setError(socket, "DWP error: %s\n", buf); return FALSE; } return TRUE; }
/** * Send PING and check for PONG. * If alive return TRUE, else, return FALSE. * * @file */ int check_clamav(Socket_T socket) { char buf[STRLEN]; const char *ok = "PONG"; ASSERT(socket); if(socket_print(socket, "PING\r\n") < 0) { socket_setError(socket, "CLAMAV: error sending data -- %s", STRERROR); return FALSE; } if(!socket_readln(socket, buf, sizeof(buf))) { socket_setError(socket, "CLAMAV: error receiving data -- %s", STRERROR); return FALSE; } Str_chomp(buf); if(strncasecmp(buf, ok, strlen(ok)) != 0) { socket_setError(socket, "CLAMAV error: %s", buf); return FALSE; } return TRUE; }
/** * Check the server for greeting code '* OK' and then send LOGOUT and * check for code '* BYE'. If alive return TRUE, else, return FALSE. * * @file */ int check_imap(Socket_T socket) { char buf[STRLEN]; const char *ok = "* OK"; const char *bye = "* BYE"; ASSERT(socket); // Read and check IMAP greeting if (! socket_readln(socket, buf, sizeof(buf))) { socket_setError(socket, "IMAP: greeting read error -- %s", STRERROR); return FALSE; } Str_chomp(buf); if (strncasecmp(buf, ok, strlen(ok)) != 0) { socket_setError(socket, "IMAP: invalid greeting -- %s", buf); return FALSE; } // Logout and check response if (socket_print(socket, "001 LOGOUT\r\n") < 0) { socket_setError(socket, "IMAP: logout command error -- %s", STRERROR); return FALSE; } if (! socket_readln(socket, buf, sizeof(buf))) { socket_setError(socket, "IMAP: logout response read error -- %s", STRERROR); return FALSE; } Str_chomp(buf); if (strncasecmp(buf, bye, strlen(bye)) != 0) { socket_setError(socket, "IMAP: invalid logout response: %s", buf); return FALSE; } return TRUE; }
/** * Check the server for greeting code 220 and then send a QUIT and * check for code 221. If alive return TRUE, else, return FALSE. * * @file */ int check_ftp(Socket_T socket) { int status; char buf[STRLEN]; ASSERT(socket); do { if (! socket_readln(socket, buf, STRLEN)) { socket_setError(socket, "FTP: error receiving data -- %s", STRERROR); return FALSE; } Str_chomp(buf); } while(buf[3] == '-'); // Discard multi-line response if (sscanf(buf, "%d", &status) != 1 || status != 220) { socket_setError(socket, "FTP greeting error: %s", buf); return FALSE; } if (socket_print(socket, "QUIT\r\n") < 0) { socket_setError(socket, "FTP: error sending data -- %s", STRERROR); return FALSE; } if (! socket_readln(socket, buf, STRLEN)) { socket_setError(socket, "FTP: error receiving data -- %s", STRERROR); return FALSE; } Str_chomp(buf); if (sscanf(buf, "%d", &status) != 1 || status != 221) { socket_setError(socket, "FTP quit error: %s", buf); return FALSE; } return TRUE; }
static int check_request_checksum(Socket_T socket, int content_length, char *checksum, int hashtype) { int n, keylength = 0; MD_T result, hash; md5_context_t ctx_md5; sha1_context_t ctx_sha1; char buf[READ_SIZE]; if (content_length <= 0) { DEBUG("HTTP warning: Response does not contain a valid Content-Length -- cannot compute checksum\n"); return TRUE; } switch (hashtype) { case HASH_MD5: md5_init(&ctx_md5); while (content_length > 0) { if ((n = socket_read(socket, buf, content_length > sizeof(buf) ? sizeof(buf) : content_length)) < 0) break; md5_append(&ctx_md5, (const md5_byte_t *)buf, n); content_length -= n; } md5_finish(&ctx_md5, (md5_byte_t *)hash); keylength = 16; /* Raw key bytes not string chars! */ break; case HASH_SHA1: sha1_init(&ctx_sha1); while (content_length > 0) { if ((n = socket_read(socket, buf, content_length > sizeof(buf) ? sizeof(buf) : content_length)) < 0) break; sha1_append(&ctx_sha1, (md5_byte_t *)buf, n); content_length -= n; } sha1_finish(&ctx_sha1, (md5_byte_t *)hash); keylength = 20; /* Raw key bytes not string chars! */ break; default: socket_setError(socket, "HTTP checksum error: Unknown hash type\n"); return FALSE; } if (strncasecmp(Util_digest2Bytes((unsigned char *)hash, keylength, result), checksum, keylength * 2) != 0) { socket_setError(socket, "HTTP checksum error: Document checksum mismatch\n"); return FALSE; } else { DEBUG("HTTP: Succeeded testing document checksum\n"); } return TRUE; }
static int expect(Socket_T socket, int expect) { int status; char buf[STRLEN]; do { if (! socket_readln(socket, buf, STRLEN)) { socket_setError(socket, "LMTP: error receiving data -- %s", STRERROR); return FALSE; } Str_chomp(buf); } while (buf[3] == '-'); // Discard multi-line response if (sscanf(buf, "%d", &status) != 1 || status != expect) { socket_setError(socket, "LMTP error: %s", buf); return FALSE; } return TRUE; }
int check_http(Socket_T socket) { Port_T P; char host[STRLEN]; char auth[STRLEN]= {0}; const char *request = NULL; const char *hostheader = NULL; ASSERT(socket); P = socket_get_Port(socket); ASSERT(P); request = P->request ? P->request : "/"; Util_getHTTPHostHeader(socket, host, STRLEN); hostheader = P->request_hostheader ? P->request_hostheader : host; if (socket_print(socket, "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "Accept: */*\r\n" "User-Agent: %s/%s\r\n" "%s\r\n", request, hostheader, prog, VERSION, get_auth_header(P, auth, STRLEN)) < 0) { socket_setError(socket, "HTTP: error sending data -- %s\n", STRERROR); return FALSE; } return check_request(socket, P); }
static int say(Socket_T socket, char *msg) { if (socket_write(socket, msg, strlen(msg)) < 0) { socket_setError(socket, "LMTP: error sending data -- %s", STRERROR); return FALSE; } return TRUE; }
/** * Simple MySQL test. Connect to MySQL and read Server Handshake Packet. * If we can read the packet and it is not an error packet we assume the * server is up and working. * * @see http://dev.mysql.com/doc/internals/en/client-server-protocol.html */ int check_mysql(Socket_T socket) { ASSERT(socket); mysql_packet_t pkt; if (_response(socket, &pkt)) { short protocol_version = pkt.msg[0]; unsigned char *server_version = pkt.msg + 1; // Protocol is 10 for MySQL 5.x if ((protocol_version > 12) || (protocol_version < 9)) socket_setError(socket, "Invalid protocol version %d", protocol_version); // Handshake packet should have sequence id 0 else if (pkt.seq != 0) socket_setError(socket, "Invalid packet sequence id %d", pkt.seq); else { DEBUG("MySQL: Protocol: %d, Server Version: %s\n", protocol_version, server_version); return TRUE; } } return FALSE; }
static int expect(Socket_T socket, int expect, int log) { int status; char buf[STRLEN]; if(!socket_readln(socket, buf, STRLEN)) { socket_setError(socket, "LMTP: error receiving data -- %s\n", STRERROR); return FALSE; } Str_chomp(buf); sscanf(buf, "%d%*s", &status); if(status != expect) { if(log) socket_setError(socket, "LMTP error: %s\n", buf); return FALSE; } return TRUE; }
static int _response(Socket_T socket, mysql_packet_t *pkt) { memset(pkt, 0, sizeof *pkt); if (socket_read(socket, pkt->buf, 4) < 4) { socket_setError(socket, "Error receiving server response -- %s", STRERROR); return FALSE; } pkt->len = B3(pkt->buf); pkt->len = pkt->len > STRLEN ? STRLEN : pkt->len; // Adjust packet length for this buffer pkt->seq = pkt->buf[3]; pkt->msg = pkt->buf + 4; if (socket_read(socket, pkt->msg, pkt->len) != pkt->len) { socket_setError(socket, "Error receiving server response -- %s", STRERROR); return FALSE; } if (*pkt->msg == MYSQL_ERROR) { unsigned short code = B2(pkt->msg + 1); unsigned char *err = pkt->msg + 9; socket_setError(socket, "Server returned error code %d -- %s", code, err); return FALSE; } return TRUE; }
/** * Check that the server returns a valid HTTP response as well as checksum * or content regex if required * @param s A socket * @return TRUE if the response is valid otherwise FALSE */ static int check_request(Socket_T socket, Port_T P) { int status, content_length = -1; char buf[LINE_SIZE]; if (! socket_readln(socket, buf, LINE_SIZE)) { socket_setError(socket, "HTTP: Error receiving data -- %s\n", STRERROR); return FALSE; } Str_chomp(buf); if (! sscanf(buf, "%*s %d", &status)) { socket_setError(socket, "HTTP error: Cannot parse HTTP status in response: %s\n", buf); return FALSE; } if (status >= 400) { socket_setError(socket, "HTTP error: Server returned status %d\n", status); return FALSE; } /* Get Content-Length header value */ while (socket_readln(socket, buf, LINE_SIZE)) { if ((buf[0] == '\r' && buf[1] == '\n') || (buf[0] == '\n')) break; Str_chomp(buf); if (Str_startsWith(buf, "Content-Length")) { if (! sscanf(buf, "%*s%*[: ]%d", &content_length)) { socket_setError(socket, "HTTP error: Parsing Content-Length response header '%s'\n", buf); return FALSE; } if (content_length < 0) { socket_setError(socket, "HTTP error: Illegal Content-Length response header '%s'\n", buf); return FALSE; } } } if (P->url_request && P->url_request->regex && ! do_regex(socket, content_length, P->url_request)) return FALSE; if (P->request_checksum) return check_request_checksum(socket, content_length, P->request_checksum, P->request_hashtype); return TRUE; }
int check_apache_status(Socket_T socket) { ASSERT(socket); char host[STRLEN]; if (socket_print(socket, "GET /server-status?auto HTTP/1.1\r\n" "Host: %s\r\n" "Accept: */*\r\n" "User-Agent: Monit/%s\r\n" "Connection: close\r\n\r\n", Util_getHTTPHostHeader(socket, host, STRLEN), VERSION) < 0) { socket_setError(socket, "HTTP: error sending data -- %s", STRERROR); return FALSE; } char buffer[4096] = {0}; while (socket_readln(socket, buffer, sizeof(buffer))) { if (Str_startsWith(buffer, "Scoreboard: ")) { char *scoreboard = buffer + 12; // skip header return parse_scoreboard(socket, scoreboard); } } socket_setError(socket, "APACHE-STATUS: error -- no scoreboard found"); return FALSE; }
int check_http(Socket_T socket) { Port_T P; char host[STRLEN]; char auth[STRLEN] = {}; const char *request = NULL; const char *hostheader = NULL; ASSERT(socket); P = socket_get_Port(socket); ASSERT(P); request = P->request ? P->request : "/"; hostheader = _findHostHeaderIn(P->http_headers); hostheader = hostheader ? hostheader : P->request_hostheader ? P->request_hostheader : Util_getHTTPHostHeader(socket, host, STRLEN); // Otherwise use deprecated request_hostheader or default host StringBuffer_T sb = StringBuffer_create(168); StringBuffer_append(sb, "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "Accept: */*\r\n" "User-Agent: Monit/%s\r\n" "%s", request, hostheader, VERSION, get_auth_header(P, auth, STRLEN)); // Add headers if we have them if (P->http_headers) { for (list_t p = P->http_headers->head; p; p = p->next) { if (Str_startsWith(p->e, "Host")) // Already set contrived above continue; StringBuffer_append(sb, "%s\r\n", p->e); } } StringBuffer_append(sb, "\r\n"); int send_status = socket_write(socket, (void*)StringBuffer_toString(sb), StringBuffer_length(sb)); StringBuffer_free(&sb); if (send_status < 0) { socket_setError(socket, "HTTP: error sending data -- %s", STRERROR); return FALSE; } return check_request(socket, P); }
/** * Check an Apache server to monitor its status. * Do this using the server-status report from mod_status, which * will only be available if the server is responding to * some extent. * * Currently based on the Scoreboard response, which is available * even with ExtendedStatus Off on Apache config file. * * @file */ int check_apache_status(Socket_T socket) { char host[STRLEN]; const char *request= "/server-status?auto"; ASSERT(socket); if(socket_print(socket, "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "Accept: */*\r\n" "User-Agent: %s/%s\r\n" "Connection: close\r\n\r\n", request, Util_getHTTPHostHeader(socket, host, STRLEN), prog, VERSION) < 0) { socket_setError(socket, "HTTP: error sending data -- %s\n", STRERROR); return FALSE; } return check_apache_stat(socket); }
static int do_regex(Socket_T socket, int content_length, Request_T R) { int n, size = 0, length = 0, rv = FALSE, regex_return; char *buf = NULL; if (content_length == 0) { socket_setError(socket, "HTTP error: No content returned from server\n"); return FALSE; } else if (content_length < 0) { /* Not defined in response */ content_length = HTTP_CONTENT_MAX; } else if (content_length > HTTP_CONTENT_MAX) { content_length = HTTP_CONTENT_MAX; } length = content_length; buf = ALLOC(content_length + 1); do { n = socket_read(socket, &buf[size], length); if (n <= 0) break; size += n; length -= n; } while (length > 0); if (size == 0) { socket_setError(socket, "HTTP error: receiving data -- %s\n", STRERROR); goto error; } buf[size] = 0; #ifdef HAVE_REGEX_H regex_return = regexec(R->regex, buf, 0, NULL, 0); #else regex_return = strstr(buf, R->regex) ? 0 : 1; #endif switch (R->operator) { case Operator_Equal: if (regex_return == 0) { rv = TRUE; DEBUG("HTTP: Regular expression matches\n"); } else { #ifdef HAVE_REGEX_H char errbuf[STRLEN]; regerror(regex_return, NULL, errbuf, sizeof(errbuf)); socket_setError(socket, "HTTP error: Regular expression doesn't match: %s\n", errbuf); #else socket_setError(socket, "HTTP error: Regular expression doesn't match\n"); #endif } break; case Operator_NotEqual: if (regex_return == 0) { socket_setError(socket, "HTTP error: Regular expression matches\n"); } else { rv = TRUE; DEBUG("HTTP: Regular expression doesn't match\n"); } break; default: socket_setError(socket, "HTTP error: Invalid content operator\n"); } error: FREE(buf); return rv; }
/** * PostgreSQL test. * * @file */ int check_pgsql(Socket_T socket) { unsigned char buf[STRLEN]; unsigned char requestLogin[33] = { 0x00, /** Length */ 0x00, 0x00, 0x21, 0x00, /** ProtoVer 3.0 */ 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 0x00, /** user */ 0x72, 0x6f, 0x6f, 0x74, 0x00, /** root */ 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x00, /** database */ 0x72, 0x6f, 0x6f, 0x74, 0x00, /** root */ 0x00 }; /** Doing this is too suspicious maybe. * Type Q, Length 19 and QUERY select 1 as a; */ /** unsigned char requestQuery[20] = { 0x51, 0x00, 0x00, 0x00, 0x13, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x20, 0x31, 0x20, 0x61, 0x73, 0x20, 0x61, 0x3b, 0x00 }; */ unsigned char requestTerm[5] = { 0x58, /** Type X */ 0x00, /** Length */ 0x00, 0x00, 0x04 }; unsigned char responseAuthOk[9] = { 0x52, /** Type R */ 0x00, /** Length */ 0x00, 0x00, 0x08, 0x00, /** OK code 0 */ 0x00, 0x00, 0x00 }; ASSERT(socket); if(socket_write(socket, (unsigned char *)requestLogin, sizeof(requestLogin)) <= 0) { socket_setError(socket, "PGSQL: error sending data -- %s", STRERROR); return FALSE; } /** Nine-byte is enough to hold Auth-Ok */ if(socket_read(socket, buf, 9) <= 0) { socket_setError(socket, "PGSQL: error receiving data -- %s", STRERROR); return FALSE; } /** If server insists on auth error it is working anyway */ if(*buf=='E') { return TRUE; } /** Successful connection */ if(!memcmp((unsigned char *)buf, (unsigned char *)responseAuthOk, 9)) { /** This is where suspicious people can do SELECT query that I dont */ socket_write(socket, (unsigned char *)requestTerm, sizeof(requestTerm)); return TRUE; } /** The last possibility must be that server is demanding password */ if(*buf=='R') { return TRUE; } socket_setError(socket, "PGSQL: unknown error"); return FALSE; }
/** * Extract the Scoreboard line from the mod_status response. * Count the active apache child processes, and those which are * in other states. If each percentage exceeds the corresponding * limit, then return FALSE. * @param s A socket * @param limit The maximum percentage of logging processes * @return TRUE if logging is OK otherwise FALSE */ static int check_apache_stat(Socket_T socket) { int scored = 0; int errors = 0; char line[READ_SIZE]; char search_string[READ_SIZE + 1]; int loglimit= 0; int closelimit= 0; int dnslimit= 0; int keepalivelimit= 0; int replylimit= 0; int requestlimit= 0; int startlimit= 0; int waitlimit= 0; int gracefullimit= 0; int cleanuplimit= 0; int no_logging = 0; int no_close = 0; int no_dns = 0; int no_keepalive = 0; int no_reply = 0; int no_request = 0; int no_start = 0; int no_wait = 0; int no_graceful = 0; int no_cleanup = 0; int active_servers = 0; char *p; Port_T myPort= (Port_T)socket_get_Port(socket); ASSERT(myPort); loglimit= myPort->ApacheStatus.loglimit; closelimit= myPort->ApacheStatus.closelimit; dnslimit= myPort->ApacheStatus.dnslimit; keepalivelimit= myPort->ApacheStatus.keepalivelimit; replylimit= myPort->ApacheStatus.replylimit; requestlimit= myPort->ApacheStatus.requestlimit; startlimit= myPort->ApacheStatus.startlimit; waitlimit= myPort->ApacheStatus.waitlimit; gracefullimit= myPort->ApacheStatus.gracefullimit; cleanuplimit= myPort->ApacheStatus.cleanuplimit; while(NULL != socket_readln(socket, line, READ_SIZE)) { if(Str_startsWith(line, "Scoreboard")) { if(1 != sscanf(line, "%*s%*[: ]%1024s", search_string)) { Str_chomp(line); socket_setError(socket, "APACHE-STATUS error: parsing Apache status response '%s'\n", line); return FALSE; } else { scored = 1; } } } DEBUG("Scoreboard: %s\n", search_string); /* Check that some scoreboard line was found, if not return an error */ if(!scored){ socket_setError(socket, "APACHE-STATUS error: no scoreboard line returned by Apache\n"); return FALSE; } /* Total each of the status messages in the scoreboard */ for(p = search_string ; *p ; p++){ active_servers++; switch(*p){ case 'S': no_start++; break; case 'R': no_request++; break; case 'W': no_reply++; break; case 'K': no_keepalive++; break; case 'D': no_dns++; break; case 'C': no_close++; break; case 'L': no_logging++; break; case 'G': no_graceful++; break; case 'I': no_cleanup++; break; case '_': no_wait++; break; case '.': active_servers--; break; } } if(active_servers <= 0){ socket_setError(socket, "APACHE-STATUS warning: No idle server or threads found\n"); /* This is not really an error, only a very bussy server */ return TRUE; } /* * Conditions are only tested if the limit parameter is greater than zero. */ if(loglimit > 0){ if(Util_evalQExpression(myPort->ApacheStatus.loglimitOP, (100 * no_logging / active_servers), loglimit)) { socket_setError(socket, "APACHE-STATUS error:" " %i percent of Apache processes are logging\n", loglimit); errors++; } } if(startlimit > 0){ if(Util_evalQExpression(myPort->ApacheStatus.startlimitOP, (100 * no_start / active_servers), startlimit)) { socket_setError(socket, "APACHE-STATUS error:" " %i percent of Apache processes are starting\n", startlimit); errors++; } } if(requestlimit > 0){ if(Util_evalQExpression(myPort->ApacheStatus.requestlimitOP, (100 * no_request / active_servers), requestlimit)) { socket_setError(socket, "APACHE-STATUS error:" " %i percent of Apache processes are reading requests\n", requestlimit); errors++; } } if(replylimit > 0 ){ if(Util_evalQExpression(myPort->ApacheStatus.replylimitOP, (100 * no_reply / active_servers), replylimit)) { socket_setError(socket, "APACHE-STATUS error:" " %i percent of Apache processes are sending a reply\n", replylimit); errors++; } } if(keepalivelimit > 0){ if(Util_evalQExpression(myPort->ApacheStatus.keepalivelimitOP, (100 * no_keepalive / active_servers), keepalivelimit)) { socket_setError(socket, "APACHE-STATUS error:" " %i percent of Apache processes are in keepalive\n", keepalivelimit); errors++; } } if(dnslimit > 0){ if(Util_evalQExpression(myPort->ApacheStatus.dnslimitOP, (100 * no_dns / active_servers), dnslimit)) { socket_setError(socket, "APACHE-STATUS error:" " %i percent of Apache processes are waiting for DNS\n", dnslimit); errors++; } } if(closelimit > 0){ if(Util_evalQExpression(myPort->ApacheStatus.closelimitOP, (100 * no_close / active_servers), closelimit)){ socket_setError(socket, "APACHE-STATUS error:" " %i percent of Apache processes are closing connections\n", closelimit); errors++; } } if(gracefullimit > 0){ if(Util_evalQExpression(myPort->ApacheStatus.gracefullimitOP, (100 * no_graceful / active_servers), gracefullimit)) { socket_setError(socket, "APACHE-STATUS error:" " %i percent of Apache processes are finishing gracefully\n", gracefullimit); errors++; } } if(cleanuplimit > 0){ if(Util_evalQExpression(myPort->ApacheStatus.cleanuplimitOP, (100 * no_cleanup / active_servers), cleanuplimit)) { socket_setError(socket, "APACHE-STATUS error:" " %i percent of Apache processes are in idle cleanup\n", cleanuplimit); errors++; } } if(waitlimit > 0){ if(Util_evalQExpression(myPort->ApacheStatus.waitlimitOP, (100 * no_wait / active_servers), waitlimit)) { socket_setError(socket, "APACHE-STATUS error:" " %i percent of Apache processes are waiting for a connection\n", waitlimit); errors++; } } return (errors==0); }
int check_ntp3(Socket_T socket) { int br; char ntpRequest[NTPLEN]; char ntpResponse[NTPLEN]; ASSERT(socket); memset(ntpRequest, 0, NTPLEN); memset(ntpResponse, 0, NTPLEN); /* Prepare NTP request. The first octet consists of: bits 0-1 ... Leap Indicator bits 2-4 ... Version Number bits 5-7 ... Mode */ ntpRequest[0]= (NTP_LEAP_NOTSYNC << 6) | (NTP_VERSION << 3) | (NTP_MODE_CLIENT); /* Send request to NTP server */ if(socket_write(socket, ntpRequest, NTPLEN) <= 0 ) { socket_setError(socket, "NTP: error sending NTP request -- %s\n", STRERROR); return FALSE; } /* Receive and validate response */ if( (br= socket_read(socket, ntpResponse, NTPLEN)) <= 0) { socket_setError(socket, "NTP: did not receive answer from server -- %s\n", STRERROR); return FALSE; } if( br != NTPLEN ) { socket_setError(socket, "NTP: Received %d bytes from server, expected %d bytes\n", br, NTPLEN); return FALSE; } /* Compare NTP response. The first octet consists of: bits 0-1 ... Leap Indicator bits 2-4 ... Version Number bits 5-7 ... Mode */ if( (ntpResponse[0] & 0x07) != NTP_MODE_SERVER ) { socket_setError(socket, "NTP: Server mode error\n"); return FALSE; } if( (ntpResponse[0] & 0x38) != NTP_VERSION<<3 ) { socket_setError(socket, "NTP: Server protocol version error\n"); return FALSE; } if( (ntpResponse[0] & 0xc0) == NTP_LEAP_NOTSYNC<<6 ) { socket_setError(socket, "NTP: Server not synchronized\n"); return FALSE; } return TRUE; }
/** * Simple MySQL test. * * In the case that the anonymous login is possible, * we will perform MySQL ping. If authentication failed * we suppose the anonymous login is denied and we will * return success, because the server at least performed * authentication => it seems it works. * * @file */ int check_mysql(Socket_T socket) { unsigned char buf[STRLEN]; unsigned char requestLogin[10] = { 0x06, /** Packet Length */ 0x00, 0x00, 0x01, /** Packet Number */ 0x00, /** Flags */ 0x00, /** Max Packet */ 0x00, 0x00, 0x00, /** Username*/ 0x00 /** Password*/ }; unsigned char requestPing[5] = { 0x01, /** Packet Length */ 0x00, 0x00, 0x00, /** Packet Number */ 0x0E /** Command Ping */ }; unsigned char responsePing[5] = { 0x03, /** Packet Length */ 0x00, 0x00, 0x01, /** Packet Number */ 0x00 /** Response Code OK */ /** Padding Ignored */ }; unsigned char requestQuit[5] = { 0x01, /** Packet Length */ 0x00, 0x00, 0x00, /** Packet Number */ 0x01 /** Command Quit */ }; ASSERT(socket); if(!socket_readln(socket, (char *)buf, sizeof(buf))) { socket_setError(socket, "MYSQL: error receiving greeting -- %s\n", STRERROR); return FALSE; } if(socket_write(socket, requestLogin, sizeof(requestLogin)) < 0) { socket_setError(socket, "MYSQL: error sending login -- %s\n", STRERROR); return FALSE; } /* read just first few bytes which contains enough information */ errno = 0; if(socket_read(socket, buf, 7) <= 6) { socket_setError(socket, "MYSQL: error receiving login response\n"); return FALSE; } /* Compare Packet Number: */ if(buf[3] != 0x02) { socket_setError(socket, "MYSQL: invalid response packet number\n"); return FALSE; } /* Compare Response Code: */ if(buf[4] == 0x00) { /* If OK, we are loged in and will perform MySQL ping */ if(socket_write(socket, (unsigned char *)requestPing, sizeof(requestPing)) < 0) { socket_setError(socket, "MYSQL: error sending ping -- %s\n", STRERROR); return FALSE; } if(socket_read(socket, buf, sizeof(responsePing)) <= 0) { socket_setError(socket, "MYSQL: error receiving ping response -- %s\n", STRERROR); return FALSE; } if(memcmp((unsigned char *)buf, (unsigned char *)responsePing, sizeof(responsePing))) { socket_setError(socket, "MYSQL: ping failed\n"); return FALSE; } if(socket_write(socket, (unsigned char *)requestQuit, sizeof(requestQuit)) < 0) { socket_setError(socket, "MYSQL: error sending quit -- %s\n", STRERROR); return FALSE; } return TRUE; } else if((buf[4] == 0xFF) && ((buf[5] == 0x15 && buf[6] == 0x04) || (buf[5] == 0xE3 && buf[6] == 0x04) || (buf[5] == 0x13 && buf[6] == 0x04))) { /* If access denied (1045) or server requires newer authentication protocol (1251) or bad handshake (1043) return success immediately */ return TRUE; } socket_setError(socket, "MYSQL: login failed (error code %d)\n", buf[6] * 256 + buf[5]); return FALSE; }
static int parse_scoreboard(Socket_T socket, char *scoreboard) { int logging = 0, close = 0, dns = 0, keepalive = 0, reply = 0, request = 0, start = 0, wait = 0, graceful = 0, cleanup = 0, open = 0; for (char *state = scoreboard; *state; state++) { switch (*state) { case 'S': start++; break; case 'R': request++; break; case 'W': reply++; break; case 'K': keepalive++; break; case 'D': dns++; break; case 'C': close++; break; case 'L': logging++; break; case 'G': graceful++; break; case 'I': cleanup++; break; case '_': wait++; break; case '.': open++; break; } } int total = logging + close + dns + keepalive + reply + request + start + wait + graceful + cleanup + open; if (! total) return TRUE; // Idle server int errors = 0; Port_T p = socket_get_Port(socket); ASSERT(p); //FIXME: socket_setError overrides previous => either stop on first error, or append errors and set error at the end (convert error buffer to stringbuffer?) if (p->ApacheStatus.loglimit > 0 && Util_evalQExpression(p->ApacheStatus.loglimitOP, (100 * logging / total), p->ApacheStatus.loglimit)) { socket_setError(socket, "APACHE-STATUS: error -- %d percent of processes are logging", 100 * logging / total); errors++; } if (p->ApacheStatus.startlimit > 0 && Util_evalQExpression(p->ApacheStatus.startlimitOP, (100 * start / total), p->ApacheStatus.startlimit)) { socket_setError(socket, "APACHE-STATUS: error -- %d percent of processes are starting", 100 * start / total); errors++; } if (p->ApacheStatus.requestlimit > 0 && Util_evalQExpression(p->ApacheStatus.requestlimitOP, (100 * request / total), p->ApacheStatus.requestlimit)) { socket_setError(socket, "APACHE-STATUS: error -- %d percent of processes are reading requests", 100 * request / total); errors++; } if (p->ApacheStatus.replylimit > 0 && Util_evalQExpression(p->ApacheStatus.replylimitOP, (100 * reply / total), p->ApacheStatus.replylimit)) { socket_setError(socket, "APACHE-STATUS: error -- %d percent of processes are sending a reply", 100 * reply / total); errors++; } if (p->ApacheStatus.keepalivelimit > 0 && Util_evalQExpression(p->ApacheStatus.keepalivelimitOP, (100 * keepalive / total), p->ApacheStatus.keepalivelimit)) { socket_setError(socket, "APACHE-STATUS: error -- %d percent of processes are in keepalive", 100 * keepalive / total); errors++; } if (p->ApacheStatus.dnslimit > 0 && Util_evalQExpression(p->ApacheStatus.dnslimitOP, (100 * dns / total), p->ApacheStatus.dnslimit)) { socket_setError(socket, "APACHE-STATUS: error -- %d percent of processes are waiting for DNS", 100 * dns / total); errors++; } if (p->ApacheStatus.closelimit > 0 && Util_evalQExpression(p->ApacheStatus.closelimitOP, (100 * close / total), p->ApacheStatus.closelimit)) { socket_setError(socket, "APACHE-STATUS: error -- %d percent of processes are closing connections", 100 * close / total); errors++; } if (p->ApacheStatus.gracefullimit > 0 && Util_evalQExpression(p->ApacheStatus.gracefullimitOP, (100 * graceful / total), p->ApacheStatus.gracefullimit)) { socket_setError(socket, "APACHE-STATUS: error -- %d percent of processes are finishing gracefully", 100 * graceful / total); errors++; } if (p->ApacheStatus.cleanuplimit > 0 && Util_evalQExpression(p->ApacheStatus.cleanuplimitOP, (100 * cleanup / total), p->ApacheStatus.cleanuplimit)) { socket_setError(socket, "APACHE-STATUS: error -- %d percent of processes are in idle cleanup", 100 * cleanup / total); errors++; } if (p->ApacheStatus.waitlimit > 0 && Util_evalQExpression(p->ApacheStatus.waitlimitOP, (100 * wait / total), p->ApacheStatus.waitlimit)) { socket_setError(socket, "APACHE-STATUS: error -- %d percent of processes are waiting for a connection", 100 * wait / total); errors++; } return (errors == 0); }
/** * Simple RADIUS test. * * We send a Status-Server packet, and expect an Access-Accept or Accounting-Response packet. * * */ int check_radius(Socket_T socket) { int i, length, left; int secret_len; Port_T P; md5_context_t ctx; char *secret; unsigned char *attr; unsigned char digest[16]; unsigned char response[STRLEN]; unsigned char request[38] = { /* Status-Server */ 0x0c, /* Code, we always use zero */ 0x00, /* Packet length */ 0x00, 0x26, /* Request Authenticator */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Message-Authenticator */ 0x50, /* Length */ 0x12, /* Contents of Message-Authenticator */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; ASSERT(socket); if (socket_get_type(socket) != SOCK_DGRAM) { socket_setError(socket, "RADIUS: unsupported socket type -- protocol test skipped\n"); return TRUE; } P = socket_get_Port(socket); ASSERT(P); secret = P->request ? P->request : "testing123"; secret_len = (int)strlen(secret); /* get 16 bytes of random data */ for (i = 0; i < 16; i++) request[i + 4] = ((unsigned int)random()) & 0xff; /* sign the packet */ Util_hmacMD5(request, sizeof(request), (unsigned char *)secret, secret_len, request + 22); if (socket_write(socket, (unsigned char *)request, sizeof(request)) < 0) { socket_setError(socket, "RADIUS: error sending query -- %s\n", STRERROR); return FALSE; } /* the response should have at least 20 bytes */ if ((length = socket_read(socket, (unsigned char *)response, sizeof(response))) < 20) { socket_setError(socket, "RADIUS: error receiving response -- %s\n", STRERROR); return FALSE; } /* compare the response code (should be Access-Accept or Accounting-Response) */ if ((response[0] != 2) && (response[0] != 5)) { socket_setError(socket, "RADIUS: Invalid reply code -- error occured\n"); return FALSE; } /* compare the packet ID (it should be the same as in our request) */ if (response[1] != 0x00) { socket_setError(socket, "RADIUS: ID mismatch\n"); return FALSE; } /* check the length */ if (response[2] != 0) { socket_setError(socket, "RADIUS: message is too long\n"); return FALSE; } /* check length against packet data */ if (response[3] != length) { socket_setError(socket, "RADIUS: message has invalid length\n"); return FALSE; } /* validate that it is a well-formed packet */ attr = response + 20; left = length - 20; while (left > 0) { if (left < 2) { socket_setError(socket, "RADIUS: message is malformed\n"); return FALSE; } if (attr[1] < 2) { socket_setError(socket, "RADIUS: message has invalid attribute length\n"); return FALSE; } if (attr[1] > left) { socket_setError(socket, "RADIUS: message has attribute that is too long\n"); return FALSE; } /* validate Message-Authenticator, if found */ if (attr[0] == 0x50) { /* FIXME: validate it */ } left -= attr[1]; } /* save the reply authenticator, and copy the request authenticator over */ memcpy(digest, response + 4, 16); memcpy(response + 4, request + 4, 16); md5_init(&ctx); md5_append(&ctx, (const md5_byte_t *)response, length); md5_append(&ctx, (const md5_byte_t *)secret, secret_len); md5_finish(&ctx, response + 4); if (memcmp(digest, response + 4, 16) != 0) socket_setError(socket, "RADIUS: message fails authentication\n"); return TRUE; }
/** * Simple MySQL test. * * In the case that the anonymous login is possible, * we will perform MySQL ping. If authentication failed * we suppose the anonymous login is denied and we will * return success, because the server at least performed * authentication => it seems it works. * * @file */ int check_mysql(Socket_T socket) { unsigned char buf[STRLEN]; unsigned char requestLogin[39] = { 0x23, 0x00, 0x00, // packet_length, 3 bytes 0x01, // packet_number, 1 byte 0x00, 0xa2, 0x00, 0x00, // client_flags, 4 bytes (do+auth 4.1, transact) 0x00, 0x00, 0x00, 0x40, // max_packet_size, 4 bytes 0x08, // charset_number (latin1), 1 byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // filler, 23 bytes 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // username 0x00, // password 0x00 }; unsigned char requestPing[5] = { 0x01, 0x00, 0x00, // packet_length, 3 bytes 0x00, // packet_number, 1 byte 0x0e // command ping (14), 1 byte }; unsigned char responsePing[5] = { 0x07, 0x00, 0x00, // packet_length, 3 bytes 0x01, // packet_number, 1 byte 0x00 // affected_rows, 1 byte // remaining 4 bytes ignored }; unsigned char requestQuit[5] = { 0x01, 0x00, 0x00, // packet_length, 3 bytes 0x00, // packet_number, 1 byte 0x01 // command quit (1), 1 byte }; ASSERT(socket); if(!socket_readln(socket, (char *)buf, sizeof(buf))) { socket_setError(socket, "MYSQL: error receiving greeting -- %s", STRERROR); return FALSE; } if(socket_write(socket, requestLogin, sizeof(requestLogin)) < 0) { socket_setError(socket, "MYSQL: error sending login -- %s", STRERROR); return FALSE; } /* read just first few bytes which contains enough information */ errno = 0; if(socket_read(socket, buf, 7) <= 6) { socket_setError(socket, "MYSQL: error receiving login response"); return FALSE; } /* Compare Packet Number: */ if(buf[3] != 0x02) { socket_setError(socket, "MYSQL: invalid response packet number"); return FALSE; } /* Compare Response Code: */ if(buf[4] == 0x00) { /* If OK, we are loged in and will perform MySQL ping */ if(socket_write(socket, (unsigned char *)requestPing, sizeof(requestPing)) < 0) { socket_setError(socket, "MYSQL: error sending ping -- %s", STRERROR); return FALSE; } if(socket_read(socket, buf, sizeof(responsePing)) <= 0) { socket_setError(socket, "MYSQL: error receiving ping response -- %s", STRERROR); return FALSE; } if(memcmp((unsigned char *)buf, (unsigned char *)responsePing, sizeof(responsePing))) { socket_setError(socket, "MYSQL: ping failed"); return FALSE; } if(socket_write(socket, (unsigned char *)requestQuit, sizeof(requestQuit)) < 0) { socket_setError(socket, "MYSQL: error sending quit -- %s", STRERROR); return FALSE; } return TRUE; } else if((buf[4] == 0xFF) && ((buf[5] == 0x15 && buf[6] == 0x04) || (buf[5] == 0xE3 && buf[6] == 0x04) || (buf[5] == 0x13 && buf[6] == 0x04))) { /* If access denied (1045) or server requires newer authentication protocol (1251) or bad handshake (1043) return success immediately */ return TRUE; } socket_setError(socket, "MYSQL: login failed (error code %d)", buf[6] * 256 + buf[5]); return FALSE; }
/** * Simple DNS test. * * The nameserver is queried for NS record of DNS root. * * @file */ int check_dns(Socket_T socket) { int offset_request = 0; int offset_response = 0; int rc; unsigned char buf[STRLEN]; unsigned char *response = NULL; unsigned char request[19] = { 0x00, /** Request Length field for DNS via TCP */ 0x11, 0x00, /** Transaction ID */ 0x01, 0x01, /** Flags */ 0x00, 0x00, /** Queries count */ 0x01, 0x00, /** Answer resource records count */ 0x00, 0x00, /** Authority resource records count */ 0x00, 0x00, /** Additional resource records count */ 0x00, /** Query: */ 0x00, /** Name: DNS root (empty string) */ 0x00, /** Type: NS */ 0x02, 0x00, /** Class: IN */ 0x01 }; ASSERT(socket); switch (socket_get_type(socket)) { case SOCK_DGRAM: offset_request = 2; /* Skip Length field in request */ offset_response = 0; break; case SOCK_STREAM: offset_request = 0; offset_response = 2; /* Skip Length field in response */ break; default: socket_setError(socket, "DNS: unsupported socket type -- protocol test skipped"); return TRUE; } if (socket_write(socket, (unsigned char *)request + offset_request, sizeof(request) - offset_request) < 0) { socket_setError(socket, "DNS: error sending query -- %s", STRERROR); return FALSE; } /* Response should have at least 14 bytes */ if (socket_read(socket, (unsigned char *)buf, 15) <= 14) { socket_setError(socket, "DNS: error receiving response -- %s", STRERROR); return FALSE; } response = buf + offset_response; /* Compare transaction ID (it should be the same as in our request): */ if (response[0] != 0x00 && response[1] != 0x01) { socket_setError(socket, "DNS: response transaction ID mismatch -- received 0x%x%x, expected 0x1", response[0], response[1]); return FALSE; } /* Compare flags: */ /* Response type */ if ((response[2] & 0x80) != 0x80) { socket_setError(socket, "DNS: invalid response type: 0x%x", response[2] & 0x80); return FALSE; } /* Response code: accept request refusal as correct response as the server may disallow NS root query but the negative response means, it reacts to requests */ rc = response[3] & 0x0F; if (rc != 0x0 && rc != 0x5) { socket_setError(socket, "DNS: invalid response code: 0x%x", rc); return FALSE; } /* Compare queries count (it should be one as in our request): */ if (response[4] != 0x00 && response[5] != 0x01) { socket_setError(socket, "DNS: invalid query count in response -- received 0x%x%x, expected 1", response[4], response[5]); return FALSE; } /* Compare answer and authority resource record counts (they shouldn't be both zero) */ if (rc == 0 && response[6] == 0x00 && response[7] == 0x00 && response[8] == 0x00 && response[9] == 0x00) { socket_setError(socket, "DNS: no answer or authority records returned"); return FALSE; } return TRUE; }
/** * Generic service test. * * @file */ int check_generic(Socket_T socket) { Generic_T g = NULL; char *buf; #ifdef HAVE_REGEX_H int regex_return; #endif ASSERT(socket); if(socket_get_Port(socket)) g = ((Port_T)(socket_get_Port(socket)))->generic; buf = CALLOC(sizeof(char), Run.expectbuffer + 1); while (g != NULL) { if (g->send != NULL) { /* Unescape any \0x00 escaped chars in g's send string to allow sending a string containing \0 bytes also */ char *X = Str_dup(g->send); int l = Util_handle0Escapes(X); if(socket_write(socket, X, l) < 0) { socket_setError(socket, "GENERIC: error sending data -- %s", STRERROR); FREE(X); FREE(buf); return FALSE; } else DEBUG("GENERIC: successfully sent: '%s'\n", g->send); FREE(X); } else if (g->expect != NULL) { int n; /* Need read, not readln here */ if((n = socket_read(socket, buf, Run.expectbuffer)) < 0) { socket_setError(socket, "GENERIC: error receiving data -- %s", STRERROR); FREE(buf); return FALSE; } buf[n] = 0; if (n > 0) _escapeZeroInExpectBuffer(buf, n); #ifdef HAVE_REGEX_H regex_return = regexec(g->expect, buf, 0, NULL, 0); if (regex_return != 0) { char e[STRLEN]; regerror(regex_return, g->expect, e, STRLEN); socket_setError(socket, "GENERIC: receiving unexpected data [%s] -- %s", Str_trunc(buf, STRLEN - 4), e); FREE(buf); return FALSE; } else DEBUG("GENERIC: successfully received: '%s'\n", Str_trunc(buf, STRLEN - 4)); #else /* w/o regex support */ if (strncmp(buf, g->expect, strlen(g->expect)) != 0) { socket_setError(socket, "GENERIC: receiving unexpected data [%s]", Str_trunc(buf, STRLEN - 4)); FREE(buf); return FALSE; } else DEBUG("GENERIC: successfully received: '%s'\n", Str_trunc(buf, STRLEN - 4)); #endif } else { /* This should not happen */ socket_setError(socket, "GENERIC: unexpected strangeness"); FREE(buf); return FALSE; } g = g->next; } FREE(buf); return TRUE; }
int check_sip(Socket_T socket) { int status; char buf[STRLEN]; int port; char *transport; Port_T P; const char *request; const char *myip; char *rport= ""; char *proto; ASSERT(socket); P= socket_get_Port(socket); ASSERT(P); request= P->request?P->request:"*****@*****.**"; port = socket_get_local_port(socket); proto = socket_is_secure(socket) ? "sips" : "sip"; switch(socket_get_type(socket)) { case SOCK_DGRAM: { transport="UDP"; rport=";rport"; break; } case SOCK_STREAM: { transport="TCP"; break; } default: { socket_setError(socket, "Unsupported socket type, only TCP and UDP are supported\n"); return TRUE; } } myip= socket_get_local_host(socket); if(socket_print(socket, "OPTIONS %s:%s SIP/2.0\r\n" "Via: SIP/2.0/%s %s:%d;branch=z9hG4bKh%u%s\r\n" "Max-Forwards: %d\r\n" "To: <%s:%s>\r\n" "From: monit <%s:monit@%s>;tag=%d\r\n" "Call-ID: %u\r\n" "CSeq: 63104 OPTIONS\r\n" "Contact: <%s:%s:%d>\r\n" "Accept: application/sdp\r\n" "Content-Length: 0\r\n" "User-Agent: %s/%s\r\n\r\n", proto, // protocol request, // to transport, // via transport udp|tcp myip, // who its from port, // our port random(), // branch rport, // rport option P->maxforward, // maximum forwards proto, // protocol request, // to proto, // protocol myip, // from host random(), // tag random(), // call id proto, // protocol myip, // contact host port, // contact port prog, VERSION // user agent ) < 0) { socket_setError(socket, "SIP: error sending data -- %s\n", STRERROR); return FALSE; } if(! socket_readln(socket, buf, sizeof(buf))) { socket_setError(socket, "SIP: error receiving data -- %s\n", STRERROR); return FALSE; } Str_chomp(buf); DEBUG("Response from SIP server: %s\n", buf); if(! sscanf(buf, "%*s %d", &status)) { socket_setError(socket, "SIP error: cannot parse SIP status in response: %s\n", buf); return FALSE; } if(status >= 400) { socket_setError(socket, "SIP error: Server returned status %d\n", status); return FALSE; } if(status >= 300 && status < 400) { socket_setError(socket, "SIP info: Server redirection. Returned status %d\n", status); return FALSE; } if(status > 100 && status < 200) { socket_setError(socket, "SIP error: Provisional response . Returned status %d\n", status); return FALSE; } return TRUE; }