/* tcpclient.c * * TCP Client * note: 'echo "hello" | nc -l -p 8080 localhost' * * */ #include #include #include #include #include #include #include #include #include #include #include #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 \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"); }