stremer
(usa Arch Linux)
Enviado em 15/05/2008 - 12:53h
como um código fala mais que mil palavras, tenho um código aqui que implementei um robo http, ja mandei aqui no vol e ja mandei pra seção de scripts (mas não foi liberado). Ele é um robo http basico usando sockets. Com certeza da pra entender bastante coisa com ele. Sockets é até facil, bem parecido com manipulação de arquivos.
Segue abaixo o código, ele compila tanto em win qto linux:
--------------- rankvol.c -----------
/**
* rankvol.c
*
* Neste programa iremos conectar com um servidor web,
* obter o cookie e entao com este cookie obter novos
* dados do servidor.
*
* Iremos utilizar o VOL como exemplo.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#ifdef WIN32
#include <windows.h>
#include <winsock.h>
#include <conio.h>
#else
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <termios.h>
#endif
#ifdef WIN32
#define FILE_SEPARATOR ''
#define MSG_WAITALL 0
#define CHAR_ENTER 13
#else
#define FILE_SEPARATOR '/'
#define CHAR_ENTER 10
#endif
#ifndef TCP_NODELAY
#define TCP_NODELAY 1
#endif
/* Aqui iremos declarar algumas variaveis globais */
static char servidor[] = "www.vivaolinux.com.br";
static char scriptLogin[] = "/testarLogin.php";
static char scriptHome[] = "/index.php";
/* Porta HTTP do servidor */
static int portaServidor = 80;
/* Iremos armazenar aqui o login e a senha */
char login[17];
char senha[101];
/* Vamos lidar com buffer de 1Kb (1024 bytes) e mais
um byte para o armazenamento do null */
char buffer[1025];
/* Vamos utilizar buffer de 32Kb para o pacote de subida */
#define BUF_32KB 32768
#define BUF_32KB_WORK 32767
char bufEnvio[BUF_32KB];
/* Prototipos */
int abrirConexao(char * servidorConexao, int portaConexao);
char * obterHeaderHttp(char * servidorConexao, char * cookie);
void enviarDados(int socket, char * buffer);
char * receberDados(int socket);
char * obterPagina(char * script, char * dadosForm, char * cookie);
char * obterCookie();
int efetuarLogin(char * login, char * senha, char * cookie);
long obterRanking(char * cookie);
/* Esta funcao le um caractere. Veio de uma dica na net,
mas fiz algumas modificacoes :P
Para evitar o uso de ncurses esse e o jeito de usar algo tipo o getch
No windows deveremos usar o famoso conio.he o proprio getch */
int lerCarac() {
int chLido;
#ifdef WIN32
chLido = getch();
#else
struct termios oldt, newt;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~( ICANON | ECHO );
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
chLido = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
#endif
return chLido;
}
/* Entrada do programa */
main(int argc, char *argv[])
{
/* Entrada de dados */
printf("\nHTTP-Robot - Exemplo de Robo HTTP - Mostra ranking no VOL:\n\n");
printf("Entre com o login do VOL: ");
/* O fgets ja coloca um null na ultima posicao automaticamente */
/* Assim tambem evitamos buffer overflow :P */
fgets(login, 17, stdin);
/* Remover o enter, aproveitamos e colocamos um final de string */
if (login[strlen(login) - 1] == CHAR_ENTER) {
login[strlen(login)-1] = 0;
} else {
login[strlen(login)] = 0;
}
/* Agora vamos ler a senha */
printf("Entre com a senha do VOL: ");
char chLido = 0;
while (chLido != CHAR_ENTER && strlen(senha) < 101) {
chLido = lerCarac();
if (chLido > 27) {
/* Caracter lido e asterisco na tela */
putchar(42);
fflush(stdout);
senha[strlen(senha)] = chLido;
}
}
/* Forca finalizacao da string da senha */
senha[strlen(senha)] = 0;
printf("\n");
/* Precisamos obter o cookie */
char * cookie = obterCookie();
printf("Cookie de sessao obtido: [%s]\n", cookie);
/* Vamos enviar os dados do login */
if (!efetuarLogin(login, senha, cookie)) {
printf("Nao foi possivel efetuar o login.\n");
printf("Verifique se o login e senha estao corretos.\n");
exit(-1);
} else {
printf("Login efetuado com sucesso.\n");
}
/* Agora vamos obter o ranking */
long ranking = obterRanking(cookie);
if (ranking == 0) {
printf("Ranking nao localizado. \n");
printf("Verifique se o layout da home do vol foi modificado.\n");
} else {
printf("Seu ranking no VOL e: %d\n", ranking);
}
/* E nao podemos esquecer de liberar da memoria */
if (cookie != NULL) {
free(cookie);
}
/* Se chegar aqui e pq tudo deu certo */
printf("Programa finalizado com sucesso.\n");
return 0;
}
/* Rotina para obter o cookie da sessao */
char * obterCookie() {
/*
Primeiro precisamos do cookie
Vamos obte-lo chamando a index.php e pegando o Set-Cookie
*/
printf("\nAcessando home e obtendo cookie de sessao ...\n");
char * dadosHome = obterPagina(scriptHome, NULL, NULL);
char * posCookieIni = strstr(dadosHome, "Set-Cookie: ");
if (posCookieIni == NULL) {
printf("Erro ao obter o cookie. Pode ser problema de conexao.\n");
exit(-1);
}
char * posCookieFim = strstr(posCookieIni + 12, ";");
if (posCookieFim == NULL) {
printf("Erro ao obter o cookie. Pode ser problema de conexao.\n");
exit(-1);
}
char * cookie = (char *) malloc(sizeof(char) * 2048);
if (cookie == NULL) {
printf("Falta de memoria.\n");
exit(-1);
}
int tamanho = strlen(posCookieIni) - strlen(posCookieFim) - 12;
strncpy(cookie, posCookieIni + 12, tamanho);
cookie[tamanho] = 0;
/* Nao precisamos mais dos dados da home */
/* Automaticamente estaremos limpando posCookieIni e posCokieFim */
if (dadosHome != NULL) {
free(dadosHome);
}
return cookie;
}
/* Para efetuar login no VOL */
int efetuarLogin(char * login, char * senha, char * cookie) {
printf("Efetuando login no VOL ...\n");
int resLogin = 0;
/* 2Kb sao suficientes para os dados do form */
char * dadosForm = (char *) malloc(sizeof(char) * 2048);
if (dadosForm == NULL) {
printf("Falta de memoria.");
exit(-1);
}
if ((strlen(login) + strlen(senha) + 87) > BUF_32KB_WORK) {
printf("Erro de estouro de buffer.\n");
exit(-1);
}
sprintf(dadosForm, "referer=index.php&formLogin=%s&formSenha=%s",
login, senha);
/* Botoes do form */
strcat(dadosForm, "&imageField2.x=0&imageField2.y=0&Submit=Entrar\n");
char * dadosRecebidos = obterPagina(scriptLogin, dadosForm, cookie);
/* Vamos tentar identificar o login atraves de 2 informacoes:
1-O vol retorna um HTTP/1.1 302 Found para redirecionar
para a home quando o login esta ok
2-Ele retorna um Location: no header
3-Verificamos ainda o acesso negado (pagina login) */
char * tmpBufHttp = strstr(dadosRecebidos, "302 Found");
if (tmpBufHttp != NULL) {
char * tmpBufLocation = strstr(dadosRecebidos, "Location:");
if (tmpBufLocation != NULL) {
/* Se ele encontrar as 2 strings vamos considerar que houve
autenticacao com sucesso */
if ((strlen(tmpBufHttp) < strlen(dadosRecebidos))
&& (strlen(tmpBufLocation) < strlen(dadosRecebidos))) {
/* Mas antes vamos procurar por acesso restrito para verificar
se nao mudaram a regra */
char * tmpBufAcesso = strstr(dadosRecebidos, "acesso restrito");
if (tmpBufAcesso == NULL) {
resLogin = 1;
}
}
}
}
/* Vamos limpar da memoria */
if (dadosForm != NULL) {
free(dadosForm);
}
if (dadosRecebidos != NULL) {
/* Ao limpar dadosRecebidos estaremos limpando tmpBufHttp
e tmpBufLocation */
free(dadosRecebidos);
}
return resLogin;
}
/* Apos autenticado, obtem o ranking mostrado na home do VOL */
long obterRanking(char * cookie) {
/* Vamos obter a home */
printf("Obtendo ranking a partir da home ...\n");
long ranking = 0;
char * dadosHome = obterPagina(scriptHome, NULL, cookie);
char * posRank = strstr(dadosHome, "Ranking: <b>");
if (posRank != NULL) {
char * posFimRank = strstr(posRank, "</b>");
if (posFimRank != NULL) {
// Temos de tirar o ranking e o deg
int tamRank = strlen(posRank) - strlen(posFimRank) - 17;
if (tamRank > 0) {
char * tmpRank = (char *) malloc(sizeof(char) * (tamRank + 1));
if (tmpRank == NULL) {
printf("Falta de memoria.\n");
exit(-1);
}
strncpy(tmpRank, posRank + 12, tamRank);
tmpRank[tamRank] = 0;
/* Agora devemos converter para long
e depois limpar da memoria */
ranking = strtol(tmpRank, NULL, 10);
if (tmpRank != NULL) {
free(tmpRank);
}
}
}
}
/* Nao precisamos mais dos dados da home */
/* Automaticamente estaremos limpando posRank e posFimRank */
if (dadosHome != NULL) {
free(dadosHome);
}
return ranking;
}
/* Funcao para conectar no host */
int abrirConexao(char * servidorConexao, int portaConexao) {
#ifdef WIN32
WSADATA wsaData;
WORD wVersionRequested;
int err;
wVersionRequested = MAKEWORD(2, 0);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
fprintf(stderr, "Nao achou WinSock DLL.\n");
exit(-1);
}
#endif
int meuSocket;
struct sockaddr_in sockAddr;
struct hostent *hEnt;
/* Vamos obter os dados do host */
hEnt = gethostbyname(servidorConexao);
if (hEnt == NULL) {
printf("Erro ao obter os dados do host.\n");
exit(-1);
}
/* Agora vamos obter o socket */
meuSocket = socket(AF_INET, SOCK_STREAM, getprotobyname("tcp")->p_proto);
if (meuSocket == -1) {
printf("Erro ao obter socket tcp.\n");
exit(-1);
}
/* Vamos definir o TCP_NODELAY (usado para comunicacoes de ida e
volta para melhorar a performance) */
int flagTcpNoDelay = 1;
int resTcpNoDelay = setsockopt(meuSocket, IPPROTO_TCP, TCP_NODELAY,
(char *) &flagTcpNoDelay, sizeof(int));
if (resTcpNoDelay < 0) {
printf("Erro ao setar tcp_nodelay.\n");
exit(-1);
}
/* Vamos definir tambem o timeout do socket
(Voce pode precisar mudar em redes lentas) */
struct timeval tv;
int timeouts = 0;
tv.tv_sec = 3;
tv.tv_usec = 0;
if (setsockopt(meuSocket, SOL_SOCKET, SO_RCVTIMEO,
(char *) &tv, sizeof tv)) {
printf("Erro ao definir timeout.");
exit(-1);
}
/* Entao conectamos */
memcpy(&sockAddr.sin_addr, hEnt->h_addr, hEnt->h_length);
sockAddr.sin_family = AF_INET;
sockAddr.sin_port = htons(portaConexao);
if (connect(meuSocket, (struct sockaddr *) &sockAddr,
sizeof(sockAddr)) < 0) {
printf("Erro ao conectar no servidor.\n");
exit(-1);
}
/* E voltamos o socket conectado */
return meuSocket;
}
/* Esta funcao obtem um header http */
char * obterHeaderHttp(char * servidorConexao, char * cookie) {
/* Vamos usar um buffer grande (2kb nao tem perigo de estourar) */
char * headerHttp = (char *) malloc(sizeof(char) * 2048);
if (headerHttp == NULL) {
printf("Falta de memoria.\n");
exit(-1);
}
strcpy(headerHttp, "Host: ");
strcat(headerHttp, servidorConexao);
strcat(headerHttp, "\n");
strcat(headerHttp, "User-Agent: Mozilla/5.0 ");
strcat(headerHttp, "X11; U; Linux i686; en-US; rv:1.8.1.14)\n");
strcat(headerHttp, "Accept: text/xml,text/html;q=0.9,text/plain;");
strcat(headerHttp, "q=0.8,image/png,*/*;q=0.5\n");
strcat(headerHttp, "Accept-Language: en-us,en;q=0.5\n");
strcat(headerHttp, "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\n");
strcat(headerHttp, "Keep-Alive: 300\n");
if (cookie != NULL) {
strcat(headerHttp, "Cookie: ");
strcat(headerHttp, cookie);
strcat(headerHttp, ";\n");
}
strcat(headerHttp, "Connection: keep-alive\n");
headerHttp[strlen(headerHttp)] = 0;
return headerHttp;
}
/* Funcao para enviar dados via socket */
void enviarDados(int socket, char * buffer) {
int envio = send(socket, buffer, strlen(buffer), 0);
if (envio < 1) {
printf("Erro no envio dos dados: [%d].\n", envio);
exit(-1);
}
}
/* Funcao para receber dados via socket.
Vamos trabalhar com alocacao dinamica.
Esta funcao retorna um ponteiro para os dados recebidos
*/
char * receberDados(int socket) {
char * dados = (char *) malloc(sizeof(char) * 1025);
if (dados == NULL) {
printf("Falta de memoria.\n");
exit(-1);
}
int contBuf = 0;
while ((contBuf = recv(socket, buffer, 1024, 0)) > 0) {
/* Para mostrar que esta recebendo alguma coisa */
putchar(42);
fflush(stdout);
/* Adiciona ao buffer se possivel */
buffer[contBuf] = 0;
if (dados == NULL) {
strcpy(dados, buffer);
} else {
/* Realocamos mais 1kb */
dados = realloc(dados, strlen(dados) + 1025);
if (dados == NULL) {
printf("Erro na realocacao dinamica.\n");
exit(-1);
}
strcat(dados, buffer);
}
/* Limpar buffer */
int n = 0;
for(n = 0; n <= 1024; n++) {
buffer[n] = 0;
}
}
dados[strlen(dados)] = 0;
return dados;
}
/* Obtem os dados de uma pagina html */
char * obterPagina(char * script, char * dadosForm, char * cookie) {
int meuSocket;
printf("Conectando: (%s) ... \n", servidor);
meuSocket = abrirConexao(servidor, portaServidor);
if (meuSocket == -1) {
printf("Erro ao conectar no servidor.\n");
exit(-1);
}
/* Vamos obter o header */
char * headerHttp = obterHeaderHttp(servidor, cookie);
/* Vamos gerar o post de envio */
if (dadosForm == NULL) {
sprintf(bufEnvio, "GET %s HTTP/1.1\n", script);
} else {
sprintf(bufEnvio, "POST %s HTTP/1.1\n", script);
}
if ((strlen(bufEnvio) + strlen(headerHttp)) > BUF_32KB_WORK) {
printf("Erro de estouro de buffer.\n");
exit(-1);
}
strcat(bufEnvio, headerHttp);
/* Vamos limpar da memoria */
if (headerHttp != NULL) {
free(headerHttp);
}
/* Temos de adicionar algumas informacoes para mandar o form */
if (dadosForm != NULL) {
if ((strlen(bufEnvio) + 64) > BUF_32KB_WORK) {
printf("Erro de estouro de buffer.\n");
exit(-1);
}
strcat(bufEnvio, "Content-Type: application/x-www-form-urlencoded\n");
strcat(bufEnvio, "Content-Length: ");
char * tmpBuf = (char *) malloc(sizeof(char) * 1024);
if (tmpBuf == NULL) {
printf("Falta de memoria.\n");
exit(-1);
}
sprintf(tmpBuf, "%d", strlen(dadosForm));
if ((strlen(bufEnvio) + strlen(tmpBuf) + 2) > BUF_32KB_WORK) {
printf("Erro de estouro de buffer.\n");
exit(-1);
}
strcat(bufEnvio, tmpBuf);
strcat(bufEnvio, "\n");
if (tmpBuf != NULL) {
free(tmpBuf);
}
}
strcat(bufEnvio, "\n");
/* Armazena os dados do form */
if (dadosForm != NULL) {
if ((strlen(bufEnvio) + strlen(dadosForm)) > BUF_32KB_WORK) {
printf("Erro de estouro de buffer.\n");
exit(-1);
}
strcat(bufEnvio, dadosForm);
}
/* Envia os dados */
enviarDados(meuSocket, bufEnvio);
printf("Dados foram enviados, recebendo dados ...\n");
char * dadosRecebidos = receberDados(meuSocket);
#ifdef WIN32
closesocket(meuSocket);
WSACleanup();
#else
close(meuSocket);
#endif
printf("\n");
return dadosRecebidos;
}