From 7d9d22914f8d6c79c8fd557c742e1866c22ba16e Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 25 Apr 2017 22:45:42 -0400 Subject: [PATCH 1/6] Refactor "URL" to "domain" for blocklist - Add slash trimming to domain - Reformatted mod/friendica --- mod/admin.php | 26 ++++--- mod/friendica.php | 117 +++++++++++++++-------------- view/templates/admin_blocklist.tpl | 4 +- 3 files changed, 76 insertions(+), 71 deletions(-) diff --git a/mod/admin.php b/mod/admin.php index a783ded0a..50e1ff18e 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -272,13 +272,14 @@ function admin_content(App $a) { */ function admin_page_blocklist(App $a) { $blocklist = Config::get('system', 'blocklist'); + var_dump($blocklist); $blocklistform = array(); if (is_array($blocklist)) { foreach($blocklist as $id => $b) { $blocklistform[] = array( - 'url' => array("url[$id]", t('Blocked URL'), $b['URL'], '', t('The blocked URL'), 'required', '', ''), - 'reason' => array("reason[$id]", t("Reason for the block"), $b['reason'], t('The reason why you blocked this URL.').'('.$b['URL'].')', 'required', '', ''), - 'delete' => array("delete[$id]", t("Delete UFL").' ('.$b['URL'].')', False , "Check to delete this entry from the blocklist") + 'domain' => array("domain[$id]", t('Blocked domain'), $b['domain'], '', t('The blocked domain'), 'required', '', ''), + 'reason' => array("reason[$id]", t("Reason for the block"), $b['reason'], t('The reason why you blocked this domain.').'('.$b['domain'].')', 'required', '', ''), + 'delete' => array("delete[$id]", t("Delete domain").' ('.$b['domain'].')', False , "Check to delete this entry from the blocklist") ); } } @@ -286,15 +287,15 @@ function admin_page_blocklist(App $a) { return replace_macros($t, array( '$title' => t('Administration'), '$page' => t('Server Blocklist'), - '$intro' => t('This page can be used to define a black list of servers from the federated network that are not allowed to interact with your node. For all entered URLs you should also give a reason, why you have blocked the remote server.'), + '$intro' => t('This page can be used to define a black list of servers from the federated network that are not allowed to interact with your node. For all entered domains you should also give a reason why you have blocked the remote server.'), '$public' => t('The list of blocked servers will be made publically available on the /friendica page so that your users and people investigating communication problems can find the reason easily.'), '$addtitle' => t('Add new entry to block list'), - '$newurl' => array('newentry_url', t('Server URL'), '', t('The URL of the new server to add to the block list. Do not include the protocol to the URL.'), 'required', '', ''), - '$newreason' => array('newentry_reason', t('Block reason'), '', t('The reason why you blocked this URL.'), 'required', '', ''), + '$newdomain' => array('newentry_domain', t('Server Domain'), '', t('The domain of the new server to add to the block list. Do not include the protocol.'), 'required', '', ''), + '$newreason' => array('newentry_reason', t('Block reason'), '', t('The reason why you blocked this domain.'), 'required', '', ''), '$submit' => t('Add Entry'), '$savechanges' => t('Save changes to the blocklist'), '$currenttitle' => t('Current Entries in the Blocklist'), - '$thurl' => t('Blocked URL'), + '$thurl' => t('Blocked domain'), '$threason' => t('Reason for the block'), '$delentry' => t('Delete entry from blocklist'), '$entries' => $blocklistform, @@ -320,7 +321,7 @@ function admin_page_blocklist_post(App $a) { // Add new item to blocklist $blocklist = get_config('system', 'blocklist'); $blocklist[] = array( - 'URL' => notags(trim($_POST['newentry_url'])), + 'domain' => notags(trim($_POST['newentry_domain'])), 'reason' => notags(trim($_POST['newentry_reason'])) ); Config::set('system', 'blocklist', $blocklist); @@ -328,12 +329,13 @@ function admin_page_blocklist_post(App $a) { } else { // Edit the entries from blocklist $blocklist = array(); - foreach ($_POST['url'] as $id => $URL) { - $URL = notags(trim($URL)); + foreach ($_POST['domain'] as $id => $domain) { + // Trimming whitespaces as well as any lingering slashes + $domain = notags(trim($domain, "\x00..\x1F/")); $reason = notags(trim($_POST['reason'][$id])); if (!x($_POST['delete'][$id])) { $blocklist[] = array( - 'URL' => $URL, + 'domain' => $domain, 'reason' => $reason ); } @@ -342,7 +344,7 @@ function admin_page_blocklist_post(App $a) { info(t('Site blocklist updated.').EOL); } goaway('admin/blocklist'); - + return; // NOTREACHED } diff --git a/mod/friendica.php b/mod/friendica.php index 230ffd8bc..7bfb7c869 100644 --- a/mod/friendica.php +++ b/mod/friendica.php @@ -3,55 +3,58 @@ use \Friendica\Core\Config; function friendica_init(App $a) { - if ($a->argv[1]=="json"){ + if ($a->argv[1] == "json"){ $register_policy = Array('REGISTER_CLOSED', 'REGISTER_APPROVE', 'REGISTER_OPEN'); $sql_extra = ''; - if(x($a->config,'admin_nickname')) { - $sql_extra = sprintf(" AND nickname = '%s' ",dbesc($a->config['admin_nickname'])); + if (x($a->config,'admin_nickname')) { + $sql_extra = sprintf(" AND `nickname` = '%s' ", dbesc($a->config['admin_nickname'])); } - if (isset($a->config['admin_email']) && $a->config['admin_email']!=''){ - $adminlist = explode(",", str_replace(" ", "", $a->config['admin_email'])); + if (isset($a->config['admin_email']) && $a->config['admin_email']!='') { + $adminlist = explode(",", str_replace(" ", "", $a->config['admin_email'])); - //$r = q("SELECT username, nickname FROM user WHERE email='%s' $sql_extra", dbesc($a->config['admin_email'])); - $r = q("SELECT username, nickname FROM user WHERE email='%s' $sql_extra", dbesc($adminlist[0])); + $r = q("SELECT `username`, `nickname` FROM `user` WHERE `email` = '%s' $sql_extra", dbesc($adminlist[0])); $admin = array( 'name' => $r[0]['username'], - 'profile'=> App::get_baseurl().'/profile/'.$r[0]['nickname'], + 'profile'=> App::get_baseurl() . '/profile/' . $r[0]['nickname'], ); } else { $admin = false; } $visible_plugins = array(); - if(is_array($a->plugins) && count($a->plugins)) { - $r = q("select * from addon where hidden = 0"); - if (dbm::is_result($r)) - foreach($r as $rr) + if (is_array($a->plugins) && count($a->plugins)) { + $r = q("SELECT * FROM `addon` WHERE `hidden` = 0"); + if (dbm::is_result($r)) { + foreach($r as $rr) { $visible_plugins[] = $rr['name']; + } + } } Config::load('feature_lock'); $locked_features = array(); - if(is_array($a->config['feature_lock']) && count($a->config['feature_lock'])) { - foreach($a->config['feature_lock'] as $k => $v) { - if($k === 'config_loaded') + if (is_array($a->config['feature_lock']) && count($a->config['feature_lock'])) { + foreach ($a->config['feature_lock'] as $k => $v) { + if ($k === 'config_loaded') { continue; + } + $locked_features[$k] = intval($v); } } $data = Array( - 'version' => FRIENDICA_VERSION, - 'url' => z_root(), - 'plugins' => $visible_plugins, + 'version' => FRIENDICA_VERSION, + 'url' => z_root(), + 'plugins' => $visible_plugins, 'locked_features' => $locked_features, 'register_policy' => $register_policy[$a->config['register_policy']], - 'admin' => $admin, - 'site_name' => $a->config['sitename'], - 'platform' => FRIENDICA_PLATFORM, - 'info' => ((x($a->config,'info')) ? $a->config['info'] : ''), - 'no_scrape_url' => App::get_baseurl().'/noscrape' + 'admin' => $admin, + 'site_name' => $a->config['sitename'], + 'platform' => FRIENDICA_PLATFORM, + 'info' => ((x($a->config,'info')) ? $a->config['info'] : ''), + 'no_scrape_url' => App::get_baseurl().'/noscrape' ); echo json_encode($data); @@ -59,63 +62,63 @@ function friendica_init(App $a) { } } - - function friendica_content(App $a) { + $o = '

Friendica

' . PHP_EOL; + $o .= '

'; + $o .= t('This is Friendica, version') . ' ' . FRIENDICA_VERSION . ' '; + $o .= t('running at web location') . ' ' . z_root(); + $o .= '

' . PHP_EOL; - $o = ''; - $o .= '

Friendica

'; + $o .= '

'; + $o .= t('Please visit Friendica.com to learn more about the Friendica project.') . PHP_EOL; + $o .= '

' . PHP_EOL; - - $o .= '

'; - - $o .= t('This is Friendica, version') . ' ' . FRIENDICA_VERSION . ' '; - $o .= t('running at web location') . ' ' . z_root() . '

'; - - $o .= t('Please visit Friendica.com to learn more about the Friendica project.') . '

'; - - $o .= t('Bug reports and issues: please visit') . ' ' . ''.t('the bugtracker at github').'

'; - $o .= t('Suggestions, praise, donations, etc. - please email "Info" at Friendica - dot com') . '

'; - - $o .= '

'; + $o .= '

'; + $o .= t('Bug reports and issues: please visit') . ' ' . ''.t('the bugtracker at github').''; + $o .= '

' . PHP_EOL; + $o .= '

'; + $o .= t('Suggestions, praise, donations, etc. - please email "Info" at Friendica - dot com'); + $o .= '

' . PHP_EOL; $visible_plugins = array(); - if(is_array($a->plugins) && count($a->plugins)) { - $r = q("select * from addon where hidden = 0"); - if (dbm::is_result($r)) - foreach($r as $rr) + if (is_array($a->plugins) && count($a->plugins)) { + $r = q("SELECT * FROM `addon` WHERE `hidden` = 0"); + if (dbm::is_result($r)) { + foreach($r as $rr) { $visible_plugins[] = $rr['name']; + } + } } - - if(count($visible_plugins)) { - $o .= '

' . t('Installed plugins/addons/apps:') . '

'; + if (count($visible_plugins)) { + $o .= '

' . t('Installed plugins/addons/apps:') . '

' . PHP_EOL; $sorted = $visible_plugins; $s = ''; sort($sorted); - foreach($sorted as $p) { - if(strlen($p)) { - if(strlen($s)) $s .= ', '; + foreach ($sorted as $p) { + if (strlen($p)) { + if (strlen($s)) { + $s .= ', '; + } $s .= $p; } } - $o .= '
' . $s . '
'; + $o .= '
' . $s . '
' . PHP_EOL; + } else { + $o .= '

' . t('No installed plugins/addons/apps') . '

' . PHP_EOL; } - else - $o .= '

' . t('No installed plugins/addons/apps') . '

'; $blocklist = Config::get('system', 'blocklist'); if (count($blocklist)) { - $o .= '

'. t('On this server the following remote servers are blocked.') .'

'; - $o .= ''; + $o .= '

' . t('On this server the following remote servers are blocked.') . '

' . PHP_EOL; + $o .= '
'. t('Blocked URL') .''. t('Reason for the block') .'
' . PHP_EOL; foreach ($blocklist as $b) { - $o .= ''; + $o .= '' . PHP_EOL; } - $o .= '
' . t('Blocked domain') . '' . t('Reason for the block') . '
'. $b['URL'] .''. $b['reason'] .'
' . $b['domain'] .'' . $b['reason'] . '
'; + $o .= '' . PHP_EOL; } call_hooks('about_hook', $o); return $o; - } diff --git a/view/templates/admin_blocklist.tpl b/view/templates/admin_blocklist.tpl index e888dcd93..1484c987e 100644 --- a/view/templates/admin_blocklist.tpl +++ b/view/templates/admin_blocklist.tpl @@ -11,7 +11,7 @@

{{$addtitle}}

- {{include file="field_input.tpl" field=$newurl}} + {{include file="field_input.tpl" field=$newdomain}} {{include file="field_input.tpl" field=$newreason}}
@@ -22,7 +22,7 @@
{{foreach $entries as $e}} - {{include file="field_input.tpl" field=$e.url}} + {{include file="field_input.tpl" field=$e.domain}} {{include file="field_input.tpl" field=$e.reason}} {{include file="field_checkbox.tpl" field=$e.delete}} {{/foreach}} From c7e1a8e871363d4e60178e30e819c3169ed406c5 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 25 Apr 2017 22:45:56 -0400 Subject: [PATCH 2/6] Added check_domain_blocklist --- include/follow.php | 5 +++++ include/network.php | 49 ++++++++++++++++++++++++++++++++++++-------- mod/dfrn_request.php | 5 +++++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/include/follow.php b/include/follow.php index 304519107..11138afac 100644 --- a/include/follow.php +++ b/include/follow.php @@ -82,6 +82,11 @@ function new_contact($uid,$url,$interactive = false) { return $result; } + if (! check_domain_blocklist($url)) { + $result['message'] = t('Blocked domain'); + return $result; + } + if (! $url) { $result['message'] = t('Connect URL missing.'); return $result; diff --git a/include/network.php b/include/network.php index f9d35c52c..a362f0307 100644 --- a/include/network.php +++ b/include/network.php @@ -454,13 +454,14 @@ function allowed_url($url) { $h = @parse_url($url); - if(! $h) { + if (! $h) { return false; } - $str_allowed = get_config('system','allowed_sites'); - if(! $str_allowed) + $str_allowed = get_config('system', 'allowed_sites'); + if (! $str_allowed) { return true; + } $found = false; @@ -468,16 +469,17 @@ function allowed_url($url) { // always allow our own site - if($host == strtolower($_SERVER['SERVER_NAME'])) + if ($host == strtolower($_SERVER['SERVER_NAME'])) { return true; + } $fnmatch = function_exists('fnmatch'); - $allowed = explode(',',$str_allowed); + $allowed = explode(',', $str_allowed); - if(count($allowed)) { - foreach($allowed as $a) { + if (count($allowed)) { + foreach ($allowed as $a) { $pat = strtolower(trim($a)); - if(($fnmatch && fnmatch($pat,$host)) || ($pat == $host)) { + if (($fnmatch && fnmatch($pat, $host)) || ($pat == $host)) { $found = true; break; } @@ -486,6 +488,37 @@ function allowed_url($url) { return $found; } +/** + * Checks if the provided url domain isn't on the domain blacklist. + * Return true if the check passed (not on the blacklist), false if not + * or malformed URL + * + * @param string $url The url to check the domain from + * @return boolean + */ +function check_domain_blocklist($url) { + $h = @parse_url($url); + + if (! $h) { + return false; + } + + $domain_blocklist = get_config('system', 'blocklist', array()); + if (! $domain_blocklist) { + return true; + } + + $host = strtolower($h['host']); + + foreach ($domain_blocklist as $domain_block) { + if (strtolower($domain_block['domain']) == $host) { + return false; + } + } + + return true; +} + /** * @brief Check if email address is allowed to register here. * diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php index f3875ed8e..68f9448a5 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -514,6 +514,11 @@ function dfrn_request_post(App $a) { return; // NOTREACHED } + if (! check_domain_blocklist($url)) { + notice( t('Blocked domain') . EOL); + goaway(App::get_baseurl() . '/' . $a->cmd); + return; // NOTREACHED + } require_once('include/Scrape.php'); From 8d469c155a3c0e8c9665546ab5bc5ab091a970b5 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Tue, 25 Apr 2017 22:59:11 -0400 Subject: [PATCH 3/6] Add check_blocklist to z_fetch_url and post_url - Standard compliance --- include/network.php | 211 +++++++++++++++++++++++++------------------- 1 file changed, 122 insertions(+), 89 deletions(-) diff --git a/include/network.php b/include/network.php index a362f0307..27292499b 100644 --- a/include/network.php +++ b/include/network.php @@ -62,23 +62,27 @@ function fetch_url($url,$binary = false, &$redirects = 0, $timeout = 0, $accept_ * string 'header' => HTTP headers * string 'body' => fetched content */ -function z_fetch_url($url,$binary = false, &$redirects = 0, $opts=array()) { - - $ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => ""); - +function z_fetch_url($url, $binary = false, &$redirects = 0, $opts = array()) { + $ret = array('return_code' => 0, 'success' => false, 'header' => '', 'body' => ''); $stamp1 = microtime(true); $a = get_app(); + if (! check_domain_blocklist($url)) { + logger('z_fetch_url: domain of ' . $url . ' is blocked', LOGGER_DATA); + return $ret; + } + $ch = @curl_init($url); - if(($redirects > 8) || (! $ch)) { + + if (($redirects > 8) || (!$ch)) { return $ret; } @curl_setopt($ch, CURLOPT_HEADER, true); - if(x($opts,"cookiejar")) { + if (x($opts, "cookiejar")) { curl_setopt($ch, CURLOPT_COOKIEJAR, $opts["cookiejar"]); curl_setopt($ch, CURLOPT_COOKIEFILE, $opts["cookiejar"]); } @@ -87,52 +91,61 @@ function z_fetch_url($url,$binary = false, &$redirects = 0, $opts=array()) { // @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // @curl_setopt($ch, CURLOPT_MAXREDIRS, 5); - if (x($opts,'accept_content')){ - curl_setopt($ch,CURLOPT_HTTPHEADER, array ( - "Accept: " . $opts['accept_content'] + if (x($opts, 'accept_content')) { + curl_setopt($ch, CURLOPT_HTTPHEADER, array( + 'Accept: ' . $opts['accept_content'] )); } - @curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); + @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); @curl_setopt($ch, CURLOPT_USERAGENT, $a->get_useragent()); $range = intval(Config::get('system', 'curl_range_bytes', 0)); + if ($range > 0) { - @curl_setopt($ch, CURLOPT_RANGE, '0-'.$range); + @curl_setopt($ch, CURLOPT_RANGE, '0-' . $range); } - if(x($opts,'headers')){ + if (x($opts, 'headers')) { @curl_setopt($ch, CURLOPT_HTTPHEADER, $opts['headers']); } - if(x($opts,'nobody')){ + + if (x($opts, 'nobody')) { @curl_setopt($ch, CURLOPT_NOBODY, $opts['nobody']); } - if(x($opts,'timeout')){ + + if (x($opts, 'timeout')) { @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']); } else { - $curl_time = intval(get_config('system','curl_timeout')); + $curl_time = intval(get_config('system', 'curl_timeout')); @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); } // by default we will allow self-signed certs // but you can override this - $check_cert = get_config('system','verifyssl'); + $check_cert = get_config('system', 'verifyssl'); @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); + if ($check_cert) { @curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); } - $prx = get_config('system','proxy'); - if(strlen($prx)) { + $proxy = get_config('system', 'proxy'); + + if (strlen($proxy)) { @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); - @curl_setopt($ch, CURLOPT_PROXY, $prx); - $prxusr = @get_config('system','proxyuser'); - if(strlen($prxusr)) - @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr); + @curl_setopt($ch, CURLOPT_PROXY, $proxy); + $proxyuser = @get_config('system', 'proxyuser'); + + if (strlen($proxyuser)) { + @curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyuser); + } + } + + if ($binary) { + @curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1); } - if($binary) - @curl_setopt($ch, CURLOPT_BINARYTRANSFER,1); $a->set_curl_code(0); @@ -140,8 +153,9 @@ function z_fetch_url($url,$binary = false, &$redirects = 0, $opts=array()) { // if it throws any errors. $s = @curl_exec($ch); + if (curl_errno($ch) !== CURLE_OK) { - logger('fetch_url error fetching '.$url.': '.curl_error($ch), LOGGER_NORMAL); + logger('fetch_url error fetching ' . $url . ': ' . curl_error($ch), LOGGER_NORMAL); } $ret['errno'] = curl_errno($ch); @@ -150,136 +164,151 @@ function z_fetch_url($url,$binary = false, &$redirects = 0, $opts=array()) { $curl_info = @curl_getinfo($ch); $http_code = $curl_info['http_code']; - logger('fetch_url '.$url.': '.$http_code." ".$s, LOGGER_DATA); + logger('fetch_url ' . $url . ': ' . $http_code . " " . $s, LOGGER_DATA); $header = ''; // Pull out multiple headers, e.g. proxy and continuation headers // allow for HTTP/2.x without fixing code - while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) { - $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4); + while (preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/', $base)) { + $chunk = substr($base, 0, strpos($base, "\r\n\r\n") + 4); $header .= $chunk; - $base = substr($base,strlen($chunk)); + $base = substr($base, strlen($chunk)); } $a->set_curl_code($http_code); $a->set_curl_content_type($curl_info['content_type']); $a->set_curl_headers($header); - if($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307) { - $new_location_info = @parse_url($curl_info["redirect_url"]); - $old_location_info = @parse_url($curl_info["url"]); + if ($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307) { + $new_location_info = @parse_url($curl_info['redirect_url']); + $old_location_info = @parse_url($curl_info['url']); - $newurl = $curl_info["redirect_url"]; + $newurl = $curl_info['redirect_url']; - if (($new_location_info["path"] == "") AND ($new_location_info["host"] != "")) - $newurl = $new_location_info["scheme"]."://".$new_location_info["host"].$old_location_info["path"]; + if (($new_location_info['path'] == '') AND ( $new_location_info['host'] != '')) { + $newurl = $new_location_info['scheme'] . '://' . $new_location_info['host'] . $old_location_info['path']; + } $matches = array(); + if (preg_match('/(Location:|URI:)(.*?)\n/i', $header, $matches)) { $newurl = trim(array_pop($matches)); } - if(strpos($newurl,'/') === 0) - $newurl = $old_location_info["scheme"]."://".$old_location_info["host"].$newurl; + + if (strpos($newurl, '/') === 0) { + $newurl = $old_location_info['scheme'] . '://' . $old_location_info['host'] . $newurl; + } + if (filter_var($newurl, FILTER_VALIDATE_URL)) { $redirects++; @curl_close($ch); - return z_fetch_url($newurl,$binary, $redirects, $opts); + return z_fetch_url($newurl, $binary, $redirects, $opts); } } - $a->set_curl_code($http_code); $a->set_curl_content_type($curl_info['content_type']); - $body = substr($s,strlen($header)); - - + $body = substr($s, strlen($header)); $rc = intval($http_code); $ret['return_code'] = $rc; $ret['success'] = (($rc >= 200 && $rc <= 299) ? true : false); $ret['redirect_url'] = $url; - if(! $ret['success']) { + + if (!$ret['success']) { $ret['error'] = curl_error($ch); $ret['debug'] = $curl_info; logger('z_fetch_url: error: ' . $url . ': ' . $ret['error'], LOGGER_DEBUG); - logger('z_fetch_url: debug: ' . print_r($curl_info,true), LOGGER_DATA); + logger('z_fetch_url: debug: ' . print_r($curl_info, true), LOGGER_DATA); } - $ret['body'] = substr($s,strlen($header)); + + $ret['body'] = substr($s, strlen($header)); $ret['header'] = $header; - if(x($opts,'debug')) { + + if (x($opts, 'debug')) { $ret['debug'] = $curl_info; } + @curl_close($ch); - $a->save_timestamp($stamp1, "network"); + $a->save_timestamp($stamp1, 'network'); return($ret); - } -// post request to $url. $params is an array of post variables. - /** - * @brief Post request to $url + * @brief Send POST request to $url * * @param string $url URL to post - * @param mixed $params + * @param mixed $params array of POST variables * @param string $headers HTTP headers * @param integer $redirects Recursion counter for internal use - default = 0 * @param integer $timeout The timeout in seconds, default system config value or 60 seconds * * @return string The content */ -function post_url($url,$params, $headers = null, &$redirects = 0, $timeout = 0) { +function post_url($url, $params, $headers = null, &$redirects = 0, $timeout = 0) { $stamp1 = microtime(true); + if (!check_domain_blocklist($url)) { + logger('post_url: domain of ' . $url . ' is blocked', LOGGER_DATA); + return false; + } + $a = get_app(); $ch = curl_init($url); - if(($redirects > 8) || (! $ch)) - return false; - logger("post_url: start ".$url, LOGGER_DATA); + if (($redirects > 8) || (!$ch)) { + return false; + } + + logger('post_url: start ' . $url, LOGGER_DATA); curl_setopt($ch, CURLOPT_HEADER, true); - curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); - curl_setopt($ch, CURLOPT_POST,1); - curl_setopt($ch, CURLOPT_POSTFIELDS,$params); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $params); curl_setopt($ch, CURLOPT_USERAGENT, $a->get_useragent()); - if(intval($timeout)) { + if (intval($timeout)) { curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); - } - else { - $curl_time = intval(get_config('system','curl_timeout')); + } else { + $curl_time = intval(get_config('system', 'curl_timeout')); curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); } - if(defined('LIGHTTPD')) { - if(!is_array($headers)) { + if (defined('LIGHTTPD')) { + if (!is_array($headers)) { $headers = array('Expect:'); } else { - if(!in_array('Expect:', $headers)) { + if (!in_array('Expect:', $headers)) { array_push($headers, 'Expect:'); } } } - if($headers) - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - $check_cert = get_config('system','verifyssl'); + if ($headers) { + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + + $check_cert = get_config('system', 'verifyssl'); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false)); + if ($check_cert) { @curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); } - $prx = get_config('system','proxy'); - if(strlen($prx)) { + + $proxy = get_config('system', 'proxy'); + + if (strlen($proxy)) { curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); - curl_setopt($ch, CURLOPT_PROXY, $prx); - $prxusr = get_config('system','proxyuser'); - if(strlen($prxusr)) - curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr); + curl_setopt($ch, CURLOPT_PROXY, $proxy); + $proxyuser = get_config('system', 'proxyuser'); + if (strlen($proxyuser)) { + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyuser); + } } $a->set_curl_code(0); @@ -293,44 +322,48 @@ function post_url($url,$params, $headers = null, &$redirects = 0, $timeout = 0) $curl_info = curl_getinfo($ch); $http_code = $curl_info['http_code']; - logger("post_url: result ".$http_code." - ".$url, LOGGER_DATA); + logger('post_url: result ' . $http_code . ' - ' . $url, LOGGER_DATA); $header = ''; // Pull out multiple headers, e.g. proxy and continuation headers // allow for HTTP/2.x without fixing code - while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) { - $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4); + while (preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/', $base)) { + $chunk = substr($base, 0, strpos($base, "\r\n\r\n") + 4); $header .= $chunk; - $base = substr($base,strlen($chunk)); + $base = substr($base, strlen($chunk)); } - if($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307) { + if ($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307) { $matches = array(); preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches); $newurl = trim(array_pop($matches)); - if(strpos($newurl,'/') === 0) - $newurl = $old_location_info["scheme"] . "://" . $old_location_info["host"] . $newurl; + + if (strpos($newurl, '/') === 0) { + $newurl = $old_location_info['scheme'] . '://' . $old_location_info['host'] . $newurl; + } + if (filter_var($newurl, FILTER_VALIDATE_URL)) { $redirects++; - logger("post_url: redirect ".$url." to ".$newurl); - return post_url($newurl,$params, $headers, $redirects, $timeout); - //return fetch_url($newurl,false,$redirects,$timeout); + logger('post_url: redirect ' . $url . ' to ' . $newurl); + return post_url($newurl, $params, $headers, $redirects, $timeout); } } + $a->set_curl_code($http_code); - $body = substr($s,strlen($header)); + + $body = substr($s, strlen($header)); $a->set_curl_headers($header); curl_close($ch); - $a->save_timestamp($stamp1, "network"); + $a->save_timestamp($stamp1, 'network'); - logger("post_url: end ".$url, LOGGER_DATA); + logger('post_url: end ' . $url, LOGGER_DATA); - return($body); + return $body; } // Generic XML return From 188d3a6f5ed0329c0c0a783cf1037295c32ab762 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 26 Apr 2017 00:23:01 -0400 Subject: [PATCH 4/6] Renamed check_domain_blocklist to blocked_url - Remove debug --- include/follow.php | 2 +- include/network.php | 19 +++++++++---------- mod/admin.php | 1 - mod/dfrn_request.php | 2 +- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/follow.php b/include/follow.php index 11138afac..57520abe6 100644 --- a/include/follow.php +++ b/include/follow.php @@ -82,7 +82,7 @@ function new_contact($uid,$url,$interactive = false) { return $result; } - if (! check_domain_blocklist($url)) { + if (blocked_url($url)) { $result['message'] = t('Blocked domain'); return $result; } diff --git a/include/network.php b/include/network.php index 27292499b..6ead21acf 100644 --- a/include/network.php +++ b/include/network.php @@ -69,7 +69,7 @@ function z_fetch_url($url, $binary = false, &$redirects = 0, $opts = array()) { $a = get_app(); - if (! check_domain_blocklist($url)) { + if (blocked_url($url)) { logger('z_fetch_url: domain of ' . $url . ' is blocked', LOGGER_DATA); return $ret; } @@ -252,7 +252,7 @@ function z_fetch_url($url, $binary = false, &$redirects = 0, $opts = array()) { function post_url($url, $params, $headers = null, &$redirects = 0, $timeout = 0) { $stamp1 = microtime(true); - if (!check_domain_blocklist($url)) { + if (blocked_url($url)) { logger('post_url: domain of ' . $url . ' is blocked', LOGGER_DATA); return false; } @@ -522,34 +522,33 @@ function allowed_url($url) { } /** - * Checks if the provided url domain isn't on the domain blacklist. - * Return true if the check passed (not on the blacklist), false if not - * or malformed URL + * Checks if the provided url domain is on the domain blocklist. + * Returns true if it is or malformed URL, false if not. * * @param string $url The url to check the domain from * @return boolean */ -function check_domain_blocklist($url) { +function blocked_url($url) { $h = @parse_url($url); if (! $h) { - return false; + return true; } $domain_blocklist = get_config('system', 'blocklist', array()); if (! $domain_blocklist) { - return true; + return false; } $host = strtolower($h['host']); foreach ($domain_blocklist as $domain_block) { if (strtolower($domain_block['domain']) == $host) { - return false; + return true; } } - return true; + return false; } /** diff --git a/mod/admin.php b/mod/admin.php index 50e1ff18e..97d52f14f 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -272,7 +272,6 @@ function admin_content(App $a) { */ function admin_page_blocklist(App $a) { $blocklist = Config::get('system', 'blocklist'); - var_dump($blocklist); $blocklistform = array(); if (is_array($blocklist)) { foreach($blocklist as $id => $b) { diff --git a/mod/dfrn_request.php b/mod/dfrn_request.php index 68f9448a5..5a25c27f6 100644 --- a/mod/dfrn_request.php +++ b/mod/dfrn_request.php @@ -514,7 +514,7 @@ function dfrn_request_post(App $a) { return; // NOTREACHED } - if (! check_domain_blocklist($url)) { + if (blocked_url($url)) { notice( t('Blocked domain') . EOL); goaway(App::get_baseurl() . '/' . $a->cmd); return; // NOTREACHED From 9c53108a3e7b73347a40d725e6b2194b0e29282a Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 26 Apr 2017 22:08:47 -0400 Subject: [PATCH 5/6] Use Config::get instead of get_config --- include/network.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/network.php b/include/network.php index 6ead21acf..752920e42 100644 --- a/include/network.php +++ b/include/network.php @@ -491,7 +491,7 @@ function allowed_url($url) { return false; } - $str_allowed = get_config('system', 'allowed_sites'); + $str_allowed = Config::get('system', 'allowed_sites'); if (! $str_allowed) { return true; } @@ -535,7 +535,7 @@ function blocked_url($url) { return true; } - $domain_blocklist = get_config('system', 'blocklist', array()); + $domain_blocklist = Config::get('system', 'blocklist', array()); if (! $domain_blocklist) { return false; } From 5d3021fd5a8aca72be9e3370529d5e7b035fa0aa Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 26 Apr 2017 22:22:01 -0400 Subject: [PATCH 6/6] Add documentation about the new admin page --- doc/Settings.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/Settings.md b/doc/Settings.md index 13424a5a4..e84418a45 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -242,6 +242,12 @@ The receiving end might be off-line, there might be a high system load and so on Don't panic! Friendica will not queue messages for all time but will sort out *dead* nodes automatically after a while and remove messages from the queue then. +## Server Blocklist + +This page allows to block all communications (inbound and outbound) with a specific domain name. +Each blocked domain entry requires a reason that will be displayed on the [friendica](/friendica) page. +Matching is exact, blocking a domain doesn't block subdomains. + ## Federation Statistics The federation statistics page gives you a short summery of the nodes/servers/pods of the decentralized social network federation your node knows.