Ecco un dumper in ascolto su di un socket scritto in java.

import java.net.*;
import java.io.*;

public class Dumper {
    public static void main(String[] args) {
        try {
            int portNumber = 1234;
            try {
                portNumber = Integer.parseInt(System.getProperty("port"));
            } catch (Exception e) {
            }
            ServerSocket serv = new ServerSocket(portNumber);

            for (;;) {
                Socket sock = serv.accept();
                InputStream inStream = sock.getInputStream();
                int ch;
                while ((ch = inStream.read()) >= 0) {
                    System.out.print((char) ch);
                }
                sock.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

A che serve? Non a molto.
E’ un server in ascolto su una porta e spamma sul terminale. Puntando netcat o telnet, possiamo scrivere lato client e vedere che si dice lato server.
Oppure possiamo puntarci un browser sopra e sapere quali righe invia, per esempio, Iceweasel 2 per presentarsi ad un server web:

GET / HTTP/1.1
Host: xxxxx.yyy.zz:1234
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.17)
    Gecko/20080827 Iceweasel/2.0.0.17 (Debian-2.0.0.17-0etch1)
Accept: text/xml,application/xml,application/xhtml+xml,text/html;
    q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: it,en;q=0.7,it-ch;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive

MySQL è the world’s most popular open source database.
C è un (il?) linguaggio di programmazione sviluppato da D. Ritchie per implementare Unix OS.
L’API è l’application programming interface.

Il file /usr/include/mysql/mysql.h è l’header che contiene classi, funzioni e variabili per mysql (#include <mysql/mysql.h>).

Il manualazzo delle API MySQL.

MYSQL è la struttura per maneggiare la connessione al database (le si associa una variabile, e ne si richiama l’indirizzo con l’operatore unario & ogni qual volta serva – cioè sempre).

typedef struct st_mysql
{
  [... blah blah blah ...]
  char          *host,*user,*passwd,*unix_socket,*server_version,*host_info,*info;
  char          *db;
  struct charset_info_st *charset;
  MYSQL_FIELD   *fields;
  MEM_ROOT      field_alloc;
  my_ulonglong affected_rows;
  [... blah blah blah ...]
  my_bool       free_me;                /* If free in mysql_close */
  my_bool       reconnect;              /* set to 1 if automatic reconnect */

  /*
    Points to boolean flag in MYSQL_RES  or MYSQL_STMT. We set this flag
    from mysql_stmt_close if close had to cancel result set of this object.
  */
  my_bool *unbuffered_fetch_owner;
  [... blah blah blah ...]
} MYSQL;

MYSQL_RES è la struttura rappresentante il risutlato di una query che ritorna delle righe (es. SELECT).

typedef struct st_mysql_res {
  my_ulonglong row_count;
  MYSQL_FIELD   *fields;
  MYSQL_DATA    *data;
  MYSQL_ROWS    *data_cursor;
  unsigned long *lengths;               /* column lengths of current row */
  MYSQL         *handle;                /* for unbuffered reads */
  MEM_ROOT      field_alloc;
  unsigned int  field_count, current_field;
  MYSQL_ROW     row;                    /* If unbuffered read */
  MYSQL_ROW     current_row;            /* buffer to current row */
  my_bool       eof;                    /* Used by mysql_fetch_row */
  /* mysql_stmt_close() had to cancel this result */
  my_bool       unbuffered_fetch_cancelled;
  const struct st_mysql_methods *methods;
} MYSQL_RES;

MYSQL_ROW è un array che rappresenta una riga di dati.

typedef char **MYSQL_ROW;               /* return data as array of strings */

Ci metto pure il MYSQL_FIELD, visto che viene considerato dalla struct RES.

typedef struct st_mysql_field {
  char *name;                 /* Name of column */
  char *org_name;             /* Original column name, if an alias */
  char *table;                /* Table of column if column was a field */
  char *org_table;            /* Org table name, if table was an alias */
  char *db;                   /* Database for table */
  char *catalog;              /* Catalog for table */
  char *def;                  /* Default value (set by mysql_list_fields) */
  [... blah blah blah ...]
  enum enum_field_types type; /* Type of field. See mysql_com.h for types */
} MYSQL_FIELD;

Dopo aver creato il socket, averlo bind’ato e messo in ascolto, nonche’ averlo connesso lato client, le nostre due macchine possono comunicare grazie ai comandi send() e recv():

int send(int sockd, void *buf, int len, int opt)
int recv(int sockd, void *buf, int len, int opt)

Il parametro len indica la lunghezza del buffer (dei dati da mandare o ricevere) e opt sono le opzioni (spesso 0).


Tutto quanto ho descritto finora è perfetto per la gestione TCP.

Nel caso del protocollo UDP non c’e’ connessione, quindi si possono evitare connect(), listen() e accept().

La comunicazione avviene fra i socket di client e server (con socket bind’ato) grazie a funzione come sendto e recvfrom – il kernel riceve i pacchetti datagram e li riceve sul processo in ascolto sulla porta a cui sono diretti oppure li scarta (con conseguente messaggio ICMP “port unreachable” al client) se non c’e’ alcun processo in ascolto.

ssize_t sendto(int sockd, const void *buf, size_t len,
        int flags, const struct sockaddr *to, socklen_t tolen)

Flags è un parametro di opzioni che va di default a 0, to e tolen sono rispettivamnete l’indirizzo di destinazione e la sua lunghezza.

ssize_t recvfrom(int sockd, const void *buf,
        size_t len, int flags, const struct sockaddr *from,
        socklen_t *fromlen)

(Si possono mettere from e fromlen a NULL se non si è interessati a sapere i dati dell’indirizzo di origine.)

Dopo aver creato un socket e creato una struttura coi dati dell’host necessari, dobbiamo associare le due cose grazie alla funzione bind().
La funzione bind viene utilizzata dal server per associare il socket ad un ip ed una porta; un client non usa bind perche’ il suo indirizzo viene determinato dall’interfaccia usata, e la porta la decide il kernel scegliendo la prima disponibile.
Il prototipo di bind è:

int bind(int sockd, const struct sockaddr *serv_addr, socklen_t addrlen)

dove viene considerato il socket descriptor, la struttura dell’indirizzo del server, e la dimensione dell’indirizzo.

L’indirizzo puo’ essere inserito liberamente, oppure si possono utilizzare alcune costanti predefinite:

  • INADDR_ANY per indicare qualunque indirizzo (0.0.0.0)
  • INADDR_BROADCAST
  • INADDR_LOOPBACK (127.0.0.1)

Dopo il bind’aggio del socket, si deve indicare la server di rimanere in ascolto con listen() e di accettare con accept() eventuali connessioni.

int listen(int sockd, int backlog)
int accept(int sockd, struct sockaddr *addr,
        socklen_t *addrlen)

Nel caso di accept, l’indirizzo e la dimensione relativa sono del client che invia la richiesta di connessione (nel caso ci servissero i dati per loggare i contatti).


La connessione TCP fra client e server si stabilisce grazie alla funzione connect() dopo aver creato il socket-lato-client:

int connect(int sockd, const struct sockaddr *serv_addr,
        socklen_t addrlen)

Forse non tutti sanno che l’ordine di lettura dei byte non è fissato (wiki).
Le diverse architetture dei calcolatori hanno un modo differente di leggere dati di dimensione superiore al byte: la cosa fondamentale è che puo’ cambiare l’ordine in cui diversi byte consecutivi vengono memorizzati.
Big endian è il caso in cui le posizioni di memoria sono occupate a partire dal byte piu’ significativo (a sinistra) (Mac, per esempio, usa questo modo).
Little endian è il caso in cui le posizioni di memoria partono dal byte piu’ a destra (Intel).

Esempio:

Se vogliamo memorizzare la stringa ABCD, e la localizziamo all’indirizzo di memoria 100, in big endian avremo A al 100, B al 101, C al 102, D al 103; mentre in little endian avremo D al 100, C al 101, B al 102 e A al 103.

Ovviamente questo problema diventa immenso quando si inseriesce come stringa un numero (ad esempio una porta): 1234 puo’ diventare 4321 con effetti assai diversi.

Il formato di rete dei byte, o network byte order, è big endian; per questo è necessario convertire i valori numerici degli ip e delle porte in big endian – per fare questo ci vengono in soccorso le funzioni htonl (per i long int) e htons (per gli short). Ad esempio nel definire la struttura dell’indirizzo abbiamo:

struct sockaddr_in sa;

sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = htonl(INADDR_ANY);

In generale un socket di rete è un tubo tra due processi (ie: client – server) col quale ciascuno dei due processi puo’ interagire (leggere, scrivere, ecc.).

Il prototipo per creare un socket in C è il seguente:

int socket (int domain, int type, int prot)

Il parametro domain indica la famiglia da usare (AF_INET, AF_INET6, AF_UNIX), il type definisce la comunicazione (tcp, upd: SOCK_STREAM, SOCK_DGRAM) e protocol definisce un particolare protocollo (generalmente vale 0).

Il socket da solo non vale niente (allocca le strutture necessarie nel kernel); è quindi necessario fornire gli indirizzi secondo strutture di dati prestabilite (in sys/socket.h):

struct sockaddr
{
    sa_family_t  sa_family;
    char         sa_data[14];
}

La struttura è volutamente generica: le funzioni che usano gli indirizzi hanno nel prototipo un puntatore alla struttura, e necessitano di un casting (del puntatore) per farsi capire dal protocollo voluto.

Gli indirizzi IPv4 seguono la seguente struttura:

struct  sockaddr_in
{
     sa_family_t     sin_family;
     in_port_t       sin_port;
     struct in_addr  sin_addr;
     unsigned char   sin_zero[8];
}

e la struttura in_addr (che contiene l’IPv4 di 32 bit) risulta:

struct  in_addr
{
      u_int32_t       s_addr;
}

Le strutture IPv6 e per indirizzi locali hanno alcuni campi specifici.

Il perche’ Il Creatore (comunque lo vogliate chiamare) abbia inventato la nebbia, non lo so.
Comunque so per certo che ci deve essere qualcosa che non gli piace nella terra fra Ferrara e Rovigo…
O_o

Almeno qualche pistolotto che sfrutta i socket in C (e le struct) è venuto fuori… figata notevole. ^_^

$ man 7 ip
$ man socket
$ man bind / listen / connect
$ man accept / send /recv

http://www.cs.utah.edu/dept/old/texinfo/glibc-manual-0.02/library_15.html

    
SIMOTRONE WEB PAGE is based on WordPress platform, RSS tech , RSS comments design by Gx3.