View Full Version : [C] Proxy: ricevere ed inoltrare una pagina web
Ciao!
Ho un problema nel far funzionare correttamente il proxy http che sto scrivendo: quando un client (generalmente un browser) si connette al mio proxy, quest'ultimo riceve la richiesta in questo formato:
GET http://www.hwupgrade.it/ HTTP/1.1
Host: www.hwupgrade.it
User-Agent: Mozilla/5.0 (X11; U; Linux i686; rv:1.7.3) Gecko/20041020 Firefox/0.10.1
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: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Proxy-Connection: keep-alive
dalla quale estrae il nome dell'host, che usera' poi come parametro nella funzione gethostbyname() per ricavare l'indirizzo IP dell'host. Fin qui tutto ok. Successivamente inizializzo un nuovo socket ed effettuo la connessione all'host di cui sopra, tramite la funzione connect(), inoltrando la stessa richiesta ricevuta dal proxy ma rimuovendo il nome dell'host dalla prima riga (cosi' come descritto nell'RFC 2616 sul protocollo HTTP), in modo che dopo GET vi sia il path assoluto della pagina richiesta.
Il problema e' che il server che risponde tenta di redirigere la connessione de qualche altra parte, e io non so gestire la cosa. Non esiste un modo diverso per fare questa ricezione/inoltro?
Grazie,
Gica
Ho fatto qualche cambiamento nella parte relativa alla risoluzione dei nomi... Non c'entra nulla con il problema precedente, ma poiche' ho visto che la funzione gethostbyname() e' ormai deprecata, l'ho sostituita con getaddrinfo() (standard POSIX 1003.1-2001). Solo che adesso succede qualcosa di inaspettato. Non vi posto tutto il codice, vi descrivo sommariamente cosa faccio:
1) Nella funzione main() inizializzo i socket per le connessioni, e metto in ascolto il socket associato al server;
2) quando arriva una nuova connessione, genero un processo figlio che si occupa di gestirla, mentre il padre torna in attesa di nuove connessioni;
3) il processo figlio legge sul socket la richiesta inviata dal browser (per ora supporta solo le GET), ne estrae l'URL richiesto e il nome dell'host dove reperirlo, quindi invoca la funzione id_gethostbyname() (opera mia) per la risoluzione dei nomi a dominio, che e' la seguente:
/* Function: id_gethostbyname()
*
* Purpose: try to get the IPv4 address of the host whose name is passed
* with the info->h_name string.
*
* Parameters:
* - struct req_info *info: a pointer to a (preallocated) structure of
* req_info type, in which will be stored the response information.
*
* Return value:
*
* 0 on success, in which case the function returns the IP address
* in the h_addr field of info structure passed in;
*
* -1 on error.
*/
int
id_gethostbyname(struct req_info *info)
{
int ret; /* return value */
struct addrinfo hints; /* hints for the research */
struct addrinfo *res; /* research result */
/* hints initialization: we need an IPv4 address to
* use in a SOCK_STREAM TCP socket */
hints.ai_flags=0;
hints.ai_family=PF_INET;
hints.ai_socktype=SOCK_STREAM;
hints.ai_protocol=0;
ret=getaddrinfo(info->h_name, NULL, &hints, &res);
if (ret!=0)
{
printf("Resolution error: %s\n", gai_strerror(ret));
return -1;
}
while (res!=NULL)
{
if (res->ai_family==PF_INET)
{
info->h_addr.sa_family=res->ai_addr->sa_family;
strncpy(info->h_addr.sa_data, res->ai_addr->sa_data, 14);
break;
}
else res=res->ai_next;
}
freeaddrinfo(res);
return 0;
}
La definizione della struttura req_info e' la seguente:
struct req_info
{
char URI[URL_MAX];
char h_name[NAME_MAX];
struct sockaddr h_addr;
}; e la uso semplicemente per memorizzare i vari campi che mi interessano.
Quando invoco la funzione id_gethostbyname(), passo come argomento l'indirizzo della variabile info , nel cui campo h_name c'e' il nome dell'host di cui voglio l'indirizzo.
Ho eseguito il programma anche con il debugger (gdb), e la cosa strana e' che il processo figlio (che invoca la funzione incriminata) termina correttamente ma, subito dopo la sua terminazione, termina anche il padre e mi viene stampato questo strano messaggio:
./proxy: symbol lookup error: ./proxy: undefined symbol: close, version GLIBC_2.0 dove proxy e' il nome dell'eseguibile...
Qualche idea? Anche non strettamente correlata alla funzione, ma piu' al comportamento del programma e all'errore stampato.
Grazie,
Gica
PS: non fate troppo caso al mio inglese maccheronico...
Risolto... come al solito l'idiozia si era impadronita di me :muro:
Solo una cosa, pero'... Io gli voglio tanto bene a R. Stallman :ave: pero' gcc e' un po' stranuccio: se in un programma metto una chiamata a una funzione che vuole 3 argomenti, ma io ce ne metto 2, il compilatore mi dice piu' o meno "too few arguments in function vattelapesca"; la funzione wait() vuole un argomento, ma se nel programma scrivo wait(); il compilatore non mi dice nulla... :mbe: e io divento scemo!
Gica
PS: scemo lo sono gia'... :D
Forse la wait nell'implementazione ha un valore di deafult per l'argomento...
Orbene... a distanza di settimane non sono ancora riuscito a scrivere una funzione
breve
intelligente
brillante
corretta
elegante
per leggere una richiesta http proveniente da un browser e diretta al proxy server. Una delle versioni funzionanti, ma oscene, che ho fatto e' la seguente:
int leggi_richiesta(int sock, char *buffer, int dim)
{
char *pattern="\r\n\r\n";
char c;
int termina=0;
int i=0;
memset(buffer, 0, dim);
while (!termina)
{
if (recv(sock, &c, 1, 0)<0)
return -1;
buffer[i]=c;
++i;
if ((i>=4)&&(strncmp(buffer+i-4, pattern, 4)==0))
termina=1;
if ((!termina)&&(i>=dim-1))
return -1;
}
buffer[i]='\0';
return 0;
}
In pratica, poiche' una richiesta http termina con una doppia sequenza di CR-LF (cioe' "\r\n\r\n"), io leggo dal socket (sock) finche' non si verifica una delle seguenti condizioni:
ho ricevuto la sequenza "\r\n\r\n"
il num di caratteri letti ha raggiunto il limite
la funzione recv fallisce
Uno dei problemi che si presentano e' che, se sul socket si riceve qualcosa di diverso da una richiesta http, che magari e' lunga meno di 4 caratteri, la funzione resta in attesa indefinitamente...
Sbirciando tra i sorgenti di un server http molto semplice, ho visto che l'autore risolve il problema ponendo il socket in modalita' non bloccante, in modo che la recv ritorni sempre (anche se non ci sono dati sul socket), e facendo in modo che, se sul socket non si leggono dati per piu' di 30 secondi, la funzione di lettura termini ugualmente.
Prescindendo da questa soluzione (che non mi ispira molto), sapreste suggerirmi un modo per rendere meno oscena la mia funzione?
Grazie
Ho perfezionato la funzione precedente affiche' funzioni in tutti i casi:
int leggi_richiesta(int sock, char *buffer, int dim)
{
char *pattern="\r\n\r\n"; // Sequenza di uscita
char c;
int timeout=30;
int i=0;
time_t istante_ultimo_dato_ricevuto = time(NULL);
memset(buffer, 0, dim);
/* Pongo il socket in modalita'non bloccante */
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0)
return -1;
do
{
while (recv(sock, &c, 1, 0) < 0)
if (time(NULL) - istante_ultimo_dato_ricevuto > timeout)
return -1;
istante_ultimo_dato_ricevuto = time(NULL);
buffer[i] = c;
i++;
if ( (i>=4) && (strncmp(buffer+i-4, pattern, 4)==0) )
{
buffer[i] = '\0';
return 0;
}
} while (i < dim-1);
return -1;
}
La funzione ritorna se si verifica una delle seguenti condizioni:
e' stata ricevuta la sequenza di uscita ("\r\n\r\n")
si e' superata la capacita' del buffer (dim)
sono passati piu' di 30 secondi dall'ultimo carattere letto sul socket
Credo che la funzione sia corretta e faccia tutto quello che deve fare, ma non mi soddisfa ancora la forma. Suggerimenti?
Alla faccenda del socket non bloccante ci avevo gia' pensato; l'idea del timeout, invece, l'ho presa guardando i sorgenti di questo (http://sourceforge.net/projects/nullhttpd/) progetto.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.