#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/if.h>
#include <netdb.h>
#include <arpa/inet.h>
#include "http_download.h"
/*
* make_network_ip - make ip from host byte to network byte.
*
* host - remote host ip.
*
* successfull return the network byte,failed return 0;
*/
unsigned int make_network_ip(char *host)
{
struct hostent *h;
unsigned int ret;
if ((h = gethostbyname(host)) == NULL) {
ret = inet_addr(host);
if (ret == -1)
return 0;
return ret;
}
else {
ret = *((unsigned int *)h->h_addr);
if (ret <= 0)
return 0;
return ret;
}
}
ssize_t sock_read_timeout(int sock_id, char *buff, size_t n, int time_out)
{
fd_set readfds;
struct timeval timeout;
size_t n_left;
ssize_t n_read = 0;
int ret;
while (1) {
FD_ZERO(&readfds);
FD_SET(sock_id, &readfds);
timeout.tv_sec = time_out;
timeout.tv_usec = 0;
ret = select(sock_id + 1, &readfds, NULL, NULL, &timeout);
if (ret < 0) {
if (errno == EINTR)
continue;
return -1;
}
if (ret == 0) {
//fprintf(stdout, "%s", "select read time out ...\n");
return -1;
}
if (FD_ISSET(sock_id, &readfds)) {
if ((n_read = read(sock_id, buff, n)) < 0) {
if (errno == EINTR)
continue;
//fprintf(stdout, "%s", "select unkonw error...\n");
return -2;
}
else if (n_read == 0)
break;
else
break;
}
}
return n_read;
}
ssize_t sock_write_timeout(int sock_id, char *buff, size_t n, int time_out)
{
fd_set writefds;
struct timeval timeout;
ssize_t n_written = 0;
int ret;
while (1) {
FD_ZERO(&writefds);
FD_SET(sock_id, &writefds);
timeout.tv_sec = time_out;
timeout.tv_usec = 0;
ret = select(sock_id + 1, NULL, &writefds, NULL, &timeout);
if (ret < 0) {
if (errno == EINTR)
continue;
return -1;
}
if (ret == 0) {
//fprintf(stdout, "%s", "select write time out ...\n");
return -1;
}
if (FD_ISSET(sock_id, &writefds)) {
if ((n_written = write(sock_id, buff, n)) < 0) {
if (errno == EINTR)
continue;
//fprintf(stdout, "%s", "select unkonw error...\n");
return -2;
}
else if (n_written == 0)
break;
else
break;
}
}
return n_written;
}
int tcp_connect_timeout(unsigned int remote_ip, unsigned int remote_port, int timeout)
{
struct sockaddr_in serv_addr;
int sock_fd;
if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("[-] socket");
return -1;
}
setsockopt(sock_fd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(int));
setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(int));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = remote_port;
serv_addr.sin_addr.s_addr = remote_ip;
if (connect(sock_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1)
return 0;
return sock_fd;
}
/*
Abstract real file name from full path name, like
/pub/linux/kernel/v2.6/linux-2.6.24.1.tar.sign ---> linux-2.6.24.1.tar.sign
*/
char *abstract_file_name(char *base_url_name)
{
char *p = base_url_name;
p += strlen(base_url_name) - 1;
while (*p != '/')
p--;
p++;
return p;
}
/*
* Abstract download information from url, it can handle port like
*/
HTTP_DOWN abstract_url(char *http_url)
{
HTTP_DOWN http_down;
char temp_port[6], *p;
char *http = "http://";
int i = 0, j = 0;
int flag_port = 0;
p += strlen(http);
while (*p != '/') {
if (*p == ':') {
flag_port = 1;
p++;
while (isdigit(*p))
temp_port[j++] = *p++;
temp_port[j] = '\0';
http_down.remote_port = atoi(temp_port);
if (!CHECK_PORT(http_down.remote_port) || j >= 6) {
DPRINT("%s", "Bad url.\n");
exit(-1);
}
continue;
}
else {
http_down.remote_host[i++] = *p++;
}
}
http_down.remote_host[i] = '\0';
i = 0;
while (*p)
http_down.remote_file[i++] = *p++;
http_down.remote_file[i] = '\0';
if (!flag_port)
http_down.remote_port = DEFAULT_PORT;
return http_down;
}
/* Download a file from url, succeed return 1, failed 0. */
int http_download(char *http_url, char *save_file)
{
HTTP_DOWN file_get;
char local_file[MAX_FILE_LEN], *p;
char post_url_data[1024];
char buff[BUFF_SIZE], *content_len;
char *real_data_pos;
int sock_fd, file_fd, i;
int file_length = 0, r_len;
int http_check;
file_get = abstract_url(http_url);
if (!save_file) {
p = abstract_file_name(file_get.remote_file);
strncpy(local_file, p, strlen(p) + 1);
}
else
strncpy(local_file, save_file, strlen(save_file) + 1);
/* create local file.*/
file_fd = open(local_file, O_WRONLY | O_CREAT);
if (file_fd < 0) {
DPRINT("Create local file %s failed.\n", local_file);
return 0;
}
/* connect to http server. */
for (i = 0; i < MAX_CONNECT_NUM; i++) {
sock_fd = tcp_connect_timeout(make_network_ip(file_get.remote_host),
htons(file_get.remote_port), TIME_OUT);
if (sock_fd > 0)
break;
}
if (i == MAX_CONNECT_NUM) {
DPRINT("Connect %s:%d failed.\n",
file_get.remote_host, file_get.remote_port);
return 1;
}
/* make http get request. */
snprintf(post_url_data, sizeof(post_url_data),
"GET %s HTTP/1.1\r\n%sHost: %s\r\n%s",
file_get.remote_file, http_data_m, file_get.remote_host, http_data_e);
/* send http request.*/
sock_write_timeout(sock_fd, post_url_data, strlen(post_url_data), TIME_OUT);
/* read the first data from http server. */
r_len = sock_read_timeout(sock_fd, buff, BUFF_SIZE, TIME_OUT);
if (r_len == -1) {
DPRINT("%s", "Read data timeout.\n");
return 0;
}
if (r_len == -2) {
DPRINT("%s", "Host unknown error.\n");
return 0;
}
http_check = atoi(buff + 9);
if ((http_check != 200) && (http_check != 206))
return 0;
content_len = strstr(buff, "Content-Length: ");
if (!content_len) {
content_len = strstr(buff, "Content-length: ");
if (!content_len)
return 0;
}
file_length = atoi(content_len + 16);
/* write the first data to file. */
real_data_pos = strstr(buff, "\r\n\r\n") + 4;
write(file_fd, real_data_pos, strlen(real_data_pos));
while (1) {
r_len = sock_read_timeout(sock_fd, buff, BUFF_SIZE, TIME_OUT);
if (r_len <= 0)
break;
write(file_fd, buff, r_len);
}
close(file_fd);
close(sock_fd);
return 1;
}
int main(int argc, char **argv)
{
if (http_download(argv[1], argv[2]) == 1)
printf("done.\n");
else
printf("failed.\n");
return 0;
}
|