Bash port scanner

Stavo facendo qualche esperimento con la shell, e leggendo un pò a caso in rete, ho provato a realizzare un semplice port scanner in bash scripting. Molti degli elementi su Linux funzionano come dei file, ovvero è possibile accedere in lettura per ottenere informazioni e in scrittura per aggiornare il loro stato. Un esempio è il file /dev/tcp/host/port, in particolare scrivendo su questo file speciale viene aperta una connessione TCP verso host:port. Se la scrittura su file ha successo allora la porta viene aperta, altrimenti la porta è chiusa. That's the trick !

for port in {1..65535}; do
  echo >/dev/tcp/devme.it/$port &&
    echo "Yes, I found that port $port is open" ||
    echo "Ops, I found that port $port is closed"
done

Il codice di sopra funziona esattamente come un port scanner con l'unico problema che se la porta risulta chiusa, la shell la rileva dopo circa un paio di minuti, il che rende in pratica poco utilizzabile lo script di sopra. La soluzione che ho trovato in rete è quella di realizzare una sorta di allarme che interrompe lo script se il tempo impiegato per analizzare una porta supera un certo limite, passato come parametro. Riporto il codice per completezza:

alarm() {
  perl -e  '
    eval {
      $SIG{ALRM} = sub { die };
      alarm shift;
      system(@ARGV);
    };
    if ($@) { exit 1 }
   ' "$@";
}

Volendo scrivere un comando completo, il tutto diventa:

scanme() {
  if [[ -z $1 || -z $2 ]]; then
    echo "Usage: $0 <host> <port> <port-range>"
    return
  fi
 
  local host=$1
  local ports=()
  case $2 in
    *-*)
      IFS=- read start end <<< "$2"
      for ((port=start; port <= end; port++)); do
        ports+=($port)
      done
      ;;
    *,*)
      IFS=, read -ra ports <<< "$2"
      ;;
    *)
      ports+=($2)
      ;;
  esac
 
 
  for port in "${ports[@]}"; do
    alarm 1 "echo >/dev/tcp/$host/$port" &&
    echo "Yes, I found that port $port is open" ||
    echo "Ops, I found that port $port is closed"
  done
}

Testando il comando di sopra si ha: scanme devme.it 80-92

#$> scan scanme 80-92 
Yes, I found that port 80 is open
Ops, I found that port 81 is closed
Ops, I found that port 82 is closed

Esiste poi un comando di sistema che consente di lanciare un comando con un time limit, timeout. Con questo comando il tutto diventa molto più semplice:

#$> timeout 1 bash -c "echo >/dev/tcp/$host/$port" &&
    echo "Yes, I found that port $port is open" ||
    echo "Ops, I found that port $port is closed"

Stay tuned!

RESTEasy: le chiamate rest in Java

In questo piccolo tutorial vedremo come realizzare una piccola chiamata REST verso un servizio remoto, il tutto realizzato in Java. Ricordiamo che le chiamate REST consentono di invocare servizi remoti tramite il protocollo HTTP.
La libreria che ho utilizzato per il supporto alle chiamate REST è RESTEasy la quale oltre al supporto HTTP fornisce il supporto alle annotation JAX-RS. Vediamo subito come procedere. Intanto, utilizzando il vostro IDE preferito (Eclipse ad esempio) create un nuovo progetto e aggiungete le seguente librerie nel build path: 

libraries

Alcune di queste non sono essenziali per la creazione del servizio, ma vi consiglio comunque di lasciarle in modo tale da avere supporto a future implementazioni. Il progetto che sono andato a creare è J2EE installato all’interno di tomcat. Ogni servizio verrà testato da una unit test presente all’interno del progetto stesso, così da verificare la correttezza del servizio. Quindi si parte con la configurazione del nostro web descriptor in modo da configurare il nostro contesto: 

<?xml version="1.0" encoding="UTF-8"?>
 
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
 
    <display-name>resteasy</display-name>
 
    <listener>
        <listener-class>
            org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
        </listener-class>
    </listener>
 
    <servlet>
        <servlet-name>drest</servlet-name>
        <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>drest</servlet-name>
        <url-pattern>/restful-services/*</url-pattern>
    </servlet-mapping>
 
    <context-param>
         <param-name>resteasy.scan</param-name>
        <param-value>true</param-value>    
    </context-param>
 
 
    <context-param>
        <param-name>resteasy.servlet.mapping.prefix</param-name>
        <param-value>/restful-services</param-value>
    </context-param>
 
</web-app>

Fatto questo procediamo con la definizione del servizio. Una premessa fondamentale da fare è la seguente: la creazione di un servizio REST richiede una fase di progettazione attenta e puntuale, in cui molti aspetti devono essere tenuti in considerazione e risolti nel migliore dei modi. L’accesso al servizio, l’autenticazione, l’invio dei dati in input e la restituzione del risultato sono tutti aspetti che fanno parte della progettazione di un servizio. Ciò detto, nel nostro esempio, non considereremo la fase di progettazione in tutti i suoi punti, ma procederemo con la creazione di un servizio a scopo di esempio. Definiamo quindi la classe contenitore del nostro servizio attraverso l’utilizzo delle annotation:

@Path("/devmeservice")
public class DevMeService {
 
	@GET
    @Path("/v1/passes/{pass_type_id}/{device_id}")
    @Produces("text/plain")
    public Response getPassLatestVersion(@HeaderParam("Authorization") String authorization,
					   @PathParam("pass_type_id")String pass_type_id,
					   @PathParam("serial_number")String serial_number){
 
		String auth_token = null;
		if (!authorization.matches("^ApplePass.*")) {
			return Response.status(HttpStatus.SC_FORBIDDEN).build();
		}
		String [] items = authorization.split(" ");
		if (items.length!=2) {
			return Response.status(HttpStatus.SC_FORBIDDEN).build();
		}
 
		auth_token = items[1];
 
		String sql = "SELECT * " +
			 "FROM passes " +
			 "WHERE serial_number = ? AND authentication_token = ? LIMIT 1";
		DBHandle dbhandle = DBHandle.getInstance();
		try {
			PreparedStatement ps = dbhandle.getPreparedStatement(sql);
			ps.setString(1, serial_number);
			ps.setString(2, pass_type_id);
			ResultSet rs = ps.executeQuery();
			if (rs.next()) {
				String pass = readFile(/* PASSES ROOT PATH */"/data/passes/"+serial_number+"/pass.json");
 
			}else {
				return Response.status(HttpStatus.SC_UNAUTHORIZED).build();
			}
		} catch (SQLException e) {
			log.error("Error while registering new device.", e);
			return Response.status(HttpStatus.SC_SERVICE_UNAVAILABLE).build();
		} finally {
			dbhandle.close();
		}
		return null;
    }
 
}

Il metodo di sopra restituisce l’ultima versione di un pass (Passbook) associato ad un particolare dispositivo, sulla base di un codice di autorizzazione concesso al client e dei dati associato al pass stesso. Il metodo verifica prima se il client è autorizzato all’accesso al servizio e successivamente tenta di recuperare le informazioni associate al pass e quindi di restituirlo in output.
Si noti l’utilizzo delle annotation in testa alla classe e in testa al nostro metodo, la prima definizione il nome del servizio che dovrà essere invocato, una sorta di base url. Le seconde annotation definiscono il metodo HTTP che viene utilizzato per l’invocazione del metodo, il percorso del servizio ed infine il mime type della risposta fornita dal metodo. Invocando il servizio con un client HTTP saremo in grado di ottenere la risposta da elaborare localmente.

Nei prossimi articoli tratteremo altri contesti in cui utilizzare le chiamate REST, come i Passbook recentemente introdotti dalla Apple come prossima release in iOS6.

Stay tuned!

WordPress Themes