Example #1
0
 /**
  * Private constructor.
  * It parses PHP server variables, and initializes its variables.
  */
 private function __construct()
 {
     // This is the pattern of a local (or unknown) IP address in both IPv4 and IPv6
     $local_ip_pattern = '((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown|::1|fe80::|fc00::)';
     // Client IP: REMOTE_ADDR, unless missing
     if (!isset($_SERVER['REMOTE_ADDR'])) {
         // Command line, or else.
         $this->_client_ip = '';
     } elseif (!isValidIPv6($_SERVER['REMOTE_ADDR']) || preg_match('~::ffff:\\d+\\.\\d+\\.\\d+\\.\\d+~', $_SERVER['REMOTE_ADDR']) !== 0) {
         $this->_client_ip = preg_replace('~^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)~', '\\1', $_SERVER['REMOTE_ADDR']);
         // Just incase we have a legacy IPv4 address.
         // @ TODO: Convert to IPv6.
         if (filter_var($this->_client_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) {
             $this->_client_ip = 'unknown';
         }
     } else {
         $this->_client_ip = $_SERVER['REMOTE_ADDR'];
     }
     // Second IP, guesswork it is, try to get the best IP we can, when using proxies or such
     $this->_ban_ip = $this->_client_ip;
     // Forwarded, maybe?
     if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_CLIENT_IP']) && (preg_match('~^' . $local_ip_pattern . '~', $_SERVER['HTTP_CLIENT_IP']) == 0 || preg_match('~^' . $local_ip_pattern . '~', $this->_client_ip) != 0)) {
         // check the first forwarded for as the block - only switch if it's better that way.
         if (strtok($_SERVER['HTTP_X_FORWARDED_FOR'], '.') != strtok($_SERVER['HTTP_CLIENT_IP'], '.') && '.' . strtok($_SERVER['HTTP_X_FORWARDED_FOR'], '.') == strrchr($_SERVER['HTTP_CLIENT_IP'], '.') && (preg_match('~^((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown)~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 || preg_match('~^((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown)~', $this->_client_ip) != 0)) {
             $this->_ban_ip = implode('.', array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])));
         } else {
             $this->_ban_ip = $_SERVER['HTTP_CLIENT_IP'];
         }
     }
     if (!empty($_SERVER['HTTP_CLIENT_IP']) && (preg_match('~^' . $local_ip_pattern . '~', $_SERVER['HTTP_CLIENT_IP']) == 0 || preg_match('~^' . $local_ip_pattern . '~', $this->_client_ip) != 0)) {
         // Since they are in different blocks, it's probably reversed.
         if (strtok($this->_client_ip, '.') != strtok($_SERVER['HTTP_CLIENT_IP'], '.')) {
             $this->_ban_ip = implode('.', array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])));
         } else {
             $this->_ban_ip = $_SERVER['HTTP_CLIENT_IP'];
         }
     } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
         // If there are commas, get the last one.. probably.
         if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false) {
             $ips = array_reverse(explode(', ', $_SERVER['HTTP_X_FORWARDED_FOR']));
             // Go through each IP...
             foreach ($ips as $i => $ip) {
                 // Make sure it's in a valid range...
                 if (preg_match('~^' . $local_ip_pattern . '~', $ip) != 0 && preg_match('~^' . $local_ip_pattern . '~', $this->_client_ip) == 0) {
                     continue;
                 }
                 // Otherwise, we've got an IP!
                 $this->_ban_ip = trim($ip);
                 break;
             }
         } elseif (preg_match('~^' . $local_ip_pattern . '~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 || preg_match('~^' . $local_ip_pattern . '~', $this->_client_ip) != 0) {
             $this->_ban_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
         }
     }
     // Some final checking.
     if (filter_var($this->_ban_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false && !isValidIPv6($this->_ban_ip)) {
         $this->_ban_ip = '';
     }
     if ($this->_client_ip == 'unknown') {
         $this->_client_ip = '';
     }
     // Keep compatibility with the uses of $_SERVER['REMOTE_ADDR']...
     $_SERVER['REMOTE_ADDR'] = $this->_client_ip;
     // Set the scheme, for later use
     $this->_scheme = !empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on' ? 'https' : 'http';
     // Make sure we know everything about you... HTTP_USER_AGENT!
     $this->_user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? htmlspecialchars($_SERVER['HTTP_USER_AGENT'], ENT_QUOTES, 'UTF-8') : '';
     // Keep compatibility with the uses of $_SERVER['HTTP_USER_AGENT']...
     $_SERVER['HTTP_USER_AGENT'] = $this->_user_agent;
     // We want to know who we are, too :P
     $this->_server_software = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';
 }
Example #2
0
/**
 * @todo needs a description
 *
 * @param int $memID = 0 id_member
 */
function TrackIP($memID = 0)
{
    global $user_profile, $scripturl, $txt, $user_info, $modSettings, $sourcedir;
    global $context, $smcFunc;
    // Can the user do this?
    isAllowedTo('moderate_forum');
    if ($memID == 0) {
        $context['ip'] = $user_info['ip'];
        loadTemplate('Profile');
        loadLanguage('Profile');
        $context['sub_template'] = 'trackIP';
        $context['page_title'] = $txt['profile'];
        $context['base_url'] = $scripturl . '?action=trackip';
    } else {
        $context['ip'] = $user_profile[$memID]['member_ip'];
        $context['base_url'] = $scripturl . '?action=profile;area=tracking;sa=ip;u=' . $memID;
    }
    // Searching?
    if (isset($_REQUEST['searchip'])) {
        $context['ip'] = trim($_REQUEST['searchip']);
    }
    if (preg_match('/^\\d{1,3}\\.(\\d{1,3}|\\*)\\.(\\d{1,3}|\\*)\\.(\\d{1,3}|\\*)$/', $context['ip']) == 0 && isValidIPv6($context['ip']) === false) {
        fatal_lang_error('invalid_tracking_ip', false);
    }
    $ip_var = str_replace('*', '%', $context['ip']);
    $ip_string = strpos($ip_var, '%') === false ? '= {string:ip_address}' : 'LIKE {string:ip_address}';
    if (empty($context['tracking_area'])) {
        $context['page_title'] = $txt['trackIP'] . ' - ' . $context['ip'];
    }
    $request = $smcFunc['db_query']('', '
		SELECT id_member, real_name AS display_name, member_ip
		FROM {db_prefix}members
		WHERE member_ip ' . $ip_string, array('ip_address' => $ip_var));
    $context['ips'] = array();
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        $context['ips'][$row['member_ip']][] = '<a href="' . $scripturl . '?action=profile;u=' . $row['id_member'] . '">' . $row['display_name'] . '</a>';
    }
    $smcFunc['db_free_result']($request);
    ksort($context['ips']);
    // Gonna want this for the list.
    require_once $sourcedir . '/Subs-List.php';
    // Start with the user messages.
    $listOptions = array('id' => 'track_message_list', 'title' => $txt['messages_from_ip'] . ' ' . $context['ip'], 'start_var_name' => 'messageStart', 'items_per_page' => $modSettings['defaultMaxMessages'], 'no_items_label' => $txt['no_messages_from_ip'], 'base_href' => $context['base_url'] . ';searchip=' . $context['ip'], 'default_sort_col' => 'date', 'get_items' => array('function' => 'list_getIPMessages', 'params' => array('m.poster_ip ' . $ip_string, array('ip_address' => $ip_var))), 'get_count' => array('function' => 'list_getIPMessageCount', 'params' => array('m.poster_ip ' . $ip_string, array('ip_address' => $ip_var))), 'columns' => array('ip_address' => array('header' => array('value' => $txt['ip_address']), 'data' => array('sprintf' => array('format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a>', 'params' => array('ip' => false))), 'sort' => array('default' => 'INET_ATON(m.poster_ip)', 'reverse' => 'INET_ATON(m.poster_ip) DESC')), 'poster' => array('header' => array('value' => $txt['poster']), 'data' => array('db' => 'member_link')), 'subject' => array('header' => array('value' => $txt['subject']), 'data' => array('sprintf' => array('format' => '<a href="' . $scripturl . '?topic=%1$s.msg%2$s#msg%2$s" rel="nofollow">%3$s</a>', 'params' => array('topic' => false, 'id' => false, 'subject' => false)))), 'date' => array('header' => array('value' => $txt['date']), 'data' => array('db' => 'time'), 'sort' => array('default' => 'm.id_msg DESC', 'reverse' => 'm.id_msg'))), 'additional_rows' => array(array('position' => 'after_title', 'value' => $txt['messages_from_ip_desc'], 'class' => 'windowbg2', 'style' => 'padding: 1ex 2ex;')));
    // Create the messages list.
    createList($listOptions);
    // Set the options for the error lists.
    $listOptions = array('id' => 'track_user_list', 'title' => $txt['errors_from_ip'] . ' ' . $context['ip'], 'start_var_name' => 'errorStart', 'items_per_page' => $modSettings['defaultMaxMessages'], 'no_items_label' => $txt['no_errors_from_ip'], 'base_href' => $context['base_url'] . ';searchip=' . $context['ip'], 'default_sort_col' => 'date2', 'get_items' => array('function' => 'list_getUserErrors', 'params' => array('le.ip ' . $ip_string, array('ip_address' => $ip_var))), 'get_count' => array('function' => 'list_getUserErrorCount', 'params' => array('ip ' . $ip_string, array('ip_address' => $ip_var))), 'columns' => array('ip_address2' => array('header' => array('value' => $txt['ip_address']), 'data' => array('sprintf' => array('format' => '<a href="' . $context['base_url'] . ';searchip=%1$s">%1$s</a>', 'params' => array('ip' => false))), 'sort' => array('default' => 'INET_ATON(le.ip)', 'reverse' => 'INET_ATON(le.ip) DESC')), 'display_name' => array('header' => array('value' => $txt['display_name']), 'data' => array('db' => 'member_link')), 'message' => array('header' => array('value' => $txt['message']), 'data' => array('sprintf' => array('format' => '%1$s<br /><a href="%2$s">%2$s</a>', 'params' => array('message' => false, 'url' => false)))), 'date2' => array('header' => array('value' => $txt['date']), 'data' => array('db' => 'time'), 'sort' => array('default' => 'le.id_error DESC', 'reverse' => 'le.id_error'))), 'additional_rows' => array(array('position' => 'after_title', 'value' => $txt['errors_from_ip_desc'], 'class' => 'windowbg2', 'style' => 'padding: 1ex 2ex;')));
    // Create the error list.
    createList($listOptions);
    $context['single_ip'] = strpos($context['ip'], '*') === false;
    if ($context['single_ip']) {
        $context['whois_servers'] = array('afrinic' => array('name' => $txt['whois_afrinic'], 'url' => 'http://www.afrinic.net/cgi-bin/whois?searchtext=' . $context['ip'], 'range' => array(41, 154, 196)), 'apnic' => array('name' => $txt['whois_apnic'], 'url' => 'http://wq.apnic.net/apnic-bin/whois.pl?searchtext=' . $context['ip'], 'range' => array(58, 59, 60, 61, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 133, 150, 153, 163, 171, 202, 203, 210, 211, 218, 219, 220, 221, 222)), 'arin' => array('name' => $txt['whois_arin'], 'url' => 'http://whois.arin.net/rest/ip/' . $context['ip'], 'range' => array(7, 24, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 96, 97, 98, 99, 128, 129, 130, 131, 132, 134, 135, 136, 137, 138, 139, 140, 142, 143, 144, 146, 147, 148, 149, 152, 155, 156, 157, 158, 159, 160, 161, 162, 164, 165, 166, 167, 168, 169, 170, 172, 173, 174, 192, 198, 199, 204, 205, 206, 207, 208, 209, 216)), 'lacnic' => array('name' => $txt['whois_lacnic'], 'url' => 'http://lacnic.net/cgi-bin/lacnic/whois?query=' . $context['ip'], 'range' => array(186, 187, 189, 190, 191, 200, 201)), 'ripe' => array('name' => $txt['whois_ripe'], 'url' => 'http://www.db.ripe.net/whois?searchtext=' . $context['ip'], 'range' => array(62, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 141, 145, 151, 188, 193, 194, 195, 212, 213, 217)));
        foreach ($context['whois_servers'] as $whois) {
            // Strip off the "decimal point" and anything following...
            if (in_array((int) $context['ip'], $whois['range'])) {
                $context['auto_whois_server'] = $whois;
            }
        }
    }
}
Example #3
0
/**
 * Another helper function that put together the
 * @param string $fullip An IP address either IPv6 or not
 * @return string A SQL condition
 */
function constructBanQueryIP($fullip)
{
    // First attempt a IPv6 address.
    if (isValidIPv6($fullip)) {
        $ip_parts = convertIPv6toInts($fullip);
        $ban_query = '((' . $ip_parts[0] . ' BETWEEN bi.ip_low1 AND bi.ip_high1)
			AND (' . $ip_parts[1] . ' BETWEEN bi.ip_low2 AND bi.ip_high2)
			AND (' . $ip_parts[2] . ' BETWEEN bi.ip_low3 AND bi.ip_high3)
			AND (' . $ip_parts[3] . ' BETWEEN bi.ip_low4 AND bi.ip_high4)
			AND (' . $ip_parts[4] . ' BETWEEN bi.ip_low5 AND bi.ip_high5)
			AND (' . $ip_parts[5] . ' BETWEEN bi.ip_low6 AND bi.ip_high6)
			AND (' . $ip_parts[6] . ' BETWEEN bi.ip_low7 AND bi.ip_high7)
			AND (' . $ip_parts[7] . ' BETWEEN bi.ip_low8 AND bi.ip_high8))';
    } elseif (preg_match('/^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$/', $fullip, $ip_parts) == 1) {
        $ban_query = '((' . $ip_parts[1] . ' BETWEEN bi.ip_low1 AND bi.ip_high1)
			AND (' . $ip_parts[2] . ' BETWEEN bi.ip_low2 AND bi.ip_high2)
			AND (' . $ip_parts[3] . ' BETWEEN bi.ip_low3 AND bi.ip_high3)
			AND (' . $ip_parts[4] . ' BETWEEN bi.ip_low4 AND bi.ip_high4))';
    } else {
        $ban_query = '(bi.ip_low1 = 255 AND bi.ip_high1 = 255
			AND bi.ip_low2 = 255 AND bi.ip_high2 = 255
			AND bi.ip_low3 = 255 AND bi.ip_high3 = 255
			AND bi.ip_low4 = 255 AND bi.ip_high4 = 255)';
    }
    return $ban_query;
}
Example #4
0
function cleanRequest()
{
    global $board, $topic, $boardurl, $scripturl, $modSettings, $smcFunc;
    // Makes it easier to refer to things this way.
    $scripturl = $boardurl . '/index.php';
    // What function to use to reverse magic quotes - if sybase is on we assume that the database sensibly has the right unescape function!
    $removeMagicQuoteFunction = ini_get('magic_quotes_sybase') || strtolower(ini_get('magic_quotes_sybase')) == 'on' ? 'unescapestring__recursive' : 'stripslashes__recursive';
    // Save some memory.. (since we don't use these anyway.)
    unset($GLOBALS['HTTP_POST_VARS'], $GLOBALS['HTTP_POST_VARS']);
    unset($GLOBALS['HTTP_POST_FILES'], $GLOBALS['HTTP_POST_FILES']);
    // These keys shouldn't be set...ever.
    if (isset($_REQUEST['GLOBALS']) || isset($_COOKIE['GLOBALS'])) {
        die('Invalid request variable.');
    }
    // Same goes for numeric keys.
    foreach (array_merge(array_keys($_POST), array_keys($_GET), array_keys($_FILES)) as $key) {
        if (is_numeric($key)) {
            die('Numeric request keys are invalid.');
        }
    }
    // Numeric keys in cookies are less of a problem. Just unset those.
    foreach ($_COOKIE as $key => $value) {
        if (is_numeric($key)) {
            unset($_COOKIE[$key]);
        }
    }
    // Get the correct query string.  It may be in an environment variable...
    if (!isset($_SERVER['QUERY_STRING'])) {
        $_SERVER['QUERY_STRING'] = getenv('QUERY_STRING');
    }
    // It seems that sticking a URL after the query string is mighty common, well, it's evil - don't.
    if (strpos($_SERVER['QUERY_STRING'], 'http') === 0) {
        header('HTTP/1.1 400 Bad Request');
        die;
    }
    // Are we going to need to parse the ; out?
    if (strpos(ini_get('arg_separator.input'), ';') === false && !empty($_SERVER['QUERY_STRING'])) {
        // Get rid of the old one! You don't know where it's been!
        $_GET = array();
        // Was this redirected? If so, get the REDIRECT_QUERY_STRING.
        // Do not urldecode() the querystring, unless you so much wish to break OpenID implementation. :)
        $_SERVER['QUERY_STRING'] = substr($_SERVER['QUERY_STRING'], 0, 5) === 'url=/' ? $_SERVER['REDIRECT_QUERY_STRING'] : $_SERVER['QUERY_STRING'];
        // Replace ';' with '&' and '&something&' with '&something=&'.  (this is done for compatibility...)
        // @todo smflib
        parse_str(preg_replace('/&(\\w+)(?=&|$)/', '&$1=', strtr($_SERVER['QUERY_STRING'], array(';?' => '&', ';' => '&', '%00' => '', "" => ''))), $_GET);
        // Magic quotes still applies with parse_str - so clean it up.
        if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0 && empty($modSettings['integrate_magic_quotes'])) {
            $_GET = $removeMagicQuoteFunction($_GET);
        }
    } elseif (strpos(ini_get('arg_separator.input'), ';') !== false) {
        if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0 && empty($modSettings['integrate_magic_quotes'])) {
            $_GET = $removeMagicQuoteFunction($_GET);
        }
        // Search engines will send action=profile%3Bu=1, which confuses PHP.
        foreach ($_GET as $k => $v) {
            if ((string) $v === $v && strpos($k, ';') !== false) {
                $temp = explode(';', $v);
                $_GET[$k] = $temp[0];
                for ($i = 1, $n = count($temp); $i < $n; $i++) {
                    @(list($key, $val) = @explode('=', $temp[$i], 2));
                    if (!isset($_GET[$key])) {
                        $_GET[$key] = $val;
                    }
                }
            }
            // This helps a lot with integration!
            if (strpos($k, '?') === 0) {
                $_GET[substr($k, 1)] = $v;
                unset($_GET[$k]);
            }
        }
    }
    // There's no query string, but there is a URL... try to get the data from there.
    if (!empty($_SERVER['REQUEST_URI'])) {
        // Remove the .html, assuming there is one.
        if (substr($_SERVER['REQUEST_URI'], strrpos($_SERVER['REQUEST_URI'], '.'), 4) == '.htm') {
            $request = substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '.'));
        } else {
            $request = $_SERVER['REQUEST_URI'];
        }
        // @todo smflib.
        // Replace 'index.php/a,b,c/d/e,f' with 'a=b,c&d=&e=f' and parse it into $_GET.
        if (strpos($request, basename($scripturl) . '/') !== false) {
            parse_str(substr(preg_replace('/&(\\w+)(?=&|$)/', '&$1=', strtr(preg_replace('~/([^,/]+),~', '/$1=', substr($request, strpos($request, basename($scripturl)) + strlen(basename($scripturl)))), '/', '&')), 1), $temp);
            if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0 && empty($modSettings['integrate_magic_quotes'])) {
                $temp = $removeMagicQuoteFunction($temp);
            }
            $_GET += $temp;
        }
    }
    // If magic quotes is on we have some work...
    if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0) {
        $_ENV = $removeMagicQuoteFunction($_ENV);
        $_POST = $removeMagicQuoteFunction($_POST);
        $_COOKIE = $removeMagicQuoteFunction($_COOKIE);
        foreach ($_FILES as $k => $dummy) {
            if (isset($_FILES[$k]['name'])) {
                $_FILES[$k]['name'] = $removeMagicQuoteFunction($_FILES[$k]['name']);
            }
        }
    }
    // Add entities to GET.  This is kinda like the slashes on everything else.
    $_GET = htmlspecialchars__recursive($_GET);
    // Let's not depend on the ini settings... why even have COOKIE in there, anyway?
    $_REQUEST = $_POST + $_GET;
    // Make sure $board and $topic are numbers.
    if (isset($_REQUEST['board'])) {
        // Make sure its a string and not something else like an array
        $_REQUEST['board'] = (string) $_REQUEST['board'];
        // If there's a slash in it, we've got a start value! (old, compatible links.)
        if (strpos($_REQUEST['board'], '/') !== false) {
            list($_REQUEST['board'], $_REQUEST['start']) = explode('/', $_REQUEST['board']);
        } elseif (strpos($_REQUEST['board'], '.') !== false) {
            list($_REQUEST['board'], $_REQUEST['start']) = explode('.', $_REQUEST['board']);
        }
        // Now make absolutely sure it's a number.
        $board = (int) $_REQUEST['board'];
        $_REQUEST['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
        // This is for "Who's Online" because it might come via POST - and it should be an int here.
        $_GET['board'] = $board;
    } else {
        $board = 0;
    }
    // If there's a threadid, it's probably an old YaBB SE link.  Flow with it.
    if (isset($_REQUEST['threadid']) && !isset($_REQUEST['topic'])) {
        $_REQUEST['topic'] = $_REQUEST['threadid'];
    }
    // We've got topic!
    if (isset($_REQUEST['topic'])) {
        // Make sure its a string and not something else like an array
        $_REQUEST['topic'] = (string) $_REQUEST['topic'];
        // Slash means old, beta style, formatting.  That's okay though, the link should still work.
        if (strpos($_REQUEST['topic'], '/') !== false) {
            list($_REQUEST['topic'], $_REQUEST['start']) = explode('/', $_REQUEST['topic']);
        } elseif (strpos($_REQUEST['topic'], '.') !== false) {
            list($_REQUEST['topic'], $_REQUEST['start']) = explode('.', $_REQUEST['topic']);
        }
        $topic = (int) $_REQUEST['topic'];
        // Now make sure the online log gets the right number.
        $_GET['topic'] = $topic;
    } else {
        $topic = 0;
    }
    // There should be a $_REQUEST['start'], some at least.  If you need to default to other than 0, use $_GET['start'].
    if (empty($_REQUEST['start']) || $_REQUEST['start'] < 0 || (int) $_REQUEST['start'] > 2147473647) {
        $_REQUEST['start'] = 0;
    }
    // The action needs to be a string and not an array or anything else
    if (isset($_REQUEST['action'])) {
        $_REQUEST['action'] = (string) $_REQUEST['action'];
    }
    if (isset($_GET['action'])) {
        $_GET['action'] = (string) $_GET['action'];
    }
    // Make sure we have a valid REMOTE_ADDR.
    if (!isset($_SERVER['REMOTE_ADDR'])) {
        $_SERVER['REMOTE_ADDR'] = '';
        // A new magic variable to indicate we think this is command line.
        $_SERVER['is_cli'] = true;
    } elseif (!isValidIPv6($_SERVER['REMOTE_ADDR']) || preg_match('~::ffff:\\d+\\.\\d+\\.\\d+\\.\\d+~', $_SERVER['REMOTE_ADDR']) !== 0) {
        $_SERVER['REMOTE_ADDR'] = preg_replace('~^::ffff:(\\d+\\.\\d+\\.\\d+\\.\\d+)~', '\\1', $_SERVER['REMOTE_ADDR']);
        // Just incase we have a legacy IPv4 address.
        // @ TODO: Convert to IPv6.
        if (preg_match('~^((([1]?\\d)?\\d|2[0-4]\\d|25[0-5])\\.){3}(([1]?\\d)?\\d|2[0-4]\\d|25[0-5])$~', $_SERVER['REMOTE_ADDR']) === 0) {
            $_SERVER['REMOTE_ADDR'] = 'unknown';
        }
    }
    // Try to calculate their most likely IP for those people behind proxies (And the like).
    $_SERVER['BAN_CHECK_IP'] = $_SERVER['REMOTE_ADDR'];
    // Find the user's IP address. (but don't let it give you 'unknown'!)
    // @ TODO: IPv6 really doesn't need this.
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_CLIENT_IP']) && (preg_match('~^((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown|::1|fe80::|fc00::)~', $_SERVER['HTTP_CLIENT_IP']) == 0 || preg_match('~^((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown|::1|fe80::|fc00::)~', $_SERVER['REMOTE_ADDR']) != 0)) {
        // We have both forwarded for AND client IP... check the first forwarded for as the block - only switch if it's better that way.
        if (strtok($_SERVER['HTTP_X_FORWARDED_FOR'], '.') != strtok($_SERVER['HTTP_CLIENT_IP'], '.') && '.' . strtok($_SERVER['HTTP_X_FORWARDED_FOR'], '.') == strrchr($_SERVER['HTTP_CLIENT_IP'], '.') && (preg_match('~^((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown)~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 || preg_match('~^((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0)) {
            $_SERVER['BAN_CHECK_IP'] = implode('.', array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])));
        } else {
            $_SERVER['BAN_CHECK_IP'] = $_SERVER['HTTP_CLIENT_IP'];
        }
    }
    if (!empty($_SERVER['HTTP_CLIENT_IP']) && (preg_match('~^((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown|::1|fe80::|fc00::)~', $_SERVER['HTTP_CLIENT_IP']) == 0 || preg_match('~^((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown|::1|fe80::|fc00::)~', $_SERVER['REMOTE_ADDR']) != 0)) {
        // Since they are in different blocks, it's probably reversed.
        if (strtok($_SERVER['REMOTE_ADDR'], '.') != strtok($_SERVER['HTTP_CLIENT_IP'], '.')) {
            $_SERVER['BAN_CHECK_IP'] = implode('.', array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])));
        } else {
            $_SERVER['BAN_CHECK_IP'] = $_SERVER['HTTP_CLIENT_IP'];
        }
    } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        // If there are commas, get the last one.. probably.
        if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false) {
            $ips = array_reverse(explode(', ', $_SERVER['HTTP_X_FORWARDED_FOR']));
            // Go through each IP...
            foreach ($ips as $i => $ip) {
                // Make sure it's in a valid range...
                if (preg_match('~^((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown|::1|fe80::|fc00::)~', $ip) != 0 && preg_match('~^((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown|::1|fe80::|fc00::)~', $_SERVER['REMOTE_ADDR']) == 0) {
                    continue;
                }
                // Otherwise, we've got an IP!
                $_SERVER['BAN_CHECK_IP'] = trim($ip);
                break;
            }
        } elseif (preg_match('~^((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown|::1|fe80::|fc00::)~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 || preg_match('~^((0|10|172\\.(1[6-9]|2[0-9]|3[01])|192\\.168|255|127)\\.|unknown|::1|fe80::|fc00::)~', $_SERVER['REMOTE_ADDR']) != 0) {
            $_SERVER['BAN_CHECK_IP'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
        }
    }
    // Make sure we know the URL of the current request.
    if (empty($_SERVER['REQUEST_URI'])) {
        $_SERVER['REQUEST_URL'] = $scripturl . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '');
    } elseif (preg_match('~^([^/]+//[^/]+)~', $scripturl, $match) == 1) {
        $_SERVER['REQUEST_URL'] = $match[1] . $_SERVER['REQUEST_URI'];
    } else {
        $_SERVER['REQUEST_URL'] = $_SERVER['REQUEST_URI'];
    }
    // And make sure HTTP_USER_AGENT is set.
    $_SERVER['HTTP_USER_AGENT'] = isset($_SERVER['HTTP_USER_AGENT']) ? htmlspecialchars(stripslashes($_SERVER['HTTP_USER_AGENT']), ENT_QUOTES) : '';
    // Some final checking.
    if (preg_match('~^((([1]?\\d)?\\d|2[0-4]\\d|25[0-5])\\.){3}(([1]?\\d)?\\d|2[0-4]\\d|25[0-5])$~', $_SERVER['BAN_CHECK_IP']) === 0 || !isValidIPv6($_SERVER['BAN_CHECK_IP'])) {
        $_SERVER['BAN_CHECK_IP'] = '';
    }
    if ($_SERVER['REMOTE_ADDR'] == 'unknown') {
        $_SERVER['REMOTE_ADDR'] = '';
    }
}
Example #5
0
    /**
     * This function is behind the screen for adding new bans and modifying existing ones.
     *
     * Adding new bans:
     *  - is accesssed by ?action=admin;area=ban;sa=add.
     *  - uses the ban_edit sub template of the ManageBans template.
     *
     * Modifying existing bans:
     *  - is accesssed by ?action=admin;area=ban;sa=edit;bg=x
     *  - uses the ban_edit sub template of the ManageBans template.
     *  - shows a list of ban triggers for the specified ban.
     */
    public function action_edit()
    {
        global $txt, $modSettings, $context, $scripturl;
        require_once SUBSDIR . '/Bans.subs.php';
        $ban_errors = Error_Context::context('ban', 1);
        // Saving a new or edited ban?
        if ((isset($_POST['add_ban']) || isset($_POST['modify_ban']) || isset($_POST['remove_selection'])) && !$ban_errors->hasErrors()) {
            $this->action_edit2();
        }
        $ban_group_id = isset($context['ban']['id']) ? $context['ban']['id'] : (isset($_REQUEST['bg']) ? (int) $_REQUEST['bg'] : 0);
        // Template needs this to show errors using javascript
        loadLanguage('Errors');
        createToken('admin-bet');
        $context['form_url'] = $scripturl . '?action=admin;area=ban;sa=edit';
        // Prepare any errors found to the template to show
        $context['ban_errors'] = array('errors' => $ban_errors->prepareErrors(), 'type' => $ban_errors->getErrorType() == 0 ? 'minor' : 'serious', 'title' => $txt['ban_errors_detected']);
        if (!$ban_errors->hasErrors()) {
            // If we're editing an existing ban, get it from the database.
            if (!empty($ban_group_id)) {
                $context['ban_group_id'] = $ban_group_id;
                // We're going to want this for making our list.
                require_once SUBSDIR . '/GenericList.class.php';
                // Setup for a createlist
                $listOptions = array('id' => 'ban_items', 'base_href' => $scripturl . '?action=admin;area=ban;sa=edit;bg=' . $ban_group_id, 'no_items_label' => $txt['ban_no_triggers'], 'items_per_page' => $modSettings['defaultMaxMessages'], 'get_items' => array('function' => 'list_getBanItems', 'params' => array('ban_group_id' => $ban_group_id)), 'get_count' => array('function' => 'list_getNumBanItems', 'params' => array('ban_group_id' => $ban_group_id)), 'columns' => array('type' => array('header' => array('value' => $txt['ban_banned_entity'], 'style' => 'width: 60%;'), 'data' => array('function' => create_function('$ban_item', '
									global $txt;

									if (in_array($ban_item[\'type\'], array(\'ip\', \'hostname\', \'email\')))
										return \'<strong>\' . $txt[$ban_item[\'type\']] . \':</strong>&nbsp;\' . $ban_item[$ban_item[\'type\']];
									elseif ($ban_item[\'type\'] == \'user\')
										return \'<strong>\' . $txt[\'username\'] . \':</strong>&nbsp;\' . $ban_item[\'user\'][\'link\'];
									else
										return \'<strong>\' . $txt[\'unknown\'] . \':</strong>&nbsp;\' . $ban_item[\'no_bantype_selected\'];
								'))), 'hits' => array('header' => array('value' => $txt['ban_hits'], 'style' => 'width: 15%;text-align: center'), 'data' => array('db' => 'hits', 'class' => 'centertext')), 'id' => array('header' => array('value' => $txt['ban_actions'], 'style' => 'width: 15%;'), 'data' => array('function' => create_function('$ban_item', '
									global $txt, $context, $scripturl;

									return \'<a href="\' . $scripturl . \'?action=admin;area=ban;sa=edittrigger;bg=\' . $context[\'ban\'][\'id\'] . \';bi=\' . $ban_item[\'id\'] . \'">\' . $txt[\'ban_edit_trigger\'] . \'</a>\';
								'))), 'checkboxes' => array('header' => array('value' => '<input type="checkbox" onclick="invertAll(this, this.form, \'ban_items\');" class="input_check" />', 'style' => 'width: 5%;'), 'data' => array('sprintf' => array('format' => '<input type="checkbox" name="ban_items[]" value="%1$d" class="input_check" />', 'params' => array('id' => false))))), 'form' => array('href' => $scripturl . '?action=admin;area=ban;sa=edit;bg=' . $ban_group_id), 'additional_rows' => array(array('position' => 'below_table_data', 'class' => 'submitbutton', 'value' => '
								<input type="submit" name="remove_selection" value="' . $txt['ban_remove_selected_triggers'] . '" class="right_submit" />
								<a class="linkbutton" href="' . $scripturl . '?action=admin;area=ban;sa=edittrigger;bg=' . $ban_group_id . '">' . $txt['ban_add_trigger'] . '</a>
								<input type="hidden" name="bg" value="' . $ban_group_id . '" />
								<input type="hidden" name="' . $context['session_var'] . '" value="' . $context['session_id'] . '" />
								<input type="hidden" name="' . $context['admin-bet_token_var'] . '" value="' . $context['admin-bet_token'] . '" />')));
                createList($listOptions);
            } else {
                $context['ban'] = array('id' => 0, 'name' => '', 'expiration' => array('status' => 'never', 'days' => 0), 'reason' => '', 'notes' => '', 'ban_days' => 0, 'cannot' => array('access' => true, 'post' => false, 'register' => false, 'login' => false), 'is_new' => true);
                $context['ban_suggestions'] = array('main_ip' => '', 'hostname' => '', 'email' => '', 'member' => array('id' => 0));
                // Overwrite some of the default form values if a user ID was given.
                if (!empty($_REQUEST['u'])) {
                    $context['ban_suggestions'] = array_merge($context['ban_suggestions'], getMemberData((int) $_REQUEST['u']));
                    if (!empty($context['ban_suggestions']['member']['id'])) {
                        $context['ban_suggestions']['href'] = $scripturl . '?action=profile;u=' . $context['ban_suggestions']['member']['id'];
                        $context['ban_suggestions']['member']['link'] = '<a href="' . $context['ban_suggestions']['href'] . '">' . $context['ban_suggestions']['member']['name'] . '</a>';
                        // Default the ban name to the name of the banned member.
                        $context['ban']['name'] = $context['ban_suggestions']['member']['name'];
                        // @todo: there should be a better solution...
                        // used to lock the "Ban on Username" input when banning from profile
                        $context['ban']['from_user'] = true;
                        // Would be nice if we could also ban the hostname.
                        if ((preg_match('/^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/', $context['ban_suggestions']['main_ip']) == 1 || isValidIPv6($context['ban_suggestions']['main_ip'])) && empty($modSettings['disableHostnameLookup'])) {
                            $context['ban_suggestions']['hostname'] = host_from_ip($context['ban_suggestions']['main_ip']);
                        }
                        $context['ban_suggestions']['other_ips'] = banLoadAdditionalIPs($context['ban_suggestions']['member']['id']);
                    }
                } else {
                    $context['use_autosuggest'] = true;
                    loadJavascriptFile('suggest.js');
                }
            }
        }
        // Set the right template
        $context['sub_template'] = 'ban_edit';
        // A couple of text strings we *may* need
        addJavascriptVar(array('txt_ban_name_empty' => $txt['ban_name_empty'], 'txt_ban_restriction_empty' => $txt['ban_restriction_empty']), true);
        // And a bit of javascript to enable/disable some fields
        addInlineJavascript('addLoadEvent(fUpdateStatus);', true);
    }
Example #6
0
/**
 * Convert a single IP to a ranged IP.
 *
 * - internal function used to convert a user-readable format to a format suitable for the database.
 *
 * @param string $fullip
 * @return array|string 'unknown' if the ip in the input was '255.255.255.255'
 */
function ip2range($fullip)
{
    // If its IPv6, validate it first.
    if (isValidIPv6($fullip) !== false) {
        $ip_parts = explode(':', expandIPv6($fullip, false));
        $ip_array = array();
        if (count($ip_parts) != 8) {
            return array();
        }
        for ($i = 0; $i < 8; $i++) {
            if ($ip_parts[$i] == '*') {
                $ip_array[$i] = array('low' => '0', 'high' => hexdec('ffff'));
            } elseif (preg_match('/^([0-9A-Fa-f]{1,4})\\-([0-9A-Fa-f]{1,4})$/', $ip_parts[$i], $range) == 1) {
                $ip_array[$i] = array('low' => hexdec($range[1]), 'high' => hexdec($range[2]));
            } elseif (is_numeric(hexdec($ip_parts[$i]))) {
                $ip_array[$i] = array('low' => hexdec($ip_parts[$i]), 'high' => hexdec($ip_parts[$i]));
            }
        }
        return $ip_array;
    }
    // Pretend that 'unknown' is 255.255.255.255. (since that can't be an IP anyway.)
    if ($fullip == 'unknown') {
        $fullip = '255.255.255.255';
    }
    $ip_parts = explode('.', $fullip);
    $ip_array = array();
    if (count($ip_parts) != 4) {
        return array();
    }
    for ($i = 0; $i < 4; $i++) {
        if ($ip_parts[$i] == '*') {
            $ip_array[$i] = array('low' => '0', 'high' => '255');
        } elseif (preg_match('/^(\\d{1,3})\\-(\\d{1,3})$/', $ip_parts[$i], $range) == 1) {
            $ip_array[$i] = array('low' => $range[1], 'high' => $range[2]);
        } elseif (is_numeric($ip_parts[$i])) {
            $ip_array[$i] = array('low' => $ip_parts[$i], 'high' => $ip_parts[$i]);
        }
    }
    // Makes it simpiler to work with.
    $ip_array[4] = array('low' => 0, 'high' => 0);
    $ip_array[5] = array('low' => 0, 'high' => 0);
    $ip_array[6] = array('low' => 0, 'high' => 0);
    $ip_array[7] = array('low' => 0, 'high' => 0);
    return $ip_array;
}