Perl - Net::DNS to create PTR record - perl

First, the relevant xkcd comic: http://xkcd.com/979/
Next, the 10-year old thread on PerlMonks: http://www.perlmonks.org/?node_id=210422
Basically, I'm failing in my attempts to use Net::DNS::Update to create a PTR record and I'd like to see how others have manged this.
Below is what I'm trying. $hst is the hostname that I already have an A record for. $rev is the backwards IP address in-addr.arpa thingy.
# Create the update packet:
my $update = Net::DNS::Update->new($OURDOMAIN);
# Add the PTR record:
$update->push(update => rr_add("$rev 3600 PTR $hst"));
# Send the update to the zone's primary master.
my $res = Net::DNS::Resolver->new;
$res->nameservers("$OURNMSERV");

If $OURDOMAIN is your main domain name, you need to know that you can't put PTR records into your own domain, they have to be put in the right .in-addr.arpa zone.
That zone is most likely being run by your ISP, and they're unlikely to support dynamic updates from end users.

Another ten years later, http://xkcd.com/979/ is still relevant. Having read the question and answer and also the linked PerlMonks thread, it was still unclear to me how to properly add PTR records using Net::DNS.
Well, 'when in Rome do as the Romans do' the saying goes. Having noticed that nsupdate can add PTR records to our internal DNS system just fine without me specifying the reverse zone, I was trying to find out what it does to send valid updates. Turned out it actually sends a SOA query first to figure out what the reverse zone is. Copying this behavior seems to be a somewhat robust way to add PTR records via Net::DNS:
use Net::DNS;
my $hostname = 'somehost.example.com.';
my $ip = '10.11.12.13';
my $primary_ns = 'primary.example.com';
# First, construct the reverse name based on the IP address
my #octets = split /\./, $ip;
my #rev_octs = reverse(#octets);
my $rev_name = join(".", #rev_octs) . '.in-addr.arpa.';
# Then, query the SOA record for the constructed reverse name
my $resolver = Net::DNS::Resolver->new();
$resolver->nameservers($primary_ns);
# Note: Need to use 'send' rather than 'query' since we are interested
# in the 'authority section' of the reply only.
my $packet = $resolver->send($rev_name, 'SOA');
my $zone;
if ($packet) {
# We expect only one authority record
my $len = $packet->authority;
if ($len == 0) {
# Server might not be authorative for reverse lookup zone.
warn "No authority section in reply.\n";
exit 1;
}
my $auth = ($packet->authority)[0];
$zone = $auth->name;
} else {
warn 'query failed: ', $resolver->errorstring, "\n";
exit 1;
}
# Armed with this information, we can finally add the PTR records
my $update = Net::DNS::Update->new($zone);
# Make sure that the PTR record does not already exist
$update->push( pre => nxrrset("$rev_name PTR") );
$update->push( update => rr_add("$rev_name 1200 PTR $hostname") );
my $reply = $resolver->send($update);
if ($reply) {
if ($reply->header->rcode eq 'NOERROR') {
print "Successfully added PTR record!\n";
} else {
print 'Update failed: ', $reply->header->rcode, "\n";
}
} else {
print 'Update failed: ', $resolver->errorstring, "\n";
}

Related

Determining the hash type I am working with for use in hashcat

I am trying to crack some hashed information because the passcode was lost to us. I have the hashed information in the database, and the code that was used to encrypt it. It goes through cryptastic which appears to use rijndael-256 and pbkdf2, as far as my ignorant self can tell:
public function encrypt($msg, $k, $base64 = false)
{
# open cipher module (do not change cipher/mode)
if (!$td = mcrypt_module_open('rijndael-256', '', 'ctr', ''))
return false;
$msg = serialize($msg); # serialize
$iv = mcrypt_create_iv(32, MCRYPT_RAND); # create iv
if (mcrypt_generic_init($td, $k, $iv) !== 0) # initialize buffers
return false;
$msg = mcrypt_generic($td, $msg); # encrypt
$msg = $iv . $msg; # prepend iv
$mac = $this->pbkdf2($msg, $k, 1000, 32, 'sha256'); # create mac
$msg .= $mac; # append mac
mcrypt_generic_deinit($td); # clear buffers
mcrypt_module_close($td); # close cipher module
if ($base64)
$msg = base64_encode($msg);# base64 encode?
return $msg; # return iv+ciphertext+mac
}
And in the end looks like this: wWTWLPvXT9YRz2Zj+Og0EwTTSEiZGdjAQ1TRhycJA9jusjQ2mTpptw3hSM1XJ9yPw+4XvsvFASe08AbLr3BT0LFnvGsYPrq87yI= (I know this to be a 3 digit number if that helps at all)
So I am trying to use hashcat to recover our information and I am not certain I am using the correct hash-type. I am checking this page here: https://hashcat.net/wiki/doku.php?id=example_hashes and searching for 'pbkdf2' and looking at all the hits.
The best match as far as I can tell is 9200/Cisco-IOS $8$ (PBKDF2-SHA256) except that that seems to have a header of $8$, and none of my information has any headers at all, and no $ characters. Everything else with PBKDF2 in it doesn't seem to be a match either so I find myself kind of lost before I've even gotten started.
I also noticed my hashed info always had == on the end, but only for the longer information being encrypted, in the list Juniper IVE seems to fit that format but the name doesn't match anything I can see in cryptastic.
I'm mostly ready to go aside from this as far as I can tell, I have my custom rules set up since I know how we create the initial passcodes and the hashes are in a file to be read, it's just this hash-type selection that is blocking me.
Any help appreciated!

Instagram Real-time API duplicate requests

I have an issue where when I create a real-time subscription I get duplicate notifications from different Instagram IP addresses. I have it set up so that when I get a notification, I send a request for latest updates using the min_tag_id setting. I store that in my db to use it for the next request. I don't always get duplicates, but when I do, everything about the notification is the same (time, object,changed_aspect), except I can tell they are different from my debugging output which lists two almost identical requests... the only differing info being a different IP address and the REQUEST_TIME_FLOAT is different by about 1/10th of a second. They even have the same HTTP_X_HUB_SIGNATURE value.
My general algorithm is:
process_subscription_update($data){
# get old min_id
$min_tag_id = mysqli_fetch_object(mysqli_query($dbconnecti,sprintf("SELECT instagram_min_id+0 as instaid FROM xxxx WHERE xxxx=%d",$_GET['xxxx'])));
$min_id = $min_tag_id->instaid;
# make api call
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL, 'https://api.instagram.com/v1/tags/'.$_GET['tag'].'/media/recent?client_id=xxxx&min_tag_id='.$min_id.($min_id==0?'&count=1':''));
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
$result = curl_exec($ch);
curl_close($ch);
$i = json_decode($result);
if ($min_id == $i->pagination->min_tag_id) { exit; }
# write new min_id to db
record_min_id($i->pagination->min_tag_id);
$data2 = $i->data;
foreach($data2 as $d) {
process_instagram($d);
}
// debugging output: ****************
$file = file_get_contents($_SERVER['DOCUMENT_ROOT'].'instagram/updates.txt');
$foo = "\n";
foreach($_SERVER as $key_name => $key_value) {
$foo .= $key_name . " = " . $key_value . "\n";
}
$fulldata = $file . "\n\n\n" . $result . "\n min_id = " . $min_id . $foo;
$fulldata .= "\nTIME:".$data[0]->time;
$fulldata .= "\nOBJECT:".$data[0]->object;
$fulldata .= "\nCHANGED_ASPECT:".$data[0]->changed_aspect;
file_put_contents($_SERVER['DOCUMENT_ROOT'].'instagram/updates.txt', $fulldata);
// end debugging output *************
}
I'd like to avoid checking if the instagram message id already exists in my db within the process_instagram function, and with the duplicates only coming 1/10th of a second apart, I don't know if that would work anyway.
Anybody else experience this and/or have a solution?
I solved this. I don't think there is anything I can do about receiving the duplicate notifications. So, when writing the Instagram to my db, I have a field for the Instagram id and put a unique constraint on the field. After doing the mysqli INSERT, I check to see if the errno = 1062, and if it does, I exit.
mysqli_query($dbconnecti,"INSERT INTO xxx (foo, etc, instagram_id ...")
if ($dbconnecti->errno==1062) { exit; }
...
// more script runs here if we don't have a duplicate.

Perl Undefined Error on Geo IP lookup

I am using Geo::IP to perform location lookups on ip addresses. Everything works fine until I come across an ip address which is not in the geo ip lookup database and the program shuts abruptly giving this error
Can't call method "city" on an undefined value at script.pl line 16.
Current code looks like this
$gi = Geo::IP->open("/usr/local/share/GeoIP/GeoLiteCity.dat", GEOIP_STANDARD);
my $record = $gi->record_by_addr($key);
my $city= $record->city;
Any suggestions on how I can by pass this? It works perfectly fine until it hits an ip address that isn't defined within that module.
Looking at the Geo::IP source, if the IP address is not in the database, it returns undef. Therefore, to bypass the problem, you can do:
my $record = $gi->record_by_addr($key);
## check that $record is defined
if ($record) {
my $city= $record->city;
...
}
else {
# issue an error message if wanted
warn "No record found for $key";
}
Relevant code from the Geo::IP source:
The function you're using is record_by_addr. From the source, record_by_addr is an alias for get_city_record_as_hash (see perlref for the syntax used to create an 'alias' for a function):
*record_by_addr = \&get_city_record_as_hash;
The code for get_city_record_as_hash is as follows:
#this function returns the city record as a hash ref
sub get_city_record_as_hash {
my ( $gi, $host ) = #_;
my %gir;
#gir{qw/ country_code country_code3 country_name region city
postal_code latitude longitude dma_code area_code
continent_code region_name metro_code / } =
$gi->get_city_record($host);
return defined($gir{latitude}) ? bless( \%gir, 'Geo::IP::Record' ) : undef;
}
This code runs get_city_record using $host, the IP address you supplied, as the argument. If get_city_record finds a record, the data it returns populates the %gir hash. The last line of the sub uses the [ternary form of if-else] to evaluate whether getting the record was successful, and to return the appropriate result. It checks whether $gir{latitude} is defined, and if it is, it creates and returns a Geo::IP::Record object from it (which you can query with methods like city, etc.). If it isn't, it returns undef.
A simpler way to view the last line would be this:
# is $gir{latitude} defined?
if (defined ($gir{latitude})) {
# yes: create a Geo::IP::Record object with the data in %gir
# return that object
return bless( \%gir, 'Geo::IP::Record' )
}
else {
# no: return undefined.
return undef;
}
I'd suggest that you need Data::Dumper here, to tell you what's going on with $record. I would guess that record_by_addr($key); is the root of your problems, and that because $key is in some way bad, $record is undefined.
This would thus be fixed:
use Data::Dumper;
print Dumper \$record;
I'm guessing $record will be undefined, and therefore:
next unless $record;
will skip it.

using perl to get users of AD group

I've been trying to get this to print out all the members in "domain users". Problem is, it only prints out a small portion of them, then it just sorta stops. Not sure why. Can someone shed some light on the problem?
#!/usr/bin/perl
use Net::LDAP;
my $uid = "cn=account,cn=users,dc=domain,dc=local";
my $bindPass = "password";
my $ldapServer = "ldap://server.domain.local";
# connect to ldap server
$ldap = Net::LDAP -> new ($ldapServer) || die "Could not connect to server\n";
# bind to ldap server
$ldap -> bind($uid, password => $bindPass);
# search for group
$mesg = $ldap -> search(filter => "(&(cn=Domain Users))", base => "dc=domain,dc=local");
$entry = $mesg -> entry;
# #members = $entry -> get_value ('member;Range=0-*');
#the above entry when uncommented doesn't work either.
#members = $entry -> get_value ('member');
foreach $thing (#members) {
print "$thing\n";
}
From the Net::LDAP docs:
sizelimit => N
A sizelimit that restricts the maximum number of entries to be
returned as a result of the search. A value of 0, and the default,
means that no restriction is requested. Servers may enforce a maximum
number of entries to return.
It might very well be your AD server has a restriction configured. Try checking $mesg->error() after the search.
You might have more success if you use ldap://server.domain.local:3268/ as your URL. AD uses a "mini" ldap server on that port to talk to replicated servers (google "global catalog"); you won't see all attributes on that server, but maybe it's less restrictive as to the maximum number of entries.

Perl - How to get the email address from the FROM part of header?

I am trying to set up this script for my local bands newsletter.
Currently, someone sends an email with a request to be added, we manually add it to newsletter mailer I set up.
(Which works great thanks to help I found here!)
The intent now is to have my script below log into the email account I set up for the list on our server, grab the info to add the email automatically.
I know there are a bunch of apps that do this but, I want to learn myself.
I already have the "add to list" working when there is an email address returned from the header(from) below BUT, sometimes the header(from) is a name and not the email address (eg "persons name" is returned from persons name<email#address> but, not the <email#address>.)
Now, I am not set in stone on the below method but, it works famously... to a point.
I read all the docs on these modules and there was nothing I could find to get the darn email in there all the time.
Can someone help me here? Verbose examples are greatly appreciated since I am struggling learning Perl.
#!/usr/bin/perl -w
##########
use CGI;
use Net::IMAP::Simple;
use Email::Simple;
use IO::Socket::SSL; #optional i think if no ssl is needed
use strict;
use CGI::Carp qw(fatalsToBrowser warningsToBrowser);
######################################################
# fill in your details here
my $username = '#########';
my $password = '#############';
my $mailhost = '##############';
#######################################################
print CGI::header();
# Connect
my $imap = Net::IMAP::Simple->new($mailhost, port=> 143, use_ssl => 0, ) || die "Unable to connect to IMAP: $Net::IMAP::Simple::errstr\n";
# Log in
if ( !$imap->login( $username, $password ) ) {
print STDERR "Login failed: " . $imap->errstr . "\n";
exit(64);
}
# Look in the INBOX
my $nm = $imap->select('INBOX');
# How many messages are there?
my ($unseen, $recent, $num_messages) = $imap->status();
print "unseen: $unseen, <br />recent: $recent, <br />total: $num_messages<br />\n\n";
## Iterate through unseen messages
for ( my $i = 1 ; $i <= $nm ; $i++ ) {
if ( $imap->seen($i) ) {
my $es = Email::Simple->new( join '', #{ $imap->top($i) } );
printf( "[%03d] %s\n\t%s\n", $i, $es->header('From'), $es->header('Subject'));
print "<br />";
next;
}## in the long version these are pushed into different arrays for experimenting purposes
else {
my $es = Email::Simple->new( join '', #{ $imap->top($i) } );
printf( "[%03d] %s\n\t%s\n", $i, $es->header('From'), $es->header('Subject'));
print "<br />";
}
}
# Disconnect
$imap->quit;
exit;
use Email::Address;
my #addresses = Email::Address->parse('persons name <email#address>');
print $addresses[0]->address;
The parse method returns an array, so the above way works for me.
I'm making this a separate answer because even though this information is hidden in the comments of the accepted answer, it took me all day to figure that out.
First you need to get the From header using something like Email::Simple. THEN you need to extract the address portion with Email::Address.
use Email::Simple;
use Email::Address;
my $email = Email::Simple->new($input);
my $from = $email->header('From');
my #addrs = Email::Address->parse($from);
my $from_address = $addrs[0]->address; # finally, the naked From address.
Those 4 steps in that order.
The final step is made confusing by the fact that Email::Address uses some voodoo where if you print the parts that Email::Address->parse returns, they will look like simple strings, but they are actually objects. For example if you print the result of Email::Address->parse like so,
my #addrs = Email::Address->parse($from);
foreach my $addr (#addrs) { say $addr; }
You will get the complete address as output:
"Some Name" <address#example.com>
This was highly confusing when working on this. Granted, I caused the confusion by printing the results in the first place, but I do that out of habit when debugging.