synapse/check.server.pl
changeset 11 3d9a0d238469
equal deleted inserted replaced
10:aaa61e5b4526 11:3d9a0d238469
       
     1 #!/usr/bin/perl
       
     2 #$Id: check.server.pl,v 1d715c5989e8 2022/08/03 08:42:01 grin $
       
     3 #
       
     4 # (cc)by_sa-4.0 / GPLv3+  peter 'grin' gervai  @grin:grin.hu
       
     5 #
       
     6 # hastily check a matrix server
       
     7 
       
     8 ## requires:
       
     9 ##  - libjson-perl
       
    10 ##  - curl
       
    11 ##  - fping
       
    12 ##  - dnsutils (dig)
       
    13 
       
    14 ## todo:
       
    15 ##  - apart from complete rewriting?
       
    16 ##  - check whether curl, fping, dig present
       
    17 ##  - well, really, check _anything_. no real error handling as of now.
       
    18 
       
    19 # returns:
       
    20 #  1: error
       
    21 #  0: ok
       
    22 
       
    23 use warnings; use strict;
       
    24 
       
    25 # https://$d/.well-known/matrix/server
       
    26 # https://$d/.well-known/matrix/support
       
    27 
       
    28 use JSON;
       
    29 use Data::Dumper;
       
    30 
       
    31 # 1: print everything, verbosely, even if it's unparseable.
       
    32 # 0: less chat, bad results are fatal; use exit code
       
    33 my $VERBOSE=1;
       
    34 
       
    35 my $j = JSON->new->allow_nonref;
       
    36 
       
    37 my $d = $ARGV[0];
       
    38 if( !defined( $d ) ) {
       
    39     print "Usage: $0 <matrix_domain>\nExample: $0 grin.hu\n";
       
    40     exit 1;
       
    41 }
       
    42 
       
    43 my $data_source=0;
       
    44 # curl parameters for connection
       
    45 my $timeout="--connect-timeout 5";
       
    46 my $follow_redirs="--location --max-redirs 4";
       
    47 
       
    48 # collect well-known, infodata, nodeinfo, dns SRV
       
    49 my ($srv,$port) = &get_wk($d);
       
    50 &get_wk_infodata($d);
       
    51 &get_wk_nodeinfo2($d);
       
    52 my ($srv2,$port2) = &get_srv($d);
       
    53 
       
    54 if( $srv ) { 
       
    55     # we have well-known data, lovely
       
    56     $data_source=1;
       
    57 } elsif( $srv2 ) {
       
    58     # we have SRV, it'll do
       
    59     ($srv,$port) = ($srv2,$port2);
       
    60     $data_source=2;
       
    61 } else {
       
    62     # lazy admin mode
       
    63     ($srv,$port) = ($d, 8448);
       
    64     $data_source=3;
       
    65 }
       
    66 
       
    67 print "FINAL result is $srv:$port. Checking.\n";
       
    68 my $srv_full = "$srv:$port";
       
    69 
       
    70 # ping the server first
       
    71 print "Ping: ";
       
    72 my $res = `fping --retry=3 --timeout=500 $srv`; chomp $res;
       
    73 print "$res!\n";
       
    74 
       
    75 # get server version
       
    76 print "SrvVersion: ";
       
    77 
       
    78 my $cmd1;
       
    79 if( $data_source == 2 ) {
       
    80     # SRV uses domain for SNI
       
    81     $cmd1 = "-s $timeout $follow_redirs --connect-to $srv https://$d:$port/_matrix/federation/v1/version";
       
    82 } else {
       
    83     # everyone else go for the hostname
       
    84     $cmd1 = "-s $timeout $follow_redirs https://$srv_full/_matrix/federation/v1/version";
       
    85     
       
    86     if( $srv_full eq "" ) {
       
    87         # shall not happen (and really, can't now)
       
    88         &fail( "srv_full empty while data_source=$data_source");
       
    89     } 
       
    90 }
       
    91 my $cmd  = "curl $cmd1";
       
    92 $res = qx($cmd);
       
    93 if( $res =~ /"server"/ ) {
       
    94     # seems like valid data
       
    95     my $data = $j->decode($res);
       
    96     my $v = $$data{'server'};
       
    97     
       
    98     print $$v{name} . '/' . $$v{version}."!\n";
       
    99 
       
   100 } else {
       
   101     # got something, but it's crap.
       
   102     print "no parseable data\n";
       
   103     if( !$VERBOSE ) {
       
   104         &fail("no paresable data");
       
   105     }
       
   106     
       
   107     print "here's the debug: ";
       
   108     $cmd="curl $timeout --verbose $cmd1";
       
   109     print "$cmd\n";
       
   110     system $cmd;
       
   111     &fail("no parseable data");
       
   112 }
       
   113 
       
   114 # sucess
       
   115 exit 0;
       
   116 
       
   117 
       
   118 ##########
       
   119 
       
   120 
       
   121 # get well-known in a naïve way
       
   122 sub get_wk {
       
   123     my ($d) = @_;
       
   124     
       
   125     ## try well-known matrix/server (simplistic parser; spec)
       
   126     print "WellKnown: ";
       
   127     $cmd = "curl $timeout $follow_redirs -s https://$d/.well-known/matrix/server";
       
   128     $res = qx($cmd);
       
   129 
       
   130     if( $res=~/m.server/ ) {
       
   131         my $data = $j->decode($res);
       
   132         my $srv_full = $$data{'m.server'};
       
   133         if( $srv_full =~ /^(.+):(.+)$/ ) {
       
   134             # we have proper form
       
   135             ($srv,$port) = ($1,$2);
       
   136             } else {
       
   137                 # missing port or crap result
       
   138                 print Dumper($data);
       
   139                 print "$srv_full don't contain port!!\n";
       
   140                 print "FAKING port 8448\n";
       
   141                 $port=8448;
       
   142             }
       
   143         print "$srv on port $port!\n";
       
   144     } else {
       
   145         # I can't even...
       
   146         print " (unparseable response from $cmd )\n";
       
   147         print "$res\n" if $VERBOSE;;
       
   148         print "No well-known 'm.server'.\n";
       
   149         return;
       
   150     }
       
   151     return ($srv,$port);
       
   152 }
       
   153 
       
   154 
       
   155 ## get well-known/matrix/support info (infodata; MSC1929)
       
   156 sub get_wk_infodata {
       
   157     my ($d) = @_;
       
   158 
       
   159     print "# Checking https://$d/.well-known/matrix/support\n";    
       
   160     print "WellKnown(MSC1929): ";
       
   161     $cmd = "curl $timeout $follow_redirs -s https://$d/.well-known/matrix/support";
       
   162     $res = qx($cmd);
       
   163 
       
   164     if( $res=~/admins/ ) {
       
   165         my $data = $j->decode($res);
       
   166         my $admins = $$data{'admins'};
       
   167         
       
   168         for my $entry (@$admins) {
       
   169             print $$entry{'role'}.":";
       
   170             delete $$entry{'role'};
       
   171             for my $k (sort keys %$entry) {
       
   172                 print "$k=" . $$entry{$k}." ";
       
   173             }
       
   174             print "; ";
       
   175         }
       
   176         print "\n";
       
   177     } else {
       
   178         print "nope\n";
       
   179     }
       
   180 }
       
   181 
       
   182 
       
   183 # try get nodeinfo2, https://github.com/jaywink/nodeinfo2
       
   184 sub get_wk_nodeinfo2 {
       
   185     my ($d) = @_;
       
   186 
       
   187     print "# checking https://$d/.well-known/x-nodeinfo2\n";    
       
   188     print "nodeinfo2: ";
       
   189     $cmd = "curl $timeout $follow_redirs -s https://$d/.well-known/x-nodeinfo2";
       
   190     $res = qx($cmd);
       
   191     if( $res =~ /version/ ) {
       
   192         my $data = $j->decode($res);
       
   193         if( $$data{'version'} ne '1.0' ) {
       
   194             print " (unknown version " . $$data{'version'} . ") ";
       
   195         }
       
   196 
       
   197         if( $$data{'openRegistrations'} ) {
       
   198             print "OPENREG! ";
       
   199         }
       
   200 
       
   201         print "proto: " . join(',', @{$$data{'protocols'}}) . " ";
       
   202 
       
   203         print "srv: ";
       
   204         for my $field ( qw( baseUrl name software ) ) {
       
   205             print "$field=" . $$data{'server'}{$field} . " ";
       
   206         }
       
   207 
       
   208         print "org: ";
       
   209         for my $field ( qw( name contact admin ) ) {
       
   210             print "$field=" . $$data{'organization'}{$field} . " ";
       
   211         }
       
   212     } else {
       
   213         print "none";
       
   214     }
       
   215     print "\n";
       
   216 }
       
   217 
       
   218 # DNS SRV in an extremely lazy way
       
   219 sub get_srv {
       
   220     my ($d) = @_;
       
   221         
       
   222     my ($srv,$port);
       
   223 
       
   224     ## try srv (simplistic as well)
       
   225     print "SRV (_matrix._tcp.$d): ";
       
   226     $res = `dig _matrix._tcp.$d srv +short`;
       
   227     print "$res";
       
   228     if( $res =~ /^(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\.$/ ) {
       
   229         # looks good
       
   230         my ($pri1,$pri2);
       
   231         ($pri1, $pri2, $port, $srv) = ($1,$2,$3,$4);
       
   232     } else {
       
   233         print "No SRV found.\n";
       
   234         return;
       
   235     }
       
   236     return ($srv,$port);
       
   237 }
       
   238     
       
   239 
       
   240 # guess what?
       
   241 sub fail {
       
   242     my ($msg) = @_;
       
   243     print "FAIL: $msg\n";
       
   244     exit(1);
       
   245 }
       
   246