824 regels
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			824 regels
		
	
	
		
			21 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /**
 | |
|  * @PROJECT			WebGet Downloader
 | |
|  * @COPYRIGHT		See COPYING in the top level directory
 | |
|  * @FILE			libweb.c
 | |
|  * @PURPOSE			Common Web Library
 | |
|  * @DEVELOPERS		Al Globus <globus@nas.nasa.gov>
 | |
|  *					Rafal Kupiec <belliash@asiotec.eu.org>
 | |
|  *					Jef Poskanzer <jef@mail.acme.com>
 | |
|  */
 | |
| 
 | |
| #include <unistd.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <ctype.h>
 | |
| #include <signal.h>
 | |
| #include <fcntl.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <sys/socket.h>
 | |
| #include <netinet/in.h>
 | |
| #include <netdb.h>
 | |
| 
 | |
| #include <cyassl/openssl/ssl.h>
 | |
| #include <cyassl/openssl/err.h>
 | |
| #include <cyassl/error.h> 
 | |
| 
 | |
| #ifdef CROSS_BUILD
 | |
| 	#include <cyassl/cyassl_error.h>
 | |
| #endif
 | |
| 
 | |
| #include "libweb.h"
 | |
| 
 | |
| int b64_encode(unsigned char* ptr, int len, char* space, int size) {
 | |
| 	int ptr_idx, space_idx, phase;
 | |
| 	char c;
 | |
| 
 | |
| 	space_idx = 0;
 | |
| 	phase = 0;
 | |
| 	for(ptr_idx = 0; ptr_idx < len; ++ptr_idx) {
 | |
| 		switch(phase) {
 | |
| 			case 0:
 | |
| 				c = b64_encode_table[ptr[ptr_idx] >> 2];
 | |
| 				if(space_idx < size) {
 | |
| 					space[space_idx++] = c;
 | |
| 				}
 | |
| 				c = b64_encode_table[(ptr[ptr_idx] & 0x3) << 4];
 | |
| 				if(space_idx < size) {
 | |
| 					space[space_idx++] = c;
 | |
| 				}
 | |
| 				++phase;
 | |
| 				break;
 | |
| 			case 1:
 | |
| 				space[space_idx - 1] = b64_encode_table[b64_decode_table[(int) ((unsigned char) space[space_idx - 1])] | (ptr[ptr_idx] >> 4)];
 | |
| 				c = b64_encode_table[(ptr[ptr_idx] & 0xf) << 2];
 | |
| 				if(space_idx < size) {
 | |
| 					space[space_idx++] = c;
 | |
| 				}
 | |
| 				++phase;
 | |
| 				break;
 | |
| 			case 2:
 | |
| 				space[space_idx - 1] = b64_encode_table[b64_decode_table[(int) ((unsigned char) space[space_idx - 1])] | (ptr[ptr_idx] >> 6)];
 | |
| 				c = b64_encode_table[ptr[ptr_idx] & 0x3f];
 | |
| 				if(space_idx < size) {
 | |
| 					space[space_idx++] = c;
 | |
| 				}
 | |
| 				phase = 0;
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 	while(phase++ < 3) {
 | |
| 		if(space_idx < size) {
 | |
| 			space[space_idx++] = '=';
 | |
| 		}
 | |
| 	}
 | |
| 	return space_idx;
 | |
| }
 | |
| 
 | |
| void check(void* ptr) {
 | |
| 	if(ptr == (void*) 0) {
 | |
| 		(void) fprintf(stderr, "%s: out of memory\n", argv0);
 | |
| 		exit(1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| off_t file_bytes(const char* filename) {
 | |
| 	struct stat sb;
 | |
| 
 | |
| 	if(stat(filename, &sb) < 0) {
 | |
| 		perror(filename);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	return sb.st_size;
 | |
| }
 | |
| 
 | |
| int file_copy(const char* filename, char* buf) {
 | |
| 	int fd;
 | |
| 	struct stat sb;
 | |
| 	off_t bytes;
 | |
| 
 | |
| 	fd = open(filename, O_RDONLY);
 | |
| 	if(fd == -1) {
 | |
| 		perror(filename);
 | |
| 		exit(-1);
 | |
| 	}
 | |
| 	if(fstat(fd, &sb) != 0) {
 | |
| 		perror(filename);
 | |
| 		exit(-1);
 | |
| 	}
 | |
| 	bytes = sb.st_size;
 | |
| 	if(read(fd, buf, bytes) != bytes) {
 | |
| 		perror(filename);
 | |
| 		exit(-1);
 | |
| 	}
 | |
| 	(void) close(fd);
 | |
| 	return bytes;
 | |
| }
 | |
| 
 | |
| int getURL(char* url, char* referer, char* user_agent, char* auth_token, int ncookies, char** cookies, char* header_name, char* header_value) {
 | |
| 	char* s;
 | |
| 	int protocol;
 | |
| 	char host[2000];
 | |
| 	int host_len;
 | |
| 	unsigned short port;
 | |
| 	char* file = (char*) 0;
 | |
| 	char* http = "http://";
 | |
| 	int http_len = strlen( http );
 | |
| 	char* https = "https://";
 | |
| 	int https_len = strlen( https );
 | |
| 	int proto_len;
 | |
| 
 | |
| 	if(url == (char*) 0) {
 | |
| 		(void) fprintf(stderr, "%s: null URL\n", argv0);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	if(strncmp(http, url, http_len) == 0) {
 | |
| 		proto_len = http_len;
 | |
| 		protocol = PROTO_HTTP;
 | |
| 	} else if(strncmp(https, url, https_len) == 0) {
 | |
| 		proto_len = https_len;
 | |
| 		protocol = PROTO_HTTPS;
 | |
| 	} else {
 | |
| 		(void) fprintf(stderr, "%s: non-http URL\n", argv0);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	for(s = url + proto_len; *s != '\0' && *s != ':' && *s != '/'; ++s);
 | |
| 	host_len = s - url;
 | |
| 	host_len -= proto_len;
 | |
| 	strncpy(host, url + proto_len, host_len);
 | |
| 	host[host_len] = '\0';
 | |
| 	if(*s == ':') {
 | |
| 		port = (unsigned short) atoi(++s);
 | |
| 		while(*s != '\0' && *s != '/')
 | |
| 		++s;
 | |
| 	} else {
 | |
| 		if(protocol == PROTO_HTTPS) {
 | |
| 			port = 443;
 | |
| 		} else {
 | |
| 			port = 80;
 | |
| 		}
 | |
| 	}
 | |
| 	if(*s == '\0') {
 | |
| 		file = "/";
 | |
| 	} else {
 | |
| 		file = s;
 | |
| 	}
 | |
| 	return getURLbyParts(protocol, host, port, file, referer, user_agent, auth_token, ncookies, cookies, header_name, header_value);
 | |
| }
 | |
| 
 | |
| int getURLbyParts(int protocol, char* host, unsigned short port, char* file, char* referer, char* user_agent, char* auth_token, int ncookies, char** cookies, char* header_name, char* header_value) {
 | |
| 	int sockfd;
 | |
| 	SSL_CTX* ssl_ctx = (SSL_CTX*) 0;
 | |
| 	SSL* ssl = (SSL*) 0;
 | |
| 	char buf[20000];
 | |
| 	int i, bytes, b, header_state, status;
 | |
| 
 | |
| 	(void) alarm(timeout);
 | |
| 	sockfd = open_client_socket(host, port);
 | |
| 	if(protocol == PROTO_HTTPS) {
 | |
| 		int r;
 | |
| 		ssl_ctx = SSL_CTX_new(TLSv1_client_method());
 | |
| 		SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, 0); 
 | |
| 		ssl = SSL_new(ssl_ctx);
 | |
| 		SSL_set_fd(ssl, sockfd);
 | |
| 		r = SSL_connect(ssl);
 | |
| 		if(r <= 0) {
 | |
| 			(void) fprintf(stderr, "%s: %s - SSL connection failed - %d\n", argv0, url, r);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 	}
 | |
| 	(void) alarm(timeout);
 | |
| 	bytes = snprintf(buf, sizeof(buf), "GET %s HTTP/1.0\r\n", file);
 | |
| 	bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Host: %s\r\n", host);
 | |
| 	if(referer != (char*) 0) {
 | |
| 		bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Referer: %s\r\n", referer);
 | |
| 	}
 | |
| 	bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "User-Agent: %s\r\n", user_agent);
 | |
| 	bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Accept: */*\r\n");
 | |
| 	bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Accept-Language: en\r\n");
 | |
| 	bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Accept-Charset: iso-8859-1,*,utf-8\r\n");
 | |
| 	if(auth_token != (char*) 0) {
 | |
| 		char token_buf[1000];
 | |
| 		token_buf[b64_encode((unsigned char*) auth_token, strlen( auth_token ), token_buf, sizeof(token_buf))] = '\0';
 | |
| 		bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Authorization: Basic %s\r\n", token_buf);
 | |
| 	}
 | |
| 	for(i = 0; i < ncookies; ++i) {
 | |
| 		bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "Cookie: %s\r\n", cookies[i]);
 | |
| 	}
 | |
| 	if(header_name != (char*) 0) {
 | |
| 		bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "%s: %s\r\n", header_name, header_value);
 | |
| 	}
 | |
| 	bytes += snprintf(&buf[bytes], sizeof(buf) - bytes, "\r\n");
 | |
| 	if (protocol == PROTO_HTTPS)
 | |
| 		(void) SSL_write(ssl, buf, bytes);
 | |
| 	else
 | |
| 		(void) write(sockfd, buf, bytes);
 | |
| 	(void) alarm(timeout);
 | |
| 	header_state = HDST_LINE1_PROTOCOL;
 | |
| 	status = 0;
 | |
| 	for(;;) {
 | |
| 		if(protocol == PROTO_HTTPS) {
 | |
| 			bytes = SSL_read(ssl, buf, sizeof(buf));
 | |
| 		} else {
 | |
| 			bytes = read(sockfd, buf, sizeof(buf));
 | |
| 		}
 | |
| 		if(bytes <= 0) {
 | |
| 			break;
 | |
| 		}
 | |
| 		for(b = 0; b < bytes; ++b) {
 | |
| 			if(verbose) {
 | |
| 				(void) write(1, &buf[b], 1);
 | |
| 			}
 | |
| 			switch(header_state) {
 | |
| 				case HDST_LINE1_PROTOCOL:
 | |
| 					switch(buf[b]) {
 | |
| 						case ' ': case '\t':
 | |
| 							header_state = HDST_LINE1_WHITESPACE;
 | |
| 							break;
 | |
| 						case '\n':
 | |
| 							header_state = HDST_LF;
 | |
| 							break;
 | |
| 						case '\r':
 | |
| 							header_state = HDST_CR;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_LINE1_WHITESPACE:
 | |
| 					switch(buf[b]) {
 | |
| 						case '0':
 | |
| 						case '1':
 | |
| 						case '2':
 | |
| 						case '3':
 | |
| 						case '4':
 | |
| 						case '5':
 | |
| 						case '6':
 | |
| 						case '7':
 | |
| 						case '8':
 | |
| 						case '9':
 | |
| 							status = buf[b] - '0';
 | |
| 							header_state = HDST_LINE1_STATUS;
 | |
| 							break;
 | |
| 						case '\n':
 | |
| 							header_state = HDST_LF;
 | |
| 							break;
 | |
| 						case '\r':
 | |
| 							header_state = HDST_CR;
 | |
| 							break;
 | |
| 						default:
 | |
| 							header_state = HDST_TEXT;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_LINE1_STATUS:
 | |
| 					switch(buf[b]) {
 | |
| 						case '0':
 | |
| 						case '1':
 | |
| 						case '2':
 | |
| 						case '3':
 | |
| 						case '4':
 | |
| 						case '5':
 | |
| 						case '6':
 | |
| 						case '7':
 | |
| 						case '8':
 | |
| 						case '9':
 | |
| 							status = status * 10 + buf[b] - '0';
 | |
| 							break;
 | |
| 						case '\n':
 | |
| 							header_state = HDST_LF;
 | |
| 							break;
 | |
| 						case '\r':
 | |
| 							header_state = HDST_CR;
 | |
| 							break;
 | |
| 						default:
 | |
| 							header_state = HDST_TEXT;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_BOL:
 | |
| 					switch(buf[b]) {
 | |
| 						case '\n':
 | |
| 							header_state = HDST_LF;
 | |
| 							break;
 | |
| 						case '\r':
 | |
| 							header_state = HDST_CR;
 | |
| 							break;
 | |
| 						default:
 | |
| 							header_state = HDST_TEXT;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_TEXT:
 | |
| 					switch(buf[b]) {
 | |
| 						case '\n':
 | |
| 							header_state = HDST_LF;
 | |
| 							break;
 | |
| 						case '\r':
 | |
| 							header_state = HDST_CR;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_LF:
 | |
| 					switch(buf[b]) {
 | |
| 						case '\n':
 | |
| 							goto end_of_headers;
 | |
| 						case '\r':
 | |
| 							header_state = HDST_CR;
 | |
| 							break;
 | |
| 						default:
 | |
| 							header_state = HDST_TEXT;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_CR:
 | |
| 					switch(buf[b]) {
 | |
| 						case '\n':
 | |
| 							header_state = HDST_CRLF;
 | |
| 							break;
 | |
| 						case '\r':
 | |
| 							goto end_of_headers;
 | |
| 						default:
 | |
| 							header_state = HDST_TEXT;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_CRLF:
 | |
| 					switch(buf[b]) {
 | |
| 						case '\n':
 | |
| 							goto end_of_headers;
 | |
| 						case '\r':
 | |
| 							header_state = HDST_CRLFCR;
 | |
| 							break;
 | |
| 						default:
 | |
| 							header_state = HDST_TEXT;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_CRLFCR:
 | |
| 					switch(buf[b]) {
 | |
| 						case '\n':
 | |
| 						case '\r':
 | |
| 							goto end_of_headers;
 | |
| 						default:
 | |
| 							header_state = HDST_TEXT;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| end_of_headers:
 | |
| 	if(bytes > 0) {
 | |
| 		++b;
 | |
| 		(void) write(1, &buf[b], bytes - b);
 | |
| 	}
 | |
| 	for(;;) {
 | |
| 		(void) alarm(timeout);
 | |
| 		if(protocol == PROTO_HTTPS) {
 | |
| 			bytes = SSL_read(ssl, buf, sizeof(buf));
 | |
| 		} else {
 | |
| 			bytes = read(sockfd, buf, sizeof(buf));
 | |
| 		}
 | |
| 		if(bytes == 0) {
 | |
| 			break;
 | |
| 		}
 | |
| 		if(bytes < 0) {
 | |
| 			show_error("read");
 | |
| 		}
 | |
| 		(void) write(1, buf, bytes);
 | |
| 	}
 | |
| 	if(protocol == PROTO_HTTPS) {
 | |
| 		SSL_free(ssl);
 | |
| 		SSL_CTX_free(ssl_ctx);
 | |
| 	}
 | |
| 	(void) close(sockfd);
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| void* malloc_check(size_t size) {
 | |
| 	void* ptr = malloc(size);
 | |
| 	check(ptr);
 | |
| 	return ptr;
 | |
| }
 | |
| 
 | |
| int open_client_socket(char* hostname, unsigned short port) {
 | |
| 	struct addrinfo hints;
 | |
| 	char portstr[10];
 | |
| 	int gaierr;
 | |
| 	struct addrinfo* ai;
 | |
| 	struct addrinfo* ai2;
 | |
| 	struct addrinfo* aiv4;
 | |
| 	struct addrinfo* aiv6;
 | |
| 	struct sockaddr_in6 sa;
 | |
| 	int sa_len, sock_family, sock_type, sock_protocol;
 | |
| 	int sockfd;
 | |
| 
 | |
| 	(void) memset((void*) &sa, 0, sizeof(sa));
 | |
| 	(void) memset(&hints, 0, sizeof(hints));
 | |
| 	hints.ai_family = PF_UNSPEC;
 | |
| 	hints.ai_socktype = SOCK_STREAM;
 | |
| 	(void) snprintf(portstr, sizeof(portstr), "%d", (int) port);
 | |
| 	if((gaierr = getaddrinfo(hostname, portstr, &hints, &ai)) != 0) {
 | |
| 		(void) fprintf(stderr, "%s: getaddrinfo %s - %s\n", argv0, hostname, gai_strerror(gaierr));
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	aiv4 = (struct addrinfo*) 0;
 | |
| 	aiv6 = (struct addrinfo*) 0;
 | |
| 	for(ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next) {
 | |
| 		switch(ai2->ai_family) {
 | |
| 			case AF_INET: 
 | |
| 				if(aiv4 == (struct addrinfo*) 0) {
 | |
| 					aiv4 = ai2;
 | |
| 				}
 | |
| 				break;
 | |
| 			case AF_INET6:
 | |
| 				if(aiv6 == (struct addrinfo*) 0) {
 | |
| 					aiv6 = ai2;
 | |
| 				}
 | |
| 				break;
 | |
| 		}
 | |
| 	}
 | |
| 	if(aiv4 != (struct addrinfo*) 0) {
 | |
| 		if(sizeof(sa) < aiv4->ai_addrlen) {
 | |
| 			(void) fprintf(stderr, "%s - sockaddr too small (%lu < %lu)\n", hostname, (unsigned long) sizeof(sa), (unsigned long) aiv4->ai_addrlen);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		sock_family = aiv4->ai_family;
 | |
| 		sock_type = aiv4->ai_socktype;
 | |
| 		sock_protocol = aiv4->ai_protocol;
 | |
| 		sa_len = aiv4->ai_addrlen;
 | |
| 		(void) memmove(&sa, aiv4->ai_addr, sa_len);
 | |
| 		goto ok;
 | |
| 	}
 | |
| 	if(aiv6 != (struct addrinfo*) 0) {
 | |
| 		if(sizeof(sa) < aiv6->ai_addrlen) {
 | |
| 			(void) fprintf(stderr, "%s - sockaddr too small (%lu < %lu)\n", hostname, (unsigned long) sizeof(sa), (unsigned long) aiv6->ai_addrlen);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		sock_family = aiv6->ai_family;
 | |
| 		sock_type = aiv6->ai_socktype;
 | |
| 		sock_protocol = aiv6->ai_protocol;
 | |
| 		sa_len = aiv6->ai_addrlen;
 | |
| 		(void) memmove(&sa, aiv6->ai_addr, sa_len);
 | |
| 		goto ok;
 | |
| 	}
 | |
| 	(void) fprintf(stderr, "%s: no valid address found for host %s\n", argv0, hostname);
 | |
| 	exit(1);
 | |
| ok:
 | |
| 	freeaddrinfo(ai);
 | |
| 	sockfd = socket(sock_family, sock_type, sock_protocol);
 | |
| 	if(sockfd < 0) {
 | |
| 		show_error("socket");
 | |
| 	}
 | |
| 	if(connect(sockfd, (struct sockaddr*) &sa, sa_len) < 0) {
 | |
| 		show_error("connect");
 | |
| 	}
 | |
| 	return sockfd;
 | |
| }
 | |
| 
 | |
| void postURL(char* url, char* referer, char* user_agent, char* auth_token, int ncookies, char** cookies, char* header_name, char* header_value, char** args, int argc) {
 | |
| 	char* s;
 | |
| 	int protocol;
 | |
| 	char host[2000];
 | |
| 	int host_len;
 | |
| 	unsigned short port;
 | |
| 	char* file = 0;
 | |
| 	char* http = "http://";
 | |
| 	int http_len = strlen(http);
 | |
| 	char* https = "https://";
 | |
| 	int https_len = strlen(https);
 | |
| 	int proto_len;
 | |
| 
 | |
| 	if(url == (char*) 0) {
 | |
| 		(void) fprintf(stderr, "%s: null URL\n", argv0);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	if(strncmp(http, url, http_len) == 0) {
 | |
| 		proto_len = http_len;
 | |
| 		protocol = PROTO_HTTP;
 | |
| 	} else if(strncmp(https, url, https_len) == 0) {
 | |
| 		proto_len = https_len;
 | |
| 		protocol = PROTO_HTTPS;
 | |
| 	} else {
 | |
| 		(void) fprintf(stderr, "%s: non-http URL\n", argv0);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	for(s = url + proto_len; *s != '\0' && *s != ':' && *s != '/'; ++s);
 | |
| 	host_len = s - url;
 | |
| 	host_len -= proto_len;
 | |
| 	strncpy(host, url + proto_len, host_len);
 | |
| 	host[host_len] = '\0';
 | |
| 	if(*s == ':') {
 | |
| 		port = (unsigned short) atoi(++s);
 | |
| 		while(*s != '\0' && *s != '/') {
 | |
| 			++s;
 | |
| 		}
 | |
| 	} else {
 | |
| 		if(protocol == PROTO_HTTPS) {
 | |
| 			port = 443;
 | |
| 		} else {
 | |
| 			port = 80;
 | |
| 		}
 | |
| 	}
 | |
| 	if(*s == '\0') {
 | |
| 		file = "/";
 | |
| 	} else {
 | |
| 		file = s;
 | |
| 	}
 | |
| 	postURLbyParts(protocol, host, port, file, referer, user_agent, auth_token, ncookies, cookies, header_name, header_value, args, argc);
 | |
| }
 | |
| 
 | |
| void postURLbyParts(int protocol, char* host, unsigned short port, char* file, char* referer, char* user_agent, char* auth_token, int ncookies, char** cookies, char* header_name, char* header_value, char** args, int argc) {
 | |
| 	int sockfd;
 | |
| 	SSL_CTX* ssl_ctx = (SSL_CTX*) 0;
 | |
| 	SSL* ssl = (SSL*) 0;
 | |
| 	char head_buf[20000];
 | |
| 	int max_arg, total_bytes;
 | |
| 	int multipart, next_arg_is_file;
 | |
| 	static const char* const sep = "http_post-content-separator";
 | |
| 	char* data_buf;
 | |
| 	char* enc_buf;
 | |
| 	int head_bytes, data_bytes, i, header_state;
 | |
| 	char* eq;
 | |
| 
 | |
| 	(void) alarm(timeout);
 | |
| 	sockfd = open_client_socket(host, port);
 | |
| 	if(protocol == PROTO_HTTPS) {
 | |
| 		int r;
 | |
| 		ssl_ctx = SSL_CTX_new(TLSv1_client_method());
 | |
| 		SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, 0); 
 | |
| 		ssl = SSL_new(ssl_ctx);
 | |
| 		SSL_set_fd(ssl, sockfd);
 | |
| 		r = SSL_connect(ssl);
 | |
| 		if(r <= 0) {
 | |
| 			(void) fprintf(stderr, "%s: %s - SSL connection failed - %d\n", argv0, url, r);
 | |
| 			exit(1);
 | |
| 		}
 | |
| 	}
 | |
| 	multipart = 0;
 | |
| 	total_bytes = max_arg = 0;
 | |
| 	next_arg_is_file = 0;
 | |
| 	for(i = 0; i < argc ; ++i) {
 | |
| 		int l = strlen(args[i]);
 | |
| 		if(strcmp(args[i], "-f") == 0) {
 | |
| 			multipart = 1;
 | |
| 			next_arg_is_file = 1;
 | |
| 			continue;
 | |
| 		}
 | |
| 		total_bytes += l;
 | |
| 		if(l > max_arg) {
 | |
| 			max_arg = l;
 | |
| 		}
 | |
| 		if(next_arg_is_file) {
 | |
| 			eq = strchr(args[i], '=');
 | |
| 			if(eq == (char*) 0) {
 | |
| 				(void) fprintf(stderr, "%s: missing filename\n", argv0);
 | |
| 				exit(1);
 | |
| 			} else {
 | |
| 				++eq;
 | |
| 				total_bytes += file_bytes(eq);
 | |
| 			}
 | |
| 			next_arg_is_file = 0;
 | |
| 		}
 | |
| 	}
 | |
| 	if(multipart) {
 | |
| 		for(i = 0; i < argc ; ++i) {
 | |
| 			total_bytes += strlen(sep) * 2 + 100;
 | |
| 		}
 | |
| 		total_bytes += strlen(sep) * 2;
 | |
| 	} else {
 | |
| 		total_bytes *= 4;
 | |
| 		enc_buf = (char*) malloc_check(max_arg * 4);
 | |
| 	}
 | |
| 	data_buf = (char*) malloc_check(total_bytes);
 | |
| 	if(multipart) {
 | |
| 		next_arg_is_file = 0;
 | |
| 		data_bytes = 0;
 | |
| 		for(i = 0; i < argc; ++i) {
 | |
| 			if(strcmp(args[i], "-f") == 0) {
 | |
| 				next_arg_is_file = 1;
 | |
| 				continue;
 | |
| 			}
 | |
| 			eq = strchr(args[i], '=');
 | |
| 			if(eq == (char*) 0) {
 | |
| 				data_bytes += sprintf(&data_buf[data_bytes], "--%s\r\nContent-Disposition: form-data\r\n\r\n%s\r\n", sep, args[i]);
 | |
| 			} else {
 | |
| 				*eq++ = '\0';
 | |
| 				if(next_arg_is_file) {
 | |
| 					data_bytes += sprintf(&data_buf[data_bytes], "--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n\r\n", sep, args[i], eq);
 | |
| 					data_bytes += file_copy(eq, &data_buf[data_bytes]);
 | |
| 					data_bytes += sprintf(&data_buf[data_bytes], "\r\n");
 | |
| 					next_arg_is_file = 0;
 | |
| 				} else {
 | |
| 					data_bytes += sprintf(&data_buf[data_bytes], "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n", sep, args[i], eq);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		data_bytes += sprintf(&data_buf[data_bytes], "--%s--\r\n", sep);
 | |
| 	} else {
 | |
| 		data_bytes = 0;
 | |
| 		for(i = 0; i < argc ; ++i) {
 | |
| 			if(data_bytes > 0) {
 | |
| 				data_bytes += sprintf(&data_buf[data_bytes], "&");
 | |
| 			}
 | |
| 			eq = strchr(args[i], '=');
 | |
| 			if(eq == (char*) 0) {
 | |
| 				strencode(enc_buf, args[i]);
 | |
| 				data_bytes += sprintf(&data_buf[data_bytes], "%s", enc_buf);
 | |
| 			} else {
 | |
| 				*eq++ = '\0';
 | |
| 				strencode(enc_buf, args[i]);
 | |
| 				data_bytes += sprintf(&data_buf[data_bytes], "%s=", enc_buf);
 | |
| 				strencode(enc_buf, eq);
 | |
| 				data_bytes += sprintf(&data_buf[data_bytes], "%s", enc_buf);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	(void) alarm(timeout);
 | |
| 	head_bytes = snprintf(head_buf, sizeof(head_buf), "POST %s HTTP/1.0\r\n", file);
 | |
| 	head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "Host: %s\r\n", host);
 | |
| 	head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "Content-Length: %d\r\n", data_bytes);
 | |
| 	if(referer != (char*) 0) {
 | |
| 		head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "Referer: %s\r\n", referer);
 | |
| 	}
 | |
| 	head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "User-Agent: %s\r\n", user_agent);
 | |
| 	if(multipart) {
 | |
| 		head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "Content-type: multipart/form-data; boundary=\"%s\"\r\n", sep);
 | |
| 	} else {
 | |
| 		head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "Content-type: application/x-www-form-urlencoded\r\n");
 | |
| 	}
 | |
| 	head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "Accept: */*\r\n");
 | |
| 	head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "Accept-Language: en\r\n");
 | |
| 	head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "Accept-Charset: iso-8859-1,*,utf-8\r\n");
 | |
| 	if(auth_token != (char*) 0) {
 | |
| 		char token_buf[1000];
 | |
| 		token_buf[b64_encode((unsigned char*) auth_token, strlen(auth_token), token_buf, sizeof(token_buf))] = '\0';
 | |
| 		head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "Authorization: Basic %s\r\n", token_buf);
 | |
| 	}
 | |
| 	for(i = 0; i < ncookies; ++i) {
 | |
| 		head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "Cookie: %s\r\n", cookies[i]);
 | |
| 	}
 | |
| 	if(header_name != (char*) 0) {
 | |
| 		head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "%s: %s\r\n", header_name, header_value);
 | |
| 	}
 | |
| 	head_bytes += snprintf(&head_buf[head_bytes], sizeof(head_buf) - head_bytes, "\r\n");
 | |
| 	if(protocol == PROTO_HTTPS) {
 | |
| 		(void) SSL_write(ssl, head_buf, head_bytes);
 | |
| 	} else {
 | |
| 		(void) write(sockfd, head_buf, head_bytes);
 | |
| 	}
 | |
| 	if(protocol == PROTO_HTTPS) {
 | |
| 		(void) SSL_write(ssl, data_buf, data_bytes);
 | |
| 	} else {
 | |
| 		(void) write(sockfd, data_buf, data_bytes);
 | |
| 	}
 | |
| 	(void) alarm(timeout);
 | |
| 	header_state = HDST_BOL;
 | |
| 	for(;;) {
 | |
| 		if(protocol == PROTO_HTTPS) {
 | |
| 			head_bytes = SSL_read(ssl, head_buf, sizeof(head_buf));
 | |
| 		} else {
 | |
| 			head_bytes = read(sockfd, head_buf, sizeof(head_buf));
 | |
| 		}
 | |
| 		if(head_bytes <= 0) {
 | |
| 			break;
 | |
| 		}
 | |
| 		for(i = 0; i < head_bytes; ++i) {
 | |
| 			if(verbose) {
 | |
| 				(void) write(1, &head_buf[i], 1);
 | |
| 			}
 | |
| 			switch(header_state) {
 | |
| 				case HDST_BOL:
 | |
| 					switch(head_buf[i]) {
 | |
| 						case '\n':
 | |
| 							header_state = HDST_LF;
 | |
| 							break;
 | |
| 						case '\r':
 | |
| 							header_state = HDST_CR;
 | |
| 							break;
 | |
| 						default:
 | |
| 							header_state = HDST_TEXT;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_TEXT:
 | |
| 					switch(head_buf[i]) {
 | |
| 						case '\n':
 | |
| 							header_state = HDST_LF;
 | |
| 							break;
 | |
| 						case '\r':
 | |
| 							header_state = HDST_CR;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_LF:
 | |
| 					switch(head_buf[i]) {
 | |
| 						case '\n':
 | |
| 							goto end_of_headers;
 | |
| 						case '\r':
 | |
| 							header_state = HDST_CR;
 | |
| 							break;
 | |
| 						default:
 | |
| 							header_state = HDST_TEXT;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_CR:
 | |
| 					switch(head_buf[i]) {
 | |
| 						case '\n':
 | |
| 							header_state = HDST_CRLF;
 | |
| 							break;
 | |
| 						case '\r':
 | |
| 							goto end_of_headers;
 | |
| 						default:
 | |
| 							header_state = HDST_TEXT;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_CRLF:
 | |
| 					switch(head_buf[i]) {
 | |
| 						case '\n':
 | |
| 							goto end_of_headers;
 | |
| 						case '\r':
 | |
| 							header_state = HDST_CRLFCR;
 | |
| 							break;
 | |
| 						default:
 | |
| 							header_state = HDST_TEXT;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 				case HDST_CRLFCR:
 | |
| 					switch(head_buf[i]) {
 | |
| 						case '\n':
 | |
| 						case '\r':
 | |
| 							goto end_of_headers;
 | |
| 						default:
 | |
| 							header_state = HDST_TEXT;
 | |
| 							break;
 | |
| 					}
 | |
| 					break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| end_of_headers:
 | |
| 	if(head_bytes > 0) {
 | |
| 		++i;
 | |
| 		(void) write(1, &head_buf[i], head_bytes - i);
 | |
| 	}
 | |
| 	for(;;) {
 | |
| 		(void) alarm(timeout);
 | |
| 		if(protocol == PROTO_HTTPS) {
 | |
| 			head_bytes = SSL_read(ssl, head_buf, sizeof(head_buf));
 | |
| 		} else {
 | |
| 			head_bytes = read(sockfd, head_buf, sizeof(head_buf));
 | |
| 		}
 | |
| 		if(head_bytes == 0) {
 | |
| 			break;
 | |
| 		}
 | |
| 		if(head_bytes < 0) {
 | |
| 			show_error("read");
 | |
| 		}
 | |
| 		(void) write(1, head_buf, head_bytes);
 | |
| 	}
 | |
| 	if(protocol == PROTO_HTTPS) {
 | |
| 		SSL_free(ssl);
 | |
| 		SSL_CTX_free(ssl_ctx);
 | |
| 	}
 | |
| 	(void) close(sockfd);
 | |
| }
 | |
| 
 | |
| void show_error(char* cause) {
 | |
| 	char buf[5000];
 | |
| 	(void) sprintf(buf, "%s: %s - %s", argv0, url, cause);
 | |
| 	perror(buf);
 | |
| 	exit(1);
 | |
| }
 | |
| 
 | |
| void sigcatch(int sig) {
 | |
| 	(void) fprintf(stderr, "%s: %s - timed out\n", argv0, url);
 | |
| 	exit(1);
 | |
| }
 | |
| 
 | |
| void strencode(char* to, char* from) {
 | |
| 	int tolen;
 | |
| 
 | |
| 	for(tolen = 0; *from != '\0'; ++from) {
 | |
| 		if(isalnum(*from) || strchr("/_.", *from) != (char*) 0) {
 | |
| 			*to = *from;
 | |
| 			++to;
 | |
| 			++tolen;
 | |
| 		} else {
 | |
| 			(void) sprintf(to, "%%%02x", (int) *from & 0xff);
 | |
| 			to += 3;
 | |
| 			tolen += 3;
 | |
| 		}
 | |
| 	}
 | |
| 	*to = '\0';
 | |
| }
 | |
| 
 | |
| void usage() {
 | |
| 	(void) fprintf(stderr, "usage:  %s [-c cookie] [-t timeout] [-r referer] [-u user-agent] [-a username:password] [-h header value] [-v] url\n", argv0);
 | |
| 	exit(1);
 | |
| }
 |