Resolve host of `http::uri::Uri` to IP addresses - rust

I have an http::uri::Uri value and I want to get the IpAddrs of the host. In case the host is an IP address (e.g. http://127.0.0.1:8080), it just needs to be parsed. But if it's a hostname, it needs to be DNS resolved. How do I best do that?
It seems like std only has one public way to resolve hosts: ToSocketAddrs. Unfortunately, it includes the port in the API which is irrelevant for DNS resolving. But oh well. But with this, my first attempt was thus:
use std::net::ToSocketAddr;
let host = uri.authority().unwrap().host();
let addresses = (host, 0).to_socket_addrs()?
.map(|socket_addr| socket_addr.ip());
(I am using a dummy port here since it's irrelevant for the DNS resolution.)
This attempt works for many cases, but not for the square-bracket IPv6 form: http://[::1]:8080. Here, host() is [::1] and is not parsable as IpAddr, thus to_socket_addrs() tries to resolve [::1] as host, which fails. So I would need to check manually whether the host is an IPv6 within square-brackets. Not nice, especially since I don't know what grammars are allowed here exactly.
I had several other ideas, like changing the Authority to always have a dummy port, because then I could call authority.as_str().to_socket_addrs(). The <&str as ToSocketAddr> does support the [::1]:8080 syntax! But that impl NEEDS a port and fails if there is none.
So yes, I have not found an easy way to do that. But it seems this should not be that hard! Any ideas?

Try parsing it as std::net::IpAddr first, then look up the hostname if that fails. You have to handle the square bracket notation yourself, but this isn't too bad:
use std::net::IpAddr;
fn parse_ip_from_uri_host(host: &str) -> Option<IpAddr> {
host.parse::<IpAddr>().ok().or_else(||
// Parsing failed, try as bracketed IPv6
host.strip_prefix("[")?
.strip_suffix("]")?
.parse::<IpAddr>().ok()
)
}
(Playground)
If this returns None then parsing failed, and the next step is to attempt to resolve the string as a hostname through DNS.

Related

Privnote sockets server did not understand request

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("privnote.com", 80))
#s = ssl.wrap_socket(s, keyfile=None, certfile=None, server_side=False, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_SSLv23)
def claim_note(note_url):
s.sendall(b'DELETE /'+note_url.encode()+b'HTTP/1.1\r\nX-Requested-With: XMLHttpRequest\r\nHost: privnote.com\r\n')
print(s.recv(4096))
This is my code, now let me first start by saying that I have tried so many different things apart from this. I’ve tried https port and http port, 443, 80. I’ve commented and uncommented the statement that wraps the socket with ssl. All with the same outcome. Either the api returning absolutely nothing or the api telling me the request couldn’t be understood by the server. I was looking at a GitHub repo and only one header was used and it was because it was for an Ajax call which was x-requested-with. I tried adding user agent content type and now I’m just using host and x requested with. It’s a DELETE request and the url is the first 8 chars after the link. I’ve also tried adding \r\n\r\n at the end and even tried content-length. I don’t know what else to do. I want to know why the server is saying that.
There are multiple problems with your code. If you actually print out the request you are trying to sent it will look like this:
b'DELETE /node_urlHTTP/1.1\r\nX-Requested-With: XMLHttpRequest\r\nHost: privnote.com\r\n'
There are two problems with this line: a missing space between /node_url and HTTP/1.1 and a missing final \r\n als end-of-header marker at the end. Once these are fixed you get a successful response - a 302 redirect to the HTTPS version:
b'HTTP/1.1 302 Found\r\nDate:...\r\nLocation: https://privnote.com/node_url ...
When repeating the request with HTTPS and a valid node_url (with an invalid node_url you get an error that DELETE is not an allowed method):
s.connect(("privnote.com", 443))
s = ssl.wrap_socket(s)
...
b'HTTP/1.1 200 OK\r\n ...

Is it possible to match ccTTL domains to webRequest listener?

I am trying to make firefox addon redirects connection to another link, translating ccTLD domain name to gTLD one.
(http://foo.bar.co.baz/GETdata)->(http://foo.qux.com/GETdata)
I tried match pattern and other options, without any luck.
Match pattern (https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns)
: It seems like parser does not support ccTLD(ex: foo.co.au), but only gTLD (ex: foo.com).
events.UrlFilter (https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/events/UrlFilter)
: It seems it's not for webRequest.
<all_urls>
: I also tried to call listener for all domains, then filter it inside the listener by if(details.url.contains("domain-name")) statement, which failed. It did managed to filter other gTLD domains, but not ccTLD ones.
var pattern = "https://mdn.mozillademos.org/*";
function redirect(requestDetails) {
console.log("Redirecting: " + requestDetails.url);
return {
redirectUrl: "https://38.media.tumblr.com/tumblr_ldbj01lZiP1qe0eclo1_500.gif"
};
}
browser.webRequest.onBeforeRequest.addListener(
redirect,
{urls:[pattern], types:["image"]},
["blocking"]
);
//used MDN example code. permission is given to entire hosts in manifest.json
I can't get ccTLD filtered with any options.
It seems internal parser simply can't handle it since 3rd option also failed.
Am I missing something?

How to get client host name in bottle python?

from bottle import Bottle, request
import socket
app = Bottle()
my_ip = socket.gethostbyname(socket.gethostname())
#app.route('/hello')
def hello():
client_ip = request.environ.get('REMOTE_ADDR')
print("Client IP is ", client_ip)
#Code to read client hostname or machine name
return ['Your IP is: {}\n'.format(client_ip)]
app.run(host=my_ip, port=8080)
I am using the bottle(WSGI micro web-framework for Python). I know how to get IP of the client who requested the service. But I also want to know the hostname(machine name) of the client as well.
I tried reading hostname with windows commands like nbtstat & ping but they are not 100% reliable. Are there any other options available?
A couple of things:
You're probably better off not doing this at all. Instead, consider logging all the IP addresses to a file (better yet, do nothing and simply use an existing access_log) and then resolving them, offline, in batch.
If you do insist on resolving the IP addresses inline, you don't need to call out to Windows commands in order to do it. It'll be a lot faster, simper, and more robust to resolve the addresses in-process. I've included some sample code for you below.
Finally, I'd like to address your comment:
I tried... but they are not 100% reliable
That is a problem with your expectation, not with your DNS resolver. Reverse DNS lookups will inherently yield far less than 100% matches.
Here's the sample code to do the reverse lookup in Python. Good luck!
from socket import gethostbyaddr, herror
def resolve_address(addr):
'''
Resolve the ip address string ``addr`` and return its DNS name. If no name
is found, return None.
Raises any exceptions thrown by gethostbyaddr, except for NOTFOUND (which
is handled by returning None).
NOTE: Illustrative purposes only, not for production use.
'''
try:
record = gethostbyaddr(addr)
except herror as exc:
if exc.errno == 1:
print(f'no name found for address "{addr}"')
return None
else:
print(f'an error occurred while resolving {addr}: {exc}')
raise
print(f'the name of "{addr}" is: {record[0]}')
return record[0]
assert resolve_address('151.236.216.85') == 'li560-85.members.linode.com'
assert resolve_address('10.1.1.1') is None

Requests: Check that the proxy has been used to make the HTTP Request

I've been scratching my head the whole day yesterday about this and to my surprise, can't seem to find an easy way to check this.
I am using Python's Requests library to pass my proxy such as:
def make_request(url):
with requests.Session() as s:
s.mount("http://", HTTPAdapter(max_retries=3))
s.mount("https://", HTTPAdapter(max_retries=3))
page = None
d.rotate(-1) #d contains a dict of my proxies. this allows to rotate through the proxies everytime make_request is called.
s.proxies = d[0]
page = s.get(url, timeout=3)
print('proxy used: ' + str(d[0]))
return page.content
Problem is, I can't seem to make the request fail when the proxy is not expected to work. It seems there is always a fallback on my internet ip if the proxy is not working.
For example: I tried passing a random proxy ip like 101.101.101.101:8800 or removing the ip authentication that is needed on my proxies, the request is still passed, even though it should'nt.
I thought adding the timeout parameters when passing the request would do the trick, but obviously it didn't.
So
Why does this happen?
How can I check from which ip a request is being made?
From what I have seen so far, you should use the form
s.get(url, proxies = d)
This should use the proxies in the dict d to make a connection.
This form allowed me to check with working proxies and non-working proxies the status_code
print(s.status_code)
I will update once I find out whether it just circulates over the proxies in the dict to match a working one, or one is able to actually select which one to be used.
[UPDATE]
Tried to work around the dict in proxies, to use different proxy if I wanted to. However, proxies must be a dict to work. So I used a dict in the form of:
d = {"https" : 'https://' + str(proxy_ips[n].strip('\n'))}
This seems to work and allow me to use an ip I want to. Although it seems quite dull, I hope someone might come and help!
The proxies used can be seen through:
requests.utils.getproxies()
or
requests.utils.get_environ_proxies(url)
I hope that helps, obviously quite old question, but still!

Is it possible to block Tor users?

Would it be possible to block Tor users? (https://www.torproject.org/)
Due to the nature of the site I run I should do all I can to stop multiple accounts and block certain locations. Tor is worse than proxies - a total nightmare...
Tor is much easier to block than other open proxies since the list of exit IP addresses is known and published. Read the answer at https://www.torproject.org/docs/faq-abuse.html.en#Bans and if you still want to block users from accessing your site you could use https://www.torproject.org/projects/tordnsel.html.en or the Bulk Exit List exporting tool.
If you use the Bulk Exit List exporting tool be sure to get a fresh list often and expire the old blocks since the list of IP addresses change.
Blocking Tor is wrong because (ab)users and IP addresses are not the same. By blocking Tor you will also block legitimate users and harmless restricted Tor exit nodes configured with conservative exit policies.
For example, if you concerned about attacks on SSH (port 22) then blocking only Tor will do little to increase security. What you really might need is dynamic synchronised blacklist like http://denyhosts.sourceforge.net/ that track offenders disregarding of their affiliation with Tor.
Denyhosts will automatically block Tor exit nodes that allow Tor to access port 22 without unnecessary denying access to anonymous users and operators of Tor exit nodes who never let offenders to attack your SSH services.
The Tor Project actually provides its own list here:
https://check.torproject.org/exit-addresses
In .NET it's possible and simple. I have implemented it on my site.
Let's say your site has an external IP address of 192.168.0.5 for argument's sake. Real TOR IP address at the time of posting: 95.215.44.97
Imports System.Net
Imports System.Net.Sockets
Imports System.Web
Private Function IsTorExitNode(sIP As String) As Boolean
' Reverse your IP (97.44.215.95) then pass to the string, I have just created it as one for this example
Try
Dim strTor As String = "97.44.215.95.80.5.0.168.192.ip-port.exitlist.torproject.org"
Dim host As IPHostEntry = Dns.GetHostEntry(strTor)
If host.AddressList.Length = 0 Then
Return False
Else
If host.AddressList(0).ToString() = "127.0.0.2" Then
Return True
Else
Return False
End If
End If
Catch ex As SocketException
Return False
End Try
End Function
Breakdown
Reversed IP address: 97.44.215.95
Port: 80
Reversed IP address: (your external site IP address)
If the address is a TorExitNode it will return 127.0.0.2.
In your Global.asax file, you can use the Application_Start to check if IP address returns true and then redirect them away from your site:
If IsTorExitNode("97.44.215.95") = True Then Response.Redirect("http://www.google.co.uk")
Now, as soon as they hit your site they are redirected away from it.
TOR has a list of IP addresses, but obviously they change all the time so using my function would be the best way as it's always real-time.
You can use the TorDNSEL service to perform a live query about whether a specific IP address is a Tor exit node. You query the service via a specially-formed DNS request.
Here is some sample PHP code that performs the lookup:
function isTorExitNode() {
$serverPort = $_SERVER['SERVER_PORT'];
$remoteAddr = reverseIp(getClientIp());
$serverAddr = reverseIp($_SERVER['SERVER_ADDR']);
$placeholders = '%s.%s.%s.ip-port.exitlist.torproject.org';
$name = sprintf($placeholders, $remoteAddr, $serverPort, $serverAddr);
return ( gethostbyname($name) === '127.0.0.2' );
}
function getClientIp() {
if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
return $_SERVER['HTTP_CF_CONNECTING_IP'];
}
return $_SERVER['REMOTE_ADDR'];
}
function reverseIp($ip) {
$ipParts = explode('.', $ip);
return $ipParts[3] . '.' . $ipParts[2] . '.' .
$ipParts[1] . '.' . $ipParts[0];
}
if (!isTorExitNode()) {
// Do nothing
} else {
Die("Sorry, You cannot use TOR network!!!");
}
Important Notes:
This example supports IPv4 addresses only, not IPv6.
It could take a couple of seconds to get a response, so be careful about introducing delays into your site rendering.
Since TorDNSEL was deprecated and replaced by a new system in april 2020 [1], most of the answers in this thread are outdated.
After a bit of wrangling I came up with this code that uses the new checker. What it does is it reverses the ip octets and creates a URL for the new checker, then performs a DNS request and checks wether or not the first answer has the "127.0.0.2" IP. If this is the case, the user is deemed to come from Tor, otherwise it returns false.
function IsTorExitPoint(){
$dns_record = dns_get_record(ReverseIPOctets($_SERVER['REMOTE_ADDR']).".dnsel.torproject.org.");
if ($dns_record && $dns_record[0] && $dns_record[0]["ip"] == "127.0.0.2") {
return true;
} else {
return false;
}
}
function ReverseIPOctets($inputip){
$ipoc = explode(".",$inputip);
return $ipoc[3].".".$ipoc[2].".".$ipoc[1].".".$ipoc[0];
}
[1] https://lists.torproject.org/pipermail/tor-project/2020-March/002759.html
PD: It's been a while since I've posted an answer to stackoverflow, so please bear with me and help me improve if possible.
It's a fact, that the best application defence is its code and security, not a firewall blocklist. If it's an essential matter for you to have real true users - you have to use two-factor authentication. Blocklists are totally useless nowadays.
Here (see https://github.com/RD17/DeTor) is a simple REST API to determine whether a request was made from TOR network or not.
The request is:
curl -X GET http://detor.ambar.cloud/.
The response is:
{
"sourceIp": "104.200.20.46",
"destIp": "89.207.89.82",
"destPort": "8080",
"found": true
}
As a bonus you can add a badge to your site to detect whether a user comes from TOR or not:
<img src="http://detor.ambar.cloud/badge" />
(This was written for a PHP specific question that was subsequently deleted and linked here as a duplicate).
Disclaimer: Consider the impact of blocking all Tor users as raised in the best answer here. Consider only blocking functions such as registration, payment, comments etc and not a blanket block on everything.
--
Here are two pure PHP solutions. The first downloads and caches a Tor node list and compares the visitor IP against the list. The second uses the Tor DNS Exit List project to determine if the visitor is using Tor via DNS lookups.
Method #1 (Checking IP against a Tor relay list):
Using the following set of functions we can determine if an IP belongs to the Tor network by checking it against a dynamic exit list that gets downloaded and cached for 10 minutes. Feel free to use this list but please cache for 10 minutes when possible.
Where you want to enforce the Tor check, you can simply use:
$isTorUser = isTorUser($_SERVER['REMOTE_ADDR']);
if ($isTorUser) {
// blocking action
}
Here is the code which you can put in a separate functions file and include when you want to run the check. Note, you may want to adjust some of it to change the path to the cache file.
<?php
function isTorUser($ip)
{
$list = getTorExitList();
if (arrayBinarySearch($ip, $list) !== false) {
return true;
} else {
return false;
}
}
function getTorExitList()
{
$path = __DIR__ . '/tor-list.cache';
if ( file_exists($path) && time() - filemtime($path) < 600 ) {
$list = include $path;
if ($list && is_array($list)) {
return $list;
}
}
$data = file('https://www2.openinternet.io/tor/tor-exit-list.txt');
if (!$data) {
return array();
}
$list = array();
foreach($data as $line) {
$line = trim($line);
if ($line == '' || $line[0] == '#') continue;
list($nick, $ip) = explode("\t", $line);
$list[] = $ip;
}
sort($list);
file_put_contents($path, sprintf("<?php return %s;", var_export($list, true)));
return $list;
}
/**
* Perform binary search of a sorted array.
* Credit: http://php.net/manual/en/function.array-search.php#39115
*
* Tested by VigilanTor for accuracy and efficiency
*
* #param string $needle String to search for
* #param array $haystack Array to search within
* #return boolean|number false if not found, or index if found
*/
function arrayBinarySearch($needle, $haystack)
{
$high = count($haystack);
$low = 0;
while ($high - $low > 1){
$probe = ($high + $low) / 2;
if ($haystack[$probe] < $needle){
$low = $probe;
} else{
$high = $probe;
}
}
if ($high == count($haystack) || $haystack[$high] != $needle) {
return false;
} else {
return $high;
}
}
Method #2 (Checking IP against the Tor DNS Exit List Project):
The DNS exit check is a bit more robust in that it takes into account the relay's exit policy and looks at what IP and port on your server the client is connecting to and if such exit traffic is permitted, it will return a match. The potential downfall is that if the DNS project is down temporarily, DNS requests can hang before timing out slowing things down.
For this example, I will use a class from a library I wrote and maintain called TorUtils.
First, you'll need to install it with Composer using composer require dapphp/torutils and include the standard vendor/autoloader.php code in your application.
The code for the check:
$isTor = false;
try {
// check for Tor using the remote (client IP)
if (TorDNSEL::isTor($_SERVER['REMOTE_ADDR'])) {
// do something special for Tor users
} else {
// not using Tor, educate them! :-D
}
} catch (\Exception $ex) {
// This would likely be a timeout, or possibly a malformed DNS response
error_log("Tor DNSEL query failed: " . $ex->getMessage());
}
if ($isTor) {
// blocking action
}
Additional Considerations
If your application uses PHP sessions, I'd highly suggest caching the "isTorUser" response into the session (along with the source IP) and only run the check initially or when the IP changes (e.g. $_SERVER['REMOTE_ADDR'] != $_SESSION['last_remote_addr']) as not to perform many duplicated lookups. Even though they try to be very efficient, it's a waste to do over and over for the same IP.
I found a list of all the Tor nodes updated every half an hour: https://www.dan.me.uk/tornodes
This SHOULD include the exit, entries and bridge nodes used to connect and browse through Tor.
Use this Perl script to gather the IP addresses from a downloaded webpage:
perl -lne 'print $& if /(\d+\.){3}\d+/' downloadedwebpage.html > listofips.out
It will give you a list of IP addresses , one per line. I have tried to find something that will do this without the Perl script, but after many hours searching I could not find one.
I hope this helps.
I also found some good information here too on the same site:
https://www.dan.me.uk/dnsbl
Detecting Tor traffic is rather easy. The main way to do this is to monitor the Tor exit node list and compare the IP against the list.
I had the need to do such a thing recently and built a small Ruby gem to keep the list of exit nodes up to date and provide a simple way to detect exit nodes. I also wrote a small executable you can use to detect exit nodes.
The gem is open source and can be found here: tor-guard
Installing the gem is simple enough:
$ gem install tor-guard
Using the library in your own Ruby code can be done as follows:
require 'tor-guard'
if TorGuard.exit_node?('108.56.199.13')
puts "Yep, it's an exit node!"
end
The executable is also easy to use:
$ tg 108.56.199.13 && echo "Yep, it's an exit node"
It is possible due to the tor project publishing a list of exit proxies.
The list of exit proxies can be downloaded directly from the project at https://check.torproject.org/exit-addresses in space delimited text form.
I have written a python script to add iptables rules for all exit nodes that reject all packets from them. You can find the script on github here: https://github.com/vab/torblock
If the Tor Project ever decides to stop publishing a list of exit nodes it will be possible to block them. Code would just need to be written to connect to the tor network and discover the exit nodes.
Yes, and in fact here is a script that will do it for all of your windows machines. Like others mentioned above, it's as simple as blocking all the exit nodes, but that takes a little work.
https://github.com/Austin-Src/BlockTor
I have already curated the tor nodes and tor exit nodes list which keep updating hourly. Please refer to https://github.com/SecOps-Institute/Tor-IP-Addresses
You can do a git pull every hour and get the most updated list.
For whatever reason I wasn't able to find another answer on here, as of now (20 Shevat 5781 (from Creation)) that has this particular link, so here it is:
https://check.torproject.org/torbulkexitlist
I got it by downloading Tor, then opening up a find my IP address website, then navigating to that IP address (it happens to be http://195.176.3.20/, if you navigate to it you should find the list also), and navigating to it....

Resources