diff options
| author | 2019-04-07 00:14:13 -0400 | |
|---|---|---|
| committer | 2019-04-07 00:14:13 -0400 | |
| commit | 5140318f8f758141b4e350871db1fe869eb858dc (patch) | |
| tree | 3cf26f4845b2c4674af81f108fbdbf47a996dcff /src/scan.c | |
Import Upstream version 1.1.5upstream/1.1.5
Diffstat (limited to 'src/scan.c')
| -rw-r--r-- | src/scan.c | 969 |
1 files changed, 969 insertions, 0 deletions
diff --git a/src/scan.c b/src/scan.c new file mode 100644 index 0000000..2008e8c --- /dev/null +++ b/src/scan.c @@ -0,0 +1,969 @@ +/* + * Copyright (c) 2002 Erik Fears + * Copyright (c) 2014-2018 ircd-hybrid development team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +#include "setup.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> + +#include "compat.h" +#include "config.h" +#include "irc.h" +#include "log.h" +#include "stats.h" +#include "dnsbl.h" +#include "options.h" +#include "negcache.h" +#include "main.h" +#include "memory.h" +#include "match.h" +#include "misc.h" +#include "scan.h" + +/* libopm includes */ +#include "libopm/src/opm.h" +#include "libopm/src/opm_common.h" +#include "libopm/src/opm_error.h" +#include "libopm/src/opm_types.h" + + +/* GLOBAL LIST */ +static list_t SCANNERS; /* List of OPM_T */ + +/* Function declarations */ +static struct scan_struct *scan_create(const char *[], const char *); +static void scan_free(struct scan_struct *); +static void scan_irckline(const struct scan_struct *, const char *, const char *); +static void scan_negative(const struct scan_struct *); +static void scan_log(OPM_REMOTE_T *); + +/** Callbacks for LIBOPM */ +static void scan_open_proxy(OPM_T *, OPM_REMOTE_T *, int, void *); +static void scan_negotiation_failed(OPM_T *, OPM_REMOTE_T *, int, void *); +static void scan_timeout(OPM_T *, OPM_REMOTE_T *, int, void *); +static void scan_end(OPM_T *, OPM_REMOTE_T *, int, void *); +static void scan_handle_error(OPM_T *, OPM_REMOTE_T *, int, void *); + +extern FILE *scanlogfile; + + +/* scan_cycle + * + * Perform scanner tasks. + */ +void +scan_cycle(void) +{ + node_t *node; + + /* Cycle through the blacklist first.. */ + dnsbl_cycle(); + + /* Cycle each scanner object */ + LIST_FOREACH(node, SCANNERS.head) + { + struct scanner_struct *scs = node->data; + opm_cycle(scs->scanner); + } +} + +/* scan_timer + * + * Perform actions that are to be performed every ~1 second. + * + * Parameters: NONE + * Return: NONE + * + */ +void +scan_timer(void) +{ + static time_t nc_counter; + + if (OptionsItem.negcache) + { + if (nc_counter++ >= OptionsItem.negcache_rebuild) + { + /* + * Time to rebuild the negative cache. + */ + if (OPT_DEBUG) + log_printf("SCAN -> Rebuilding negative cache"); + + negcache_rebuild(); + nc_counter = 0; + } + } +} + +/* scan_gettype(int protocol) + * + * Return human readable name of OPM PROTOCOL given OPM_TYPE_PROTOCOL + * + * Parameters: + * protocol: Protocol to return (from libopm/src/opm_types.h) + * + * Return: + * Pointer to static string containing human readable form of protocol + * name + * + */ +const char * +scan_gettype(int protocol) +{ + static const char *const undef = "undefined"; + static const struct protocol_assoc protocols[] = + { + { .type = OPM_TYPE_HTTP, .name = "HTTP" }, + { .type = OPM_TYPE_HTTPPOST, .name = "HTTPPOST" }, + { .type = OPM_TYPE_SOCKS4, .name = "SOCKS4" }, + { .type = OPM_TYPE_SOCKS5, .name = "SOCKS5" }, + { .type = OPM_TYPE_WINGATE, .name = "WINGATE" }, + { .type = OPM_TYPE_ROUTER, .name = "ROUTER" }, + { .type = OPM_TYPE_HTTPS, .name = "HTTPS" }, + { .type = OPM_TYPE_HTTPSPOST, .name = "HTTPSPOST" }, + { .type = OPM_TYPE_DREAMBOX, .name = "DREAMBOX" }, + { .type = OPM_TYPE_SSH, .name = "SSH" }, + { .type = 0 } + }; + + for (const struct protocol_assoc *tab = protocols; tab->type; ++tab) + if (protocol == tab->type) + return tab->name; + + return undef; +} + +/* scan_checkexempt + * + * Check mask against exempt list. + * + * Parameters: + * mask: Mask to check + * + * Return: + * 1 if mask is in list + * 0 if mask is not in list + */ +static int +scan_checkexempt(const char *mask, const char *ipmask) +{ + node_t *node; + + LIST_FOREACH(node, ExemptItem.masks.head) + { + const char *exempt_mask = node->data; + + if (match(exempt_mask, mask) == 0 || match(exempt_mask, ipmask) == 0) + return 1; + } + + return 0; +} + +/* scan_init + + Initialize scanner and masks list based on configuration. + + Parameters: + None + + Return: + None +*/ +void +scan_init(void) +{ + node_t *p, *p2, *p3, *p4; + + /* Setup each individual scanner */ + LIST_FOREACH(p, ScannerItemList.head) + { + struct ScannerConf *sc = p->data; + + if (OPT_DEBUG) + log_printf("SCAN -> Setting up scanner [%s]", sc->name); + + /* Build the scanner */ + struct scanner_struct *scs = xcalloc(sizeof(*scs)); + scs->scanner = opm_create(); + scs->name = xstrdup(sc->name); + + /* Setup configuration */ + opm_config(scs->scanner, OPM_CONFIG_FD_LIMIT, &sc->fd); + opm_config(scs->scanner, OPM_CONFIG_SCAN_IP, sc->target_ip); + opm_config(scs->scanner, OPM_CONFIG_SCAN_PORT, &sc->target_port); + opm_config(scs->scanner, OPM_CONFIG_TIMEOUT, &sc->timeout); + opm_config(scs->scanner, OPM_CONFIG_MAX_READ, &sc->max_read); + opm_config(scs->scanner, OPM_CONFIG_BIND_IP, sc->vhost); + + /* add target strings */ + LIST_FOREACH(p2, sc->target_string.head) + opm_config(scs->scanner, OPM_CONFIG_TARGET_STRING, p2->data); + + /* Setup callbacks */ + opm_callback(scs->scanner, OPM_CALLBACK_OPENPROXY, &scan_open_proxy, scs); + opm_callback(scs->scanner, OPM_CALLBACK_NEGFAIL, &scan_negotiation_failed, scs); + opm_callback(scs->scanner, OPM_CALLBACK_TIMEOUT, &scan_timeout, scs); + opm_callback(scs->scanner, OPM_CALLBACK_END, &scan_end, scs); + opm_callback(scs->scanner, OPM_CALLBACK_ERROR, &scan_handle_error, scs); + + /* Setup the protocols */ + LIST_FOREACH(p2, sc->protocols.head) + { + struct ProtocolConf *pc = p2->data; + + if (OPT_DEBUG >= 2) + log_printf("SCAN -> Adding protocol %s:%d to scanner [%s]", + scan_gettype(pc->type), pc->port, scs->name); + + if (opm_addtype(scs->scanner, pc->type, pc->port) == OPM_ERR_BADPROTOCOL) + log_printf("SCAN -> Error bad protocol %s:%d in scanner [%s]", + scan_gettype(pc->type), pc->port, scs->name); + } + + list_add(scs, node_create(), &SCANNERS); + } + + /* Give scanners a list of masks they scan */ + LIST_FOREACH(p, SCANNERS.head) + { + struct scanner_struct *scs = p->data; + + LIST_FOREACH(p2, UserItemList.head) + { + struct UserConf *uc = p2->data; + + LIST_FOREACH(p3, uc->scanners.head) + { + const char *scannername = p3->data; + + /* Add all these masks to scanner */ + if (strcasecmp(scannername, scs->name) == 0) + { + LIST_FOREACH(p4, uc->masks.head) + { + const char *mask = p4->data; + + if (OPT_DEBUG) + log_printf("SCAN -> Linking the mask [%s] to scanner [%s]", mask, scannername); + + list_add(xstrdup(mask), node_create(), &scs->masks); + } + + break; + } + } + } + } + + /* Initialise negative cache */ + if (OptionsItem.negcache) + { + if (OPT_DEBUG >= 2) + log_printf("SCAN -> Initializing negative cache"); + + negcache_init(); + } +} + +/* scan_connect + * + * scan_connect is called when m_notice (irc.c) matches a connection + * notice and parses the connecting user out of it. + * + * Parameters: + * user: Parsed items from the connection notice: + * user[0] = connecting users nickname + * user[1] = connecting users username + * user[2] = connecting users hostname + * user[3] = connecting users IP + * msg = Original connect notice + * Return: NONE + * + */ +void +scan_connect(const char *user[], const char *msg) +{ + node_t *p, *p2; + int ret; + + /* + * Have to use MSGLENMAX here because it is unknown what the max size of + * username/hostname can be. Some ircds use really mad values for + * these. + */ + char hostmask[MSGLENMAX]; + char addrmask[MSGLENMAX]; + + /* Check negcache before anything */ + if (negcache_check(user[3])) + { + if (OPT_DEBUG) + log_printf("SCAN -> %s!%s@%s [%s] is negatively cached. Skipping all tests.", + user[0], user[1], user[2], user[3]); + return; + } + + /* Generate user mask */ + snprintf(hostmask, sizeof(hostmask), "%s!%s@%s", user[0], user[1], user[2]); + snprintf(addrmask, sizeof(addrmask), "%s!%s@%s", user[0], user[1], user[3]); + + /* Check exempt list now that we have a mask */ + if (scan_checkexempt(hostmask, addrmask)) + { + if (OPT_DEBUG) + log_printf("SCAN -> %s [%s] is exempt from scanning", hostmask, user[3]); + + return; + } + + /* Create scan_struct */ + struct scan_struct *ss = scan_create(user, msg); + + /* Store ss in the remote struct, so that in callbacks we have ss */ + ss->remote->data = ss; + + /* Start checking our DNSBLs */ + if (LIST_SIZE(&OpmItem.blacklists)) + dnsbl_add(ss); + + /* Add ss->remote to all matching scanners */ + LIST_FOREACH(p, SCANNERS.head) + { + struct scanner_struct *scs = p->data; + + LIST_FOREACH(p2, scs->masks.head) + { + const char *scsmask = p2->data; + + if (match(scsmask, hostmask) == 0) + { + if (OPT_DEBUG) + log_printf("SCAN -> Passing %s to scanner [%s]", hostmask, scs->name); + + if ((ret = opm_scan(scs->scanner, ss->remote)) != OPM_SUCCESS) + { + switch (ret) + { + case OPM_ERR_NOPROTOCOLS: + continue; + break; + case OPM_ERR_BADADDR: + if (!strchr(ss->ip, ':')) /* XXX: hack alert. remove when libopm can deal with IPv6 addresses */ + log_printf("OPM -> Bad address %s [%s]", ss->ip, scs->name); + break; + default: + log_printf("OPM -> Unknown error %s [%s]", ss->ip, scs->name); + break; + } + } + else + ++ss->scans; /* Increase scan count only if OPM_SUCCESS */ + + break; /* Continue to next scanner */ + } + } + } + + /* All scanners returned !OPM_SUCCESS and there were no dnsbl checks */ + if (ss->scans == 0) + scan_free(ss); +} + +/* scan_create + * + * Allocate scan struct, including user information and REMOTE + * for LIBOPM. + * + * Parameters: + * user: Parsed items from the connection notice: + * user[0] = connecting users nickname + * user[1] = connecting users username + * user[2] = connecting users hostname + * user[3] = connecting users IP + * msg = Original connect notice (used as PROOF) + * + * Return: Pointer to new scan_struct + * + */ +static struct scan_struct * +scan_create(const char *user[], const char *msg) +{ + struct scan_struct *ss = xcalloc(sizeof(*ss)); + + ss->irc_nick = xstrdup(user[0]); + ss->irc_username = xstrdup(user[1]); + ss->irc_hostname = xstrdup(user[2]); + ss->ip = xstrdup(user[3]); + ss->proof = xstrdup(msg); + ss->remote = opm_remote_create(ss->ip); + + return ss; +} + +/* scan_free + * + * Free a scan_struct. This should only be done if the scan struct has + * no scans left! + * + * Parameters: + * ss: scan_struct to free + * + * Return: NONE + */ +static void +scan_free(struct scan_struct *ss) +{ + xfree(ss->irc_nick); + xfree(ss->irc_username); + xfree(ss->irc_hostname); + xfree(ss->ip); + xfree(ss->proof); + + opm_remote_free(ss->remote); + xfree(ss); +} + +/* scan_checkfinished + * + * Check if a scan is complete (ss->scans <= 0) + * and free it if need be. + */ +void +scan_checkfinished(struct scan_struct *ss) +{ + if (ss->scans <= 0) + { + if (ss->manual_target) + irc_send("PRIVMSG %s :CHECK -> All tests on %s completed.", + ss->manual_target, ss->ip); + else + { + if (OPT_DEBUG) + /* If there was a manual_target, then irc_nick, etc is NULL. */ + log_printf("SCAN -> All tests on %s!%s@%s [%s] complete.", + ss->irc_nick, ss->irc_username, ss->irc_hostname, ss->ip); + + /* Scan was a negative */ + if (ss->positive == 0) + scan_negative(ss); + } + + scan_free(ss); + } +} + +/* scan_positive + * + * Remote host (defined by ss) has been found positive by one or more + * tests. + * + * Parameters: + * ss: scan_struct containing information regarding positive host + * kline: command to send to IRC server to ban the user (see scan_irckline) + * type: string of the type of proxy found to be running on the host + * + * Return: NONE + * + */ +void +scan_positive(struct scan_struct *ss, const char *kline, const char *type) +{ + node_t *node; + + /* If already a positive, don't kline/close again */ + if (ss->positive) + return; + + /* Format KLINE and send to IRC server */ + scan_irckline(ss, kline, type); + + /* Speed up the cleanup procedure */ + /* Close all scans prematurely */ + LIST_FOREACH(node, SCANNERS.head) + { + OPM_T *scanner = ((struct scanner_struct *)node->data)->scanner; + opm_end(scanner, ss->remote); + } + + /* Set it as a positive to avoid a scan_negative call later on */ + ss->positive = 1; +} + +/* scan_open_proxy CALLBACK + * + * Called by libopm when a proxy is verified open. + * + * Parameters: + * scanner: Scanner that found the open proxy. + * remote: Remote struct containing information regarding remote end + * + * Return: NONE + */ +static void +scan_open_proxy(OPM_T *scanner, OPM_REMOTE_T *remote, int notused, void *data) +{ + struct scan_struct *ss = remote->data; + struct scanner_struct *scs = data; + + /* Record that a scan happened */ + scan_log(remote); + + if (ss->manual_target) + { + irc_send("PRIVMSG %s :CHECK -> OPEN PROXY %s:%d (%s) [%s]", + ss->manual_target, remote->ip, remote->port, + scan_gettype(remote->protocol), scs->name); + log_printf("SCAN -> OPEN PROXY %s:%d (%s) [%s]", remote->ip, + remote->port, scan_gettype(remote->protocol), scs->name); + } + else + { + /* kline and close scan */ + scan_positive(ss, IRCItem.kline, scan_gettype(remote->protocol)); + + /* Report to blacklist */ + dnsbl_report(ss); + + irc_send_channels("OPEN PROXY -> %s!%s@%s %s:%d (%s) [%s]", + ss->irc_nick, ss->irc_username, ss->irc_hostname, remote->ip, + remote->port, scan_gettype(remote->protocol), scs->name); + log_printf("SCAN -> OPEN PROXY %s!%s@%s %s:%d (%s) [%s]", + ss->irc_nick, ss->irc_username, ss->irc_hostname, remote->ip, + remote->port, scan_gettype(remote->protocol), scs->name); + } + + /* Record the proxy for stats purposes */ + stats_openproxy(remote->protocol); +} + +/* scan_negotiation_failed CALLBACK + * + * Called by libopm when negotiation of a specific protocol failed. + * + * Parameters: + * scanner: Scanner where the negotiation failed. + * remote: Remote struct containing information regarding remote end + * + * Return: NONE + * + */ +static void +scan_negotiation_failed(OPM_T *scanner, OPM_REMOTE_T *remote, int notused, void *data) +{ + const struct scanner_struct *scs = data; + + /* Record that a scan happened */ + scan_log(remote); + + if (OPT_DEBUG) + log_printf("SCAN -> Negotiation failed %s:%d (%s) [%s] (%d bytes read)", + remote->ip, remote->port, scan_gettype(remote->protocol), scs->name, + remote->bytes_read); +} + +/* scan_timeout CALLBACK + * + * Called by libopm when the negotiation of a specific protocol timed out. + * + * Parameters: + * scanner: Scanner where the connection timed out. + * remote: Remote struct containing information regarding remote end + * + * Return: NONE + * + */ +static void +scan_timeout(OPM_T *scanner, OPM_REMOTE_T *remote, int notused, void *data) +{ + const struct scanner_struct *scs = data; + + /* Record that a scan happened */ + scan_log(remote); + + if (OPT_DEBUG) + log_printf("SCAN -> Negotiation timed out %s:%d (%s) [%s] (%d bytes read)", + remote->ip, remote->port, scan_gettype(remote->protocol), scs->name, + remote->bytes_read); +} + +/* scan_end CALLBACK + * + * Called by libopm when a specific SCAN has completed (all protocols in + * that scan). + * + * Parameters: + * scanner: Scanner the scan ended on. + * remote: Remote struct containing information regarding remote end + * + * Return: NONE + */ +static void +scan_end(OPM_T *scanner, OPM_REMOTE_T *remote, int notused, void *data) +{ + struct scan_struct *ss = remote->data; + struct scanner_struct *scs = data; + + if (OPT_DEBUG) + log_printf("SCAN -> Scan %s [%s] completed", remote->ip, scs->name); + + --ss->scans; + scan_checkfinished(ss); +} + +/* scan_handle_error CALLBACK + * + * Called by libopm when an error occurs with a specific connection. This + * does not mean the entire scan has ended. + * + * Parameters: + * scanner: Scanner where the error occured. + * remote: Remote struct containing information regarding remote end + * err: OPM_ERROR code describing the error. + * + * Return: NONE + */ +static void +scan_handle_error(OPM_T *scanner, OPM_REMOTE_T *remote, int err, void *data) +{ + struct scan_struct *ss = remote->data; + struct scanner_struct *scs = data; + + switch (err) + { + case OPM_ERR_MAX_READ: + if (OPT_DEBUG >= 2) + log_printf("SCAN -> Max read on %s:%d (%s) [%s] (%d bytes read)", + remote->ip, remote->port, scan_gettype(remote->protocol), + scs->name, remote->bytes_read); + + if (ss->manual_target) + irc_send("PRIVMSG %s :CHECK -> Negotiation failed %s:%d (%s) " + "[%s] (%d bytes read)", ss->manual_target, + remote->ip, remote->port, scan_gettype(remote->protocol), + scs->name, remote->bytes_read); + break; + case OPM_ERR_BIND: + log_printf("SCAN -> Bind error on %s:%d (%s) [%s]", remote->ip, + remote->port, scan_gettype(remote->protocol), scs->name); + + if (ss->manual_target) + irc_send("PRIVMSG %s :CHECK -> Bind error on %s:%d (%s) [%s]", + ss->manual_target, remote->ip, remote->port, + scan_gettype(remote->protocol), scs->name); + break; + case OPM_ERR_NOFD: + log_printf("SCAN -> File descriptor allocation error %s:%d (%s) " + "[%s]", remote->ip, remote->port, + scan_gettype(remote->protocol), scs->name); + + if (ss->manual_target) + irc_send("PRIVMSG %s :CHECK -> Scan failed %s:%d (%s) [%s] " + "(file descriptor allocation error)", + ss->manual_target, remote->ip, remote->port, + scan_gettype(remote->protocol), scs->name); + break; + default: /* Unknown Error! */ + if (OPT_DEBUG) + log_printf("SCAN -> Unknown error %s:%d (%s) [%s]", remote->ip, + remote->port, scan_gettype(remote->protocol), scs->name); + break; + } +} + +/* scan_negative + * + * Remote host (defined by ss) has passed all tests. + * + * Parameters: + * ss: scan_struct containing information regarding negative host. + * + * Return: NONE + * + */ +static void +scan_negative(const struct scan_struct *ss) +{ + /* Insert IP in negcache */ + if (OptionsItem.negcache) + { + if (OPT_DEBUG >= 2) + log_printf("SCAN -> Adding %s to negative cache", ss->ip); + + negcache_insert(ss->ip); + } +} + +/* scan_irckline + * + * ss has been found as a positive host and is to be klined. + * Format a kline message using the kline message provided + * as a format, then pass it to irc_send() to be sent to the remote server. + * + * Parameters: + * ss: scan_struct containing information regarding host to be klined + * format: kline message to format + * type: type of proxy found (%t format character) + * + * Return: NONE + */ +static void +scan_irckline(const struct scan_struct *ss, const char *format, const char *type) +{ + char message[MSGLENMAX] = ""; /* OUTPUT */ + + unsigned int pos = 0; /* position in format */ + unsigned int len = 0; /* position in message */ + unsigned int size = 0; /* temporary size buffer */ + struct kline_format_assoc + { + const char key; + const char *data; + } table[] = + { + { 'i', ss->ip }, + { 'h', ss->irc_hostname }, + { 'u', ss->irc_username }, + { 'n', ss->irc_nick }, + { 't', type }, + { '\0', NULL } + }; + + /* + * Copy format to message character by character, inserting any matching + * data after %. + */ + while (format[pos] != '\0' && len < (MSGLENMAX - 2)) + { + switch (format[pos]) + { + case '%': + /* % is the last char in the string, move on */ + if (format[pos + 1] == '\0') + continue; + + /* %% escapes % and becomes % */ + if (format[pos + 1] == '%') + { + message[len++] = '%'; + ++pos; /* Skip past the escaped % */ + break; + } + + /* Safe to check against table now */ + for (const struct kline_format_assoc *tab = table; tab->key; ++tab) + { + if (tab->key == format[pos + 1]) + { + size = strlen(tab->data); + + /* Check if the new string can fit! */ + if ((size + len) > (MSGLENMAX - 1)) + break; + else + { + strlcat(message, tab->data, sizeof(message)); + len += size; + } + } + } + + /* Skip key character */ + ++pos; + break; + + default: + message[len++] = format[pos]; + message[len] = '\0'; + break; + } + + /* Continue to next character in format */ + ++pos; + } + + irc_send("%s", message); +} + +/* scan_manual + * + * Create a manual scan. A manual scan is a scan where the + * scan_struct contains a manual_target pointer. + */ +void +scan_manual(char *param, const char *target) +{ + char buf[INET6_ADDRSTRLEN]; + const void *addr = NULL; + const char *ip = NULL; + char *scannername; + node_t *node; + int ret, n; + struct sockaddr_storage storage; + socklen_t storage_len = 0; + + /* If there were no parameters sent, simply alert the user and return */ + if (param == NULL) + { + irc_send("PRIVMSG %s :OPM -> Invalid parameters.", target); + return; + } + + /* + * Try to extract a scanner name from param, otherwise we'll be + * adding to all scanners + */ + ip = param; + + if ((scannername = strchr(param, ' '))) + { + *scannername = '\0'; + scannername++; + } + + memset(&storage, 0, sizeof(storage)); + + if ((addr = firedns_resolveip6(ip))) + { + struct sockaddr_in6 *in = (struct sockaddr_in6 *)&storage; + + storage_len = sizeof(*in); + storage.ss_family = AF_INET6; + memcpy(&in->sin6_addr, addr, sizeof(in->sin6_addr)); + } + else if ((addr = firedns_resolveip4(ip))) + { + struct sockaddr_in *in = (struct sockaddr_in *)&storage; + + storage_len = sizeof(*in); + storage.ss_family = AF_INET; + memcpy(&in->sin_addr, addr, sizeof(in->sin_addr)); + } + else + { + irc_send("PRIVMSG %s :CHECK -> Error resolving host '%s': %s", + target, ip, firedns_strerror(firedns_errno)); + return; + } + + if ((n = getnameinfo((const struct sockaddr *)&storage, storage_len, buf, sizeof(buf), NULL, 0, NI_NUMERICHOST))) + { + irc_send("PRIVMSG %s :CHECK -> invalid address: %s", + target, gai_strerror(n)); + return; + } + + ip = buf; + + struct scan_struct *ss = xcalloc(sizeof(*ss)); + ss->ip = xstrdup(ip); + ss->remote = opm_remote_create(ss->ip); + ss->remote->data = ss; + ss->manual_target = target; + + if (scannername) + irc_send("PRIVMSG %s :CHECK -> Checking '%s' for open proxies [%s]", + target, ss->ip, scannername); + else + irc_send("PRIVMSG %s :CHECK -> Checking '%s' for open proxies on all scanners", + target, ss->ip); + + if (LIST_SIZE(&OpmItem.blacklists)) + dnsbl_add(ss); + + /* Add ss->remote to all scanners */ + LIST_FOREACH(node, SCANNERS.head) + { + struct scanner_struct *scs = node->data; + + /* + * If we have a scannername, only allow that scanner + * to be used + */ + if (scannername) + if (strcasecmp(scannername, scs->name)) + continue; + + if (OPT_DEBUG) + log_printf("SCAN -> Passing %s to scanner [%s] (MANUAL SCAN)", ss->ip, scs->name); + + if ((ret = opm_scan(scs->scanner, ss->remote)) != OPM_SUCCESS) + { + switch (ret) + { + case OPM_ERR_NOPROTOCOLS: + break; + case OPM_ERR_BADADDR: + if (!strchr(ss->ip, ':')) /* XXX: hack alert. remove when libopm can deal with IPv6 addresses */ + irc_send("PRIVMSG %s :OPM -> Bad address %s [%s]", + ss->manual_target, ss->ip, scs->name); + break; + default: + irc_send("PRIVMSG %s :OPM -> Unknown error %s [%s]", + ss->manual_target, ss->ip, scs->name); + break; + } + } + else + ++ss->scans; /* Increase scan count only if OPM_SUCCESS */ + } + + /* + * If all of the scanners gave !OPM_SUCCESS and there were no dnsbl checks, + * cleanup here + */ + if (ss->scans == 0) + { + if (scannername) + irc_send("PRIVMSG %s :CHECK -> No such scanner '%s', or '%s' has 0 protocols.", + ss->manual_target, scannername, scannername); + + irc_send("PRIVMSG %s :CHECK -> No scans active on '%s', aborting scan.", + ss->manual_target, ss->ip); + scan_free(ss); + } +} + +/* scan_log + * + * Log the fact that a given ip/port/protocol has just been scanned, if the + * user has asked for this to be logged. + * + * Parameters: + * remote: OPM_REMOTE_T for the remote end + */ +static void +scan_log(OPM_REMOTE_T *remote) +{ + struct scan_struct *ss = remote->data; + + if (!(OptionsItem.scanlog && scanlogfile)) + return; + + fprintf(scanlogfile, "[%s] %s:%d (%s) \"%s\"\n", date_iso8601(0), remote->ip, + remote->port, scan_gettype(remote->protocol), ss->proof); + fflush(scanlogfile); +} |
