Robo HTTP usando socket e código multiplataforma
Publicado por João Marcos Menezes 17/02/2009
[ Hits: 12.211 ]
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; }
Funções básicas para conexão OpenSSL em C
Enviar mensagem ao usuário trabalhando com as opções do php.ini
Meu Fork do Plugin de Integração do CVS para o KDevelop
Compartilhando a tela do Computador no Celular via Deskreen
Como Configurar um Túnel SSH Reverso para Acessar Sua Máquina Local a Partir de uma Máquina Remota
Configuração para desligamento automatizado de Computadores em um Ambiente Comercial
Compartilhamento de Rede com samba em modo Público/Anônimo de forma simples, rápido e fácil
Cups: Mapear/listar todas as impressoras de outro Servidor CUPS de forma rápida e fácil
Criando uma VPC na AWS via CLI
Tem como instalar o gerenciador AMD Adrenalin no Ubuntu 24.04? (11)