Robo HTTP usando socket e código multiplataforma
Publicado por João Marcos Menezes 17/02/2009
[ Hits: 12.279 ]
Neste programa irei mostrar como criar um Robo HTTP que se conecta em um servidor web (por socket), obtém o cookie e gerencia os dados, tudo isso em C com um código que compila no gcc tanto no Linux quanto no Windows.
Testei no linux e windows nas versões 3 e 4 do gcc. Não testei em outros sistemas operacionais, mas acredito que seja fácil portar, caso o código não seja 100% compativel.
No exemplo, conectamos com o VOL e exibimos o ranking. Isto poderá lhe ser muito util.
/** * 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] == 10) { 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; }
Simulação de controle de fluxo usando sockets
Funções básicas para conexão OpenSSL em C
Instalar e Configurar o Slackware Linux em 2025
Como configurar os repositórios do apt no Debian 12 em 2025
Passkeys: A Evolução da Autenticação Digital
Instalação de distro Linux em computadores, netbooks, etc, em rede com o Clonezilla
Configurando o Conky para iniciar corretamente no sistema
3 configurações básicas que podem melhorar muito a sua edição pelo editor nano
Como colorir os logs do terminal com ccze
Instalação Microsoft Edge no Linux Mint 22
Como configurar posicionamento e movimento de janelas no Lubuntu (Openbox) com atalhos de teclado
Zorin OS apresentando linhas na tela do notebook (0)
Opção "authori... do DHCP não funciona. (1)
Nao consigo abrir meu waydroid (1)