#define _POSIX_C_SOURCE 200809l #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include #include #include #include "xalloc.h" #include "url.h" #define CHUNK_SIZE 1024 int send_chunk(int, char *, size_t); int recv_chunk(int, char **, size_t *); ssize_t recv_timeout(int, char **, struct timeval *); int main(int argc, char **argv) { if (argc < 2) err(EXIT_FAILURE, "needs a url as argument"); url_t *url = url_parse(argv[1]); if (!url) err(EXIT_FAILURE, "error parsing url"); printf("scheme: %s\n" "net_path: %s\n" "abs_path: %s\n" "net_node: %s\n" "net_service: %s\n\n", url->scheme, url->net_path, url->abs_path, url->net_node, url->net_service); // got the url parsed, now try to connect if (strcmp(url->scheme, "http")) { url_free(url); free(url); err(EXIT_FAILURE, "only supports http"); } // get address information, this gives us a list of addinfos where // we can connect to struct addrinfo hints = {0}; struct addrinfo *servinfo = {0}; int status; hints.ai_family = AF_UNSPEC; // TODO: adjust hints based on cache and url info (TCP or UDP) hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; status = getaddrinfo(url->net_node, url->net_service, &hints, &servinfo); if (status) err(status, "getaddrinfo: %s", gai_strerror(status)); // This will print all available addresses for the given url printf("IP addresses for %s:\n", argv[1]); char ipstr[INET6_ADDRSTRLEN]; struct addrinfo *p; for(p = servinfo;p != NULL; p = p->ai_next) { void *addr; char *ipver; // get the pointer to the address itself, // different fields in IPv4 and IPv6: if (p->ai_family == AF_INET) { // IPv4 struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; addr = &(ipv4->sin_addr); ipver = "IPv4"; } else { // IPv6 struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; addr = &(ipv6->sin6_addr); ipver = "IPv6"; } inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); printf("\t%s: %s\n", ipver, ipstr); } puts("\n"); // Connect to the server int servsocket; struct addrinfo *sai; for (sai = servinfo; sai; sai = sai->ai_next) { servsocket = socket(sai->ai_family, sai->ai_socktype, sai->ai_protocol); if (servsocket != -1) break; } if (servsocket == -1 || !sai) err(errno, NULL); char port[32]; void *addr; long portn; if (sai->ai_family == AF_INET) { // IPv4 struct sockaddr_in *ipv4 = (struct sockaddr_in *)sai->ai_addr; addr = &(ipv4->sin_addr); portn = ntohs(ipv4->sin_port); } else { // IPv6 struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)sai->ai_addr; addr = &(ipv6->sin6_addr); portn = ntohl(ipv6->sin6_port); } snprintf(port, 31, "%ld", portn); inet_ntop(sai->ai_family, addr, ipstr, sizeof ipstr); printf("Connected to %s:%s\n", ipstr, port); connect(servsocket, sai->ai_addr, sai->ai_addrlen); //make socket non blocking //fcntl(servsocket, F_SETFL, O_NONBLOCK); char *header; xasprintf(&header, "GET /%s HTTP/1.0\r\n" "Host: %s\r\n" "\r\n", url->abs_path, url->net_node); printf("HTTP request header:\n"); printf("%s\n", header); if (send_chunk(servsocket, header, strlen(header)) == -1) err(errno, NULL); char *buf; ssize_t bufsize; struct timeval timeout = {10, 0}; if ((bufsize = recv_timeout(servsocket, &buf, &timeout)) < 0) err(errno, NULL); printf("GOT RESPONSE OF SIZE %ld\n\n", bufsize); puts(buf ? buf : "NOTHING RECIVED"); free(buf); free(header); close(servsocket); freeaddrinfo(servinfo); url_free(url); free(url); return 0; } // TODO: also use select here int send_chunk(int s, char *msg, size_t len) { // send a whole chunk of data size_t sent = 0; ssize_t st; while (sent < len) { st = send(s, msg+sent, len-sent, 0); if (st == -1) return -1; sent += st; } return 0; } ssize_t recv_timeout(int s, char **buf, struct timeval *t) { if (!t || !buf) { errno = EINVAL; return -1; } fd_set fds; int n; ssize_t size = 0, sr; char chunk[CHUNK_SIZE]; FD_ZERO(&fds); FD_SET(s, &fds); *buf = NULL; for (;;) { n = select(s+1, &fds, NULL, NULL, t); if (n == -1) goto err; if (n == 0) break; sr = recv(s, chunk, CHUNK_SIZE, 0); if (sr == -1) goto err; if (sr == 0) break; *buf = xrealloc(*buf, size+sr+1); memcpy(&((*buf)[size]), chunk, sr); size += sr; (*buf)[size] = '\0'; } return size; err: if (*buf) free(*buf); return -1; }