/* * Copyright (C) 2002-2003 Erik Fears * * 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. * 59 Temple Place - Suite 330 * Boston, MA 02111-1307, USA. * * */ #include "modconfig.h" #include "config.h" #include "libopm.h" #include "malloc.h" #include "opm_error.h" #include "opm_types.h" #include "opm_common.h" #include "list.h" #include "inet.h" #include "proxy.h" #include #ifdef TIME_WITH_SYS_TIME # include # include #else # ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif #include #ifdef HAVE_STRING_H # include #endif RCSID("$Id$"); static OPM_PROTOCOL_CONFIG_T *libopm_protocol_config_create(void); static void libopm_protocol_config_free(OPM_PROTOCOL_CONFIG_T *); /* * XXX - does not appear to be used anywhere? * -grifferz */ #if 0 static OPM_PROTOCOL_T *libopm_protocol_create(void); static void libopm_protocol_free(OPM_PROTOCOL_T *); #endif static OPM_SCAN_T *libopm_scan_create(OPM_T *, OPM_REMOTE_T *); static void libopm_scan_free(OPM_SCAN_T *); static OPM_CONNECTION_T *libopm_connection_create(void); static void libopm_connection_free(OPM_CONNECTION_T *); static void libopm_check_establish(OPM_T *); static void libopm_check_poll(OPM_T *); static void libopm_check_closed(OPM_T *); static void libopm_check_queue(OPM_T *); static void libopm_do_connect(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); static void libopm_do_readready(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); static void libopm_do_writeready(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); static void libopm_do_hup(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); static void libopm_do_read(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); static void libopm_do_openproxy(OPM_T *, OPM_SCAN_T *, OPM_CONNECTION_T *); static void libopm_do_callback(OPM_T *, OPM_REMOTE_T *, int, int); static OPM_REMOTE_T *libopm_setup_remote(OPM_REMOTE_T *, OPM_CONNECTION_T *); /* OPM_PROTOCOLS hash * * OPM_PPROTOCOLS hashes the protocol types (int) to functions * which handle the protocol (sending/receiving protocol specific * data). * */ static OPM_PROTOCOL_T OPM_PROTOCOLS[] = { {OPM_TYPE_HTTP, libopm_proxy_http_write, NULL}, {OPM_TYPE_SOCKS4, libopm_proxy_socks4_write, NULL}, {OPM_TYPE_SOCKS5, libopm_proxy_socks5_write, NULL}, {OPM_TYPE_ROUTER, libopm_proxy_router_write, NULL}, {OPM_TYPE_WINGATE, libopm_proxy_wingate_write, NULL}, {OPM_TYPE_HTTPPOST, libopm_proxy_httppost_write, NULL} }; /* opm_create * * Initialize a new scanner and return a pointer to it. * * Parameters: * None * * Return * Pointer to new OPM_T (scanner) */ OPM_T *opm_create() { int i; OPM_T *ret; ret = MyMalloc(sizeof *ret); ret->config = libopm_config_create(); ret->scans = libopm_list_create(); ret->queue = libopm_list_create(); ret->protocols = libopm_list_create(); ret->fd_use = 0; /* Setup callbacks */ ret->callbacks = MyMalloc(sizeof(OPM_CALLBACK_T) * CBLEN); for(i = 0; i < CBLEN; i++) { ret->callbacks[i].func = NULL; ret->callbacks[i].data = NULL; } return ret; } /* opm_remote_create * * Create OPM_REMOTE_T struct, fill it with neccessary * default values and return it to the client. * * Parameters: * ip: IP of remote host * * Return: * Address of OPM_REMOTE_T created * */ OPM_REMOTE_T *opm_remote_create(const char *ip) { OPM_REMOTE_T *ret; ret = MyMalloc(sizeof *ret); /* Do initializations */ if(ip == NULL) return NULL; ret->ip = (char*) strdup(ip); /* replace with custom strdup function */ ret->port = 0; ret->protocol = 0; ret->bytes_read = 0; ret->data = NULL; ret->protocols = libopm_list_create(); /* setup protocol list */ return ret; } /* opm_remote_free * * Free OPM_REMOTE_T struct and cleanup * * Parameters: * remote: Struct to free * * Return: * None */ void opm_remote_free(OPM_REMOTE_T *remote) { OPM_NODE_T *p, *next; OPM_PROTOCOL_CONFIG_T *ppc; MyFree(remote->ip); LIST_FOREACH_SAFE(p, next, remote->protocols->head) { ppc = (OPM_PROTOCOL_CONFIG_T *) p->data; libopm_protocol_config_free(ppc); libopm_list_remove(remote->protocols, p); libopm_node_free(p); } libopm_list_free(remote->protocols); MyFree(remote); } /* opm_callback * Register scanner level callback * * Parameters * scanner: scanner struct * type: callback type * Return: * Error code */ OPM_ERR_T opm_callback(OPM_T *scanner, int type, OPM_CALLBACK_FUNC *function, void *data) { if(type < 0 || type >= (CBLEN + 1)) return OPM_ERR_CBNOTFOUND; scanner->callbacks[type].func = function; scanner->callbacks[type].data = data; return OPM_SUCCESS; } /* opm_free * * Free OPM_T (scanner) and cleanup * * Parameters: * scanner: Address of OPM_T to cleanup * * Return: * None */ void opm_free(OPM_T *scanner) { OPM_NODE_T *p, *next; OPM_PROTOCOL_CONFIG_T *ppc; OPM_SCAN_T *scan; libopm_config_free(scanner->config); LIST_FOREACH_SAFE(p, next, scanner->protocols->head) { ppc = (OPM_PROTOCOL_CONFIG_T *) p->data; libopm_protocol_config_free(ppc); libopm_list_remove(scanner->protocols, p); libopm_node_free(p); } LIST_FOREACH_SAFE(p, next, scanner->scans->head) { scan = (OPM_SCAN_T *) p->data; libopm_scan_free(scan); libopm_list_remove(scanner->scans, p); libopm_node_free(p); } LIST_FOREACH_SAFE(p, next, scanner->queue->head) { scan = (OPM_SCAN_T *) p->data; libopm_scan_free(scan); libopm_list_remove(scanner->queue, p); libopm_node_free(p); } libopm_list_free(scanner->protocols); libopm_list_free(scanner->scans); libopm_list_free(scanner->queue); MyFree(scanner->callbacks); MyFree(scanner); } /* opm_config * * Wrapper to config_set. Set configuration variables * on the config struct. * * Parameters: * scanner: OPM_T struct the config struct resides in * key: Variable within the config struct to set * value: Address of value to set variable (key) to * * Return: * OPM_ERR_T containing error code */ OPM_ERR_T opm_config(OPM_T *scanner, int key, void *value) { return libopm_config_set((scanner->config), key, value); } /* opm_addtype * * Add a proxy type and port to the list of protocols * a scanner will use. * * Parameters: * scanner: pointer to scanner struct * type: type of proxy to scan (used in hashing to the functions) * port: port this specific type/protocol will scan on * Return: * OPM_SUCCESS: Successful protocol add * OPM_ERR_BADPROTOCOL: Protocol is unknown */ OPM_ERR_T opm_addtype(OPM_T *scanner, int type, unsigned short int port) { unsigned int i; OPM_NODE_T *node; OPM_PROTOCOL_CONFIG_T *protocol_config; for(i = 0; i < sizeof(OPM_PROTOCOLS) / sizeof(OPM_PROTOCOL_T); i++) { if(type == OPM_PROTOCOLS[i].type) { protocol_config = libopm_protocol_config_create(); protocol_config->type = &OPM_PROTOCOLS[i]; protocol_config->port = port; node = libopm_node_create(protocol_config); libopm_list_add(scanner->protocols, node); return OPM_SUCCESS; } } return OPM_ERR_BADPROTOCOL; } /* opm_remote_addtype * * Add a proxy type and port to the list of protocols * a scanner will use. * * Parameters: * remote: pointer to scanner struct * type: type of proxy to scan (used in hashing to the functions) * port: port this specific type/protocol will scan on * Return: * OPM_SUCCESS: Successful protocol add * OPM_ERR_BADPROTOCOL: Protocol is unknown */ OPM_ERR_T opm_remote_addtype(OPM_REMOTE_T *remote, int type, unsigned short int port) { unsigned int i; OPM_NODE_T *node; OPM_PROTOCOL_CONFIG_T *protocol_config; for(i = 0; i < sizeof(OPM_PROTOCOLS) / sizeof(OPM_PROTOCOL_T); i++) { if(type == OPM_PROTOCOLS[i].type) { protocol_config = libopm_protocol_config_create(); protocol_config->type = &OPM_PROTOCOLS[i]; protocol_config->port = port; node = libopm_node_create(protocol_config); libopm_list_add(remote->protocols, node); return OPM_SUCCESS; } } return OPM_ERR_BADPROTOCOL; } /* libopm_protocol_create * * Create OPM_PROTOCOL_T struct. * * Parameters: * None * Return: * Pointer to new struct * * XXX - does not appear to be used anywhere? * -grifferz */ #if 0 static OPM_PROTOCOL_T *libopm_protocol_create(void) { OPM_PROTOCOL_T *ret; ret = MyMalloc(sizeof(OPM_PROTOCOL_T)); ret->type = 0; ret->write_function = NULL; ret->read_function = NULL; return ret; } #endif /* libopm_protocol_free * * Free an OPM_PROTOCOL_T struct. Assume that if * format is not NULL, it is pointed to dynamically * allocated memory and free it. * * Parameters: * protocol: struct to free * * Return: * None * * XXX - apparently no longer used? * -grifferz */ #if 0 static void libopm_protocol_free(OPM_PROTOCOL_T *protocol) { MyFree(protocol); } #endif /* libopm_protocol_config_create * * Allocate and return address of a new OPM_PROTOCOL_CONFIG_T * * Parameters: * None * * Return: * Address of new OPM_PROTOCOL_CONFIG_T */ static OPM_PROTOCOL_CONFIG_T *libopm_protocol_config_create(void) { OPM_PROTOCOL_CONFIG_T *ret; ret = MyMalloc(sizeof *ret); return ret; } /* protocol_config_free * * Free OPM_PROTOCOL_CONFIG_T struct * * Parameters: * protocol: struct to free * * Return: * None */ static void libopm_protocol_config_free(OPM_PROTOCOL_CONFIG_T *protocol) { MyFree(protocol); } /* opm_scan * * Scan remote host. The opm_scan function takes an OPM_REMOTE_T * struct, calculates the in_addr of the remote host, and creates * a scan list based on protocols defined in the scanner. * * Parameters: * scanner: Scanner to scan host on * remote: OPM_REMOTE_T defining remote host * * Return: * (to be written) */ OPM_ERR_T opm_scan(OPM_T *scanner, OPM_REMOTE_T *remote) { OPM_SCAN_T *scan; /* New scan for OPM_T */ OPM_NODE_T *node; /* Node we'll add scan to when we link it to scans */ unsigned int fd_limit; fd_limit = *(int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT); if(LIST_SIZE(scanner->protocols) == 0 && LIST_SIZE(remote->protocols) == 0) { return OPM_ERR_NOPROTOCOLS; } scan = libopm_scan_create(scanner, remote); if(inet_pton(AF_INET, remote->ip, &(scan->addr.sa4.sin_addr) ) <= 0) { libopm_scan_free(scan); return OPM_ERR_BADADDR; } node = libopm_node_create(scan); libopm_list_add(scanner->queue, node); return OPM_SUCCESS; } /* opm_end * * End a scan prematurely. * * Parameters: * scanner: Scanner to end scan on * remote: Pointer to remote struct to search for and end * * Return: * No return. OPM_CALLBACK_END will still be called as normal. */ void opm_end(OPM_T *scanner, OPM_REMOTE_T *remote) { OPM_NODE_T *node1, *node2, *next1, *next2; OPM_SCAN_T *scan; OPM_CONNECTION_T *conn; /* End active scans */ opm_endscan(scanner, remote); /* Secondly remove all traces of it in the queue. Once removed we have to call OPM_CALLBACK_END */ LIST_FOREACH_SAFE(node1, next1, scanner->queue->head) { scan = (OPM_SCAN_T *) node1->data; if(scan->remote == remote) { /* Free all connections */ LIST_FOREACH_SAFE(node2, next2, scan->connections->head) { conn = (OPM_CONNECTION_T *) node2->data; libopm_list_remove(scan->connections, node2); libopm_connection_free(conn); libopm_node_free(node2); continue; } /* OPM_CALLBACK_END because check_closed normally handles this */ libopm_do_callback(scanner, scan->remote, OPM_CALLBACK_END, 0); /* Free up the scan */ libopm_list_remove(scanner->queue, node1); libopm_scan_free(scan); libopm_node_free(node1); } } } /* opm_endscan * * End a scan prematurely. Only end non-queued scans. This is useful * because it only checks the active scan list (saving time), where * opm_end checks both the scan and the possibly large queue. * * Parameters: * scanner: Scanner to end scan on * remote: Pointer to remote struct to search for and end * * Return: * No return. OPM_CALLBACK_END will still be called as normal. */ void opm_endscan(OPM_T *scanner, OPM_REMOTE_T *remote) { OPM_NODE_T *node1, *node2; OPM_SCAN_T *scan; OPM_CONNECTION_T *conn; /* First check to see if it's in the queue, if it is set all connections closed Next cycle of libopm_check_closed will take care of the garbage and handle OPM_CALLBACK_END */ LIST_FOREACH(node1, scanner->scans->head) { scan = (OPM_SCAN_T *) node1->data; if(scan->remote == remote) { LIST_FOREACH(node2, scan->connections->head) { conn = (OPM_CONNECTION_T *) node2->data; conn->state = OPM_STATE_CLOSED; } } } } /* opm_active Return number of scans in a scanner left. Parameters: scanner: Scanner to return active scans on Return: Number of active scans, both queued and active. */ size_t opm_active(OPM_T *scanner) { return LIST_SIZE(scanner->queue) + LIST_SIZE(scanner->scans); } /* scan_create * * Create new OPM_SCAN_T struct * * Parameters: * scanner: Scanner the scan is being created for. This * is needed to get information on currently set * protocols/config. * * remote: Remote host this scan will be scanning * * Return * Address of new struct */ static OPM_SCAN_T *libopm_scan_create(OPM_T *scanner, OPM_REMOTE_T *remote) { OPM_SCAN_T *ret; OPM_CONNECTION_T *conn; OPM_NODE_T *node, *p; ret = MyMalloc(sizeof *ret); ret->remote = remote; ret->connections = libopm_list_create(); /* Setup list of connections, one for each protocol */ LIST_FOREACH(p, scanner->protocols->head) { conn = libopm_connection_create(); conn->protocol = ((OPM_PROTOCOL_CONFIG_T *) p->data)->type; conn->port = ((OPM_PROTOCOL_CONFIG_T *) p->data)->port; node = libopm_node_create(conn); libopm_list_add(ret->connections, node); } /* Do the same for any specific protocols the remote struct might be configured with */ LIST_FOREACH(p, remote->protocols->head) { conn = libopm_connection_create(); conn->protocol = ((OPM_PROTOCOL_CONFIG_T *) p->data)->type; conn->port = ((OPM_PROTOCOL_CONFIG_T *) p->data)->port; node = libopm_node_create(conn); libopm_list_add(ret->connections, node); } memset(&(ret->addr), 0, sizeof(opm_sockaddr)); return ret; } /* scan_free * * Free and cleanup OPM_SCAN_T struct * * Parametsr: * scan: Scan struct to free * * Return: * None */ static void libopm_scan_free(OPM_SCAN_T *scan) { OPM_NODE_T *p, *next; OPM_CONNECTION_T *conn; LIST_FOREACH_SAFE(p, next, scan->connections->head) { conn = (OPM_CONNECTION_T *) p->data; libopm_connection_free(conn); libopm_list_remove(scan->connections, p); libopm_node_free(p); } libopm_list_free(scan->connections); MyFree(scan); } /* connection_create * * Allocate new OPM_CONNECTION_T * * Parameters: * None * * Return: * Address of new OPM_CONNECTION_T */ static OPM_CONNECTION_T *libopm_connection_create(void) { OPM_CONNECTION_T *ret; ret = MyMalloc(sizeof *ret); ret->fd = 0; ret->bytes_read = 0; ret->readlen = 0; ret->protocol = 0; ret->port = 0; ret->state = OPM_STATE_UNESTABLISHED; return ret; } /* connection_free * * Free OPM_CONNECTION_T struct * * Parameters: * conn: Address of struct to free * * Return: * None */ static void libopm_connection_free(OPM_CONNECTION_T *conn) { MyFree(conn); } /* opm_cycle * * Perform tasks (called by client's loop) * * Parameters: * None * Return: * None */ void opm_cycle(OPM_T *scanner) { libopm_check_queue(scanner); /* Move scans from the queue to the live scan list */ libopm_check_establish(scanner); /* Make new connections if possible */ libopm_check_poll(scanner); /* Poll connections for IO and proxy test */ libopm_check_closed(scanner); /* Check for closed or timed out connections */ } /* check_queue * * Move scans from the queue to the live scan list as long as there is * room. * * Parameters: * scanner: Scanner to check queue on * * Return: * None */ static void libopm_check_queue(OPM_T *scanner) { OPM_NODE_T *node; OPM_SCAN_T *scan; unsigned int protocols, projected, fd_limit; if(LIST_SIZE(scanner->queue) == 0) return; fd_limit = *(int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT); projected = scanner->fd_use; /* We want to keep the live scan list as small as possible, so only move queued scans to the live list if they will not push above fd_limit */ while(LIST_SIZE(scanner->queue) > 0) { /* Grab the top scan */ scan = (OPM_SCAN_T *) scanner->queue->head->data; protocols = LIST_SIZE(scan->connections); /* Check if it will fit in the live scan list */ if((protocols + projected) > fd_limit) break; /* Scans on the top of the queue were added first, swap the head off the top of the queue and add it to the tail of the live scan list */ node = libopm_list_remove(scanner->queue, scanner->queue->head); libopm_list_add(scanner->scans, node); projected += protocols; } } /* check_establish * * Make new connections if there are free file descriptors and connections * to be made. * * Parameters: * scanner: Scanner to check for establish on * Return: * None */ static void libopm_check_establish(OPM_T *scanner) { OPM_NODE_T *node1, *node2; OPM_SCAN_T *scan; OPM_CONNECTION_T *conn; unsigned int fd_limit; if(LIST_SIZE(scanner->scans) == 0) return; fd_limit = *(int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT); if(scanner->fd_use >= fd_limit) return; LIST_FOREACH(node1, scanner->scans->head) { scan = (OPM_SCAN_T *) node1->data; LIST_FOREACH(node2, scan->connections->head) { /* Only scan if we have free file descriptors */ if(scanner->fd_use >= fd_limit) return; conn = (OPM_CONNECTION_T *) node2->data; if(conn->state == OPM_STATE_UNESTABLISHED) libopm_do_connect(scanner, scan, conn); } } } /* check_closed * * Check for connections which have timed out or are * closed. Connections timed out still need to be closed. * * Remove the connection from the list of connections, free * the connection struct and free the list node. Then if this is * the last connection of the scan, consider the scan completed and * free the scan aswell (and callback that the scan ended). * * Parameters: * scanner: Scanner to check on * Return: * None */ static void libopm_check_closed(OPM_T *scanner) { time_t present; OPM_NODE_T *node1, *node2, *next1, *next2; int timeout; OPM_SCAN_T *scan; OPM_CONNECTION_T *conn; if(LIST_SIZE(scanner->scans) == 0) return; time(&present); timeout = *(int *) libopm_config(scanner->config, OPM_CONFIG_TIMEOUT); LIST_FOREACH_SAFE(node1, next1, scanner->scans->head) { scan = (OPM_SCAN_T *) node1->data; LIST_FOREACH_SAFE(node2, next2, scan->connections->head) { conn = (OPM_CONNECTION_T *) node2->data; if(conn->state == OPM_STATE_CLOSED) { if(conn->fd > 0) close(conn->fd); scanner->fd_use--; libopm_list_remove(scan->connections, node2); libopm_connection_free(conn); libopm_node_free(node2); continue; } if(((present - conn->creation) >= timeout) && conn->state != OPM_STATE_UNESTABLISHED) { close(conn->fd); scanner->fd_use--; libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_TIMEOUT, 0); libopm_list_remove(scan->connections, node2); libopm_connection_free(conn); libopm_node_free(node2); continue; } } /* No more connections left in this scan, let the client know the scan has ended, then remove the scan from the scanner, and free it up */ if(LIST_SIZE(scan->connections) == 0) { libopm_do_callback(scanner, scan->remote, OPM_CALLBACK_END, 0); libopm_list_remove(scanner->scans, node1); libopm_scan_free(scan); libopm_node_free(node1); } } } /* do_connect * * Call socket() and connect() to start a scan. * * Parametsr: * scan: Scan struct containing the connection * conn: Connection to establish * Return: * None */ static void libopm_do_connect(OPM_T * scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) { opm_sockaddr *bind_ip; struct sockaddr_in *addr; /* Outgoing host */ struct sockaddr_in local_addr; /* For binding */ addr = (struct sockaddr_in *) &(scan->addr.sa4); /* Already have the IP in byte format from opm_scan */ addr->sin_family = AF_INET; addr->sin_port = htons(conn->port); bind_ip = (opm_sockaddr *) libopm_config(scanner->config, OPM_CONFIG_BIND_IP); conn->fd = socket(PF_INET, SOCK_STREAM, 0); scanner->fd_use++; /* Increase file descriptor use */ if(conn->fd == -1) { libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_ERROR, OPM_ERR_NOFD); conn->state = OPM_STATE_CLOSED; return; } if(bind_ip != NULL) { memset(&local_addr, 0, sizeof(local_addr)); local_addr.sin_addr.s_addr = bind_ip->sa4.sin_addr.s_addr; local_addr.sin_family = AF_INET; local_addr.sin_port = htons(0); if(bind(conn->fd, (struct sockaddr *) &(local_addr), sizeof(local_addr)) == -1) { libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_ERROR, OPM_ERR_BIND); conn->state = OPM_STATE_CLOSED; return; } } /* Set socket non blocking */ fcntl(conn->fd, F_SETFL, O_NONBLOCK); connect(conn->fd, (struct sockaddr *) addr, sizeof(*addr)); conn->state = OPM_STATE_ESTABLISHED; time(&(conn->creation)); /* Stamp creation time, for timeout */ } /* check_poll * * Check sockets for ready read/write * * Parameters: * scanner: Scanner to isolate check on * Return: * None */ static void libopm_check_poll(OPM_T *scanner) { OPM_NODE_T *node1, *node2; OPM_SCAN_T *scan; OPM_CONNECTION_T *conn; static unsigned int ufds_size; static struct pollfd *ufds = NULL; unsigned int size, i; size = 0; /* Grow pollfd array (ufds) as needed */ if(ufds_size < (*(unsigned int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT))) { MyFree(ufds); ufds = MyMalloc((sizeof *ufds) * (*(unsigned int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT))); ufds_size = (*(unsigned int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT)); } if(LIST_SIZE(scanner->scans) == 0) return; LIST_FOREACH(node1, scanner->scans->head) { scan = (OPM_SCAN_T *) node1->data; LIST_FOREACH(node2, scan->connections->head) { if(size >= ufds_size) break; conn = (OPM_CONNECTION_T *) node2->data; if(conn->state < OPM_STATE_ESTABLISHED || conn->state == OPM_STATE_CLOSED) continue; ufds[size].events = 0; ufds[size].revents = 0; ufds[size].fd = conn->fd; /* Check for HUNG UP. */ ufds[size].events |= POLLHUP; /* Check for INVALID FD */ ufds[size].events |= POLLNVAL; switch(conn->state) { case OPM_STATE_ESTABLISHED: ufds[size].events |= POLLOUT; break; case OPM_STATE_NEGSENT: ufds[size].events |= POLLIN; break; } size++; } } switch (poll(ufds, size, 0)) { case -1: /* error in select/poll */ return; case 0: /* Nothing to do */ return; /* Pass pointer to connection to handler. */ } LIST_FOREACH(node1, scanner->scans->head) { scan = (OPM_SCAN_T *) node1->data; LIST_FOREACH(node2, scan->connections->head) { conn = (OPM_CONNECTION_T *) node2->data; for(i = 0; i < size; i++) { if((ufds[i].fd == conn->fd) && (conn->state != OPM_STATE_CLOSED)) { if(ufds[i].revents & POLLIN) libopm_do_readready(scanner, scan, conn); if(ufds[i].revents & POLLOUT) libopm_do_writeready(scanner, scan, conn); if(ufds[i].revents & POLLHUP) libopm_do_hup(scanner, scan, conn); } } } } } /* before_poll * * external select/poll routine to setup our fd checks * * Parameters: * scanner: Scanner to isolate check on * Return: * pollfds struct. */ int libopm_before_poll(OPM_T *scanner, pollfd *ufds) { OPM_NODE_T *node1, *node2; OPM_SCAN_T *scan; OPM_CONNECTION_T *conn; unsigned int maxsize, size; size = 0; libopm_check_closed(scanner); /* Check for closed or timed out connections */ libopm_check_queue(scanner); /* Move scans from the queue to the live scan list */ libopm_check_establish(scanner); /* Make new connections if possible */ /* ufds = MyMalloc((sizeof *ufds) * (*(unsigned int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT))); */ maxsize = (*(unsigned int *) libopm_config(scanner->config, OPM_CONFIG_FD_LIMIT)); if(LIST_SIZE(scanner->scans) == 0) return -1; LIST_FOREACH(node1, scanner->scans->head) { scan = (OPM_SCAN_T *) node1->data; LIST_FOREACH(node2, scan->connections->head) { if(size >= maxsize) break; conn = (OPM_CONNECTION_T *) node2->data; if(conn->state < OPM_STATE_ESTABLISHED || conn->state == OPM_STATE_CLOSED) continue; ufds[size].events = 0; ufds[size].revents = 0; ufds[size].fd = conn->fd; /* Check for HUNG UP. */ ufds[size].events |= POLLHUP; /* Check for INVALID FD */ ufds[size].events |= POLLNVAL; switch(conn->state) { case OPM_STATE_ESTABLISHED: ufds[size].events |= POLLOUT; break; case OPM_STATE_NEGSENT: ufds[size].events |= POLLIN; break; } size++; } } return size; } /* after_poll * * external select/poll routine to check what our fd check * * Parameters: * scanner: Scanner to isolate check on * Return: * pollfds struct. */ void libopm_after_poll(OPM_T *scanner, pollfd *ufds, unsigned int ufdssize) { OPM_NODE_T *node1, *node2; OPM_SCAN_T *scan; OPM_CONNECTION_T *conn; int i; if (opm_active(scanner) < 1) { return; } LIST_FOREACH(node1, scanner->scans->head) { scan = (OPM_SCAN_T *) node1->data; LIST_FOREACH(node2, scan->connections->head) { conn = (OPM_CONNECTION_T *) node2->data; for(i = 0; i < (int)ufdssize; i++) { if((ufds[i].fd == conn->fd) && (conn->state != OPM_STATE_CLOSED)) { if(ufds[i].revents & POLLIN) libopm_do_readready(scanner, scan, conn); if(ufds[i].revents & POLLOUT) libopm_do_writeready(scanner, scan, conn); if(ufds[i].revents & POLLHUP) libopm_do_hup(scanner, scan, conn); } } } } } /* do_readready * * Remote connection is read ready, read the data into a buffer and check it against * the target_string if neccessary * * Parameters: * scanner: Scanner doing the scan * scan: Specific scan * conn: Specific connection in the scan * * Return: * None */ static void libopm_do_readready(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) { int max_read; char c; /* If protocol has a specific read function, call that instead of reading data from here. */ if(conn->protocol->read_function) { conn->protocol->read_function(scanner, scan, conn); return; } max_read = *(int *) libopm_config(scanner->config, OPM_CONFIG_MAX_READ); while(1) { switch (read(conn->fd, &c, 1)) { case 0: libopm_do_hup(scanner, scan, conn); return; case -1: if(errno != EAGAIN) libopm_do_hup(scanner, scan, conn); return; default: conn->bytes_read++; if(conn->bytes_read >= max_read) { libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_ERROR, OPM_ERR_MAX_READ); conn->state = OPM_STATE_CLOSED; return; } if(c == '\0' || c == '\r') continue; if(c == '\n') { conn->readbuf[conn->readlen] = '\0'; conn->readlen = 0; libopm_do_read(scanner, scan, conn); if(conn->state == OPM_STATE_CLOSED) return; continue; } if(conn->readlen < READBUFLEN) { /* -1 to pad for null term */ conn->readbuf[++(conn->readlen) - 1] = c; } } } } /* do_read * * A line of data has been read from the socket, check it against * target string. * * * * Parameters: * scanner: Scanner doing the scan * scan: Specific scan * conn: Specific connection in the scan * * Return: * None */ static void libopm_do_read(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) { OPM_LIST_T *list; OPM_NODE_T *node; char *target_string; /* Check readbuf against target strings */ list = (OPM_LIST_T *) libopm_config(scanner->config, OPM_CONFIG_TARGET_STRING); LIST_FOREACH(node, list->head) { target_string = (char *) node->data; if(strstr(conn->readbuf, target_string)) { libopm_do_openproxy(scanner, scan, conn); break; } } } /* do_openproxy * * An open proxy was found on connection conn. Cleanup the connection and * call the appropriate callback to let the client know the proxy was found. * * Parameters: * scanner: Scanner doing the scan * scan: Specific scan * conn: Specific connection in the scan * * Return: * None */ static void libopm_do_openproxy(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) { OPM_REMOTE_T *remote; remote = scan->remote; /* Mark the connection for close */ conn->state = OPM_STATE_CLOSED; /* Call client's open proxy callback */ libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_OPENPROXY, 0); } /* do_writeready * * Remote connection is write ready, call the specific protocol * function for writing to this socket. * * Parameters: * scanner: Scanner doing the scan * scan: Specific scan * conn: Specific connection in the scan * * Return: * None */ static void libopm_do_writeready(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) { OPM_PROTOCOL_T *protocol; protocol = conn->protocol; /* Call write function for specific protocol */ if(protocol->write_function) protocol->write_function(scanner, scan, conn); /* Flag as NEGSENT so we don't have to send data again*/ conn->state = OPM_STATE_NEGSENT; } /* do_hup * * Connection ended prematurely * * Parameters: * scanner: Scanner doing the scan * scan: Specific scan * conn: Specific connection in the scan * error: OPM_ERR_T containing the error type * Return: * None */ static void libopm_do_hup(OPM_T *scanner, OPM_SCAN_T *scan, OPM_CONNECTION_T *conn) { OPM_REMOTE_T *remote; remote = scan->remote; /* Mark the connection for close */ conn->state = OPM_STATE_CLOSED; libopm_do_callback(scanner, libopm_setup_remote(scan->remote, conn), OPM_CALLBACK_NEGFAIL, 0); } /* do_callback * * Call callback * * Parameters: * scanner: scanner remote is on * remote: remote callback is for * type: callback type * var: optional var passed back (error codes, etc ) * Return: * None */ static void libopm_do_callback(OPM_T *scanner, OPM_REMOTE_T *remote, int type, int var) { /* Callback is out of range */ if(type < 0 || type >= (CBLEN + 1)) return; if(scanner->callbacks[type].func) (scanner->callbacks[type].func) (scanner, remote, var, scanner->callbacks[type].data); } /* setup_remote * * Setup an OPM_REMOTE_T with information from an OPM_CONNECTION_T * for callback * * Parameters: * remote, conn * * Return: * remote */ static OPM_REMOTE_T *libopm_setup_remote(OPM_REMOTE_T *remote, OPM_CONNECTION_T *conn) { remote->port = conn->port; remote->bytes_read = conn->bytes_read; remote->protocol = conn->protocol->type; return remote; }