diff options
Diffstat (limited to 'tcpclient.c')
-rw-r--r-- | tcpclient.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/tcpclient.c b/tcpclient.c new file mode 100644 index 0000000..3410b97 --- /dev/null +++ b/tcpclient.c @@ -0,0 +1,222 @@ +/* tcpclient.c + * + * TCP Client + * note: 'echo "hello" | nc -l -p 8080 localhost' + * + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#define TARGET_MAX_SZ 1*1024*1024 + +static int sockfd; +static struct addrinfo *res; +static char *recv_buff; + +void cleanup(void) +{ + shutdown(sockfd, SHUT_RDWR); + close(sockfd); + + if (recv_buff) + free(recv_buff); + + /* free the linked list */ + if (res) + freeaddrinfo(res); +} + +int main(int argc, char **argv) +{ + if (argc != 2) + { + fprintf(stderr, "usage: %s <host>\n", argv[0]); + exit(-1); + } + if (!strstr(argv[1], "http://")) + { + fprintf(stderr, "host needs to prefixed with \"http://\" protocol\n"); + exit(-1); + } + + /* grab the host and path from the URL */ + char hoststr[100] = { 0 }, pathstr[100] = { 0 }, filestr[100] = { 0 }; + sscanf(argv[1], "http://%[^/]/%s", hoststr, pathstr); + printf("host is: \"%s\"\n" + "path is: \"%s\"\n", hoststr, pathstr); + + /* parse out a filestrname */ + char *filepart = strrchr(pathstr, '/'); + if (filepart && strlen(filepart + 1)) + strcpy(filestr, filepart + 1); + else + strcpy(filestr, "index.html"); + printf("file is: \"%s\"\n", filestr); + + int ret; + + /* prepare for getaddrinfo call */ + struct addrinfo hints; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if ((ret = getaddrinfo(hoststr, "http", &hints, &res)) != 0) + { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(ret)); + exit(-1); + } + + /* try all available IP addresses */ + struct addrinfo *p; + char ipstr[INET6_ADDRSTRLEN]; + void *addr; + char *ipver; + struct sockaddr_in *ipv4; +#if 0 + struct sockaddr_in6 *ipv6; +#endif + + printf("trying all available IP addresses for %s:\n", hoststr); + for (p = res; p != NULL; p = p->ai_next) + { + /* get the pointer to the address itself, + * different fields in IPv4 and IPv6 */ + if (p->ai_family == AF_INET) + { /* IPv4 */ + ipv4 = (struct sockaddr_in *) p->ai_addr; + addr = &(ipv4->sin_addr); + ipver = "IPv4"; + } + else + continue; /* ignore IPv6 for now */ +#if 0 + else + { /* IPv6 */ + ipv6 = (struct sockaddr_in6 *) p->ai_addr; + addr = &(ipv6->sin6_addr); + ipver = "IPv6"; + } +#endif + + /* convert the IP to a string and print it */ + inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr)); + printf("attempting to connect to %s: %s\n", ipstr, ipver); + + /* create a socket */ + if ((sockfd = socket(p->ai_family, p->ai_socktype, res->ai_protocol)) == -1) + { + perror("socket"); + close(sockfd); + } + + /* attempt to connect */ + if (connect(sockfd, (struct sockaddr *) ipv4, sizeof(*ipv4)) == -1) + perror("connect"); + else + { + printf("successfully connected\n"); + break; /* break out of a loop, we got a connection */ + } + } + + if (!p) + { + fprintf(stderr, "failed to connect to a host\n"); + exit(-1); + } + + atexit(cleanup); + + /* send http commands and receive data */ + char request[128] = { 0 }; + sprintf(request, "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n", pathstr, hoststr); + size_t request_len = strlen(request) * sizeof(char); + + ret = send(sockfd, (void *) request, request_len, 0); + if (ret == -1) + { + perror("send"); + exit(-1); + } + else if (ret != request_len) + { + fprintf(stderr, "sent less bytes than requested\n"); + exit(-1); + } + + /* receive until connection gets closed */ + recv_buff = (char *) malloc(sizeof(char) * TARGET_MAX_SZ); + if (!recv_buff) + { + perror("malloc"); + exit(-1); + } + size_t recv_len = 0; + + do + { + ret = recv(sockfd, recv_buff + recv_len, TARGET_MAX_SZ - recv_len, 0); + if (ret == -1) + { + perror("recv"); + exit(-1); + } + else if (ret == 0) + printf("remote host has closed the connection, received %lu bytes\n", recv_len); + + recv_len += ret; + + } while (ret > 0); + + if (recv_len < 1) + { + fprintf(stderr, "did not receive any data from the host!\n"); + exit(-1); + } + + /* poke at http header and check for errors */ + int code; + sscanf(recv_buff, "HTTP/1.%*[01] %d ", &code); + if (code == 200) + printf("received file\n"); + else + { + printf("got an error code %d\n", code); + char errorline[100]; + sscanf(recv_buff, "%[^\r\n]", errorline); + fprintf(stderr, "%s\n", errorline); + exit(-1); + } + + /* save to disk */ + char *buff_p = strstr(recv_buff, "\r\n\r\n"); /* seek past http header */ + if (buff_p) + { + buff_p += 4; + + FILE *fp = fopen(filestr, "w"); + if (!fp) + { + perror("fopen"); + exit(-1); + } + + ret = fwrite(buff_p, recv_len - (buff_p - recv_buff), sizeof(char), fp); + fclose(fp); + } + else + fprintf(stderr, "could not seek past http header\n"); +} + |