/* * "grappling hook" program. This is an extremely stripped-down form * of nc, designed for fetching stuff from elsewhere and nothing else. * * Usage: $0 addr port * * Connects to the given addr and port (each must be numeric). If the * connection succeeds, receives data, which it copies to stdout. On * receiving EOF from the network, exits. Does not read anything from * stdin. Does not have any flags. * * By default, uses getaddrinfo() and getnameinfo(), which can handle * v6 as well as v4. Build with -DV4ONLY to use inet_aton and * inet_ntoa instead, which are v4-only but more portable. * * Build with -DLINUX_WORKAROUND on Linux, to deal with some ways in * which it insists on being gratuitously incompatible with BSD. * * Because this program is supposed to be maximally portable, we do a * number of things differently from usual. For example, we don't let * loose the full power of gcc+extensions and we deal with include * file dependency bugs by working around them rather than just * assuming they've been fixed. * * This program is in the public domain. */ #include #include #include #include #include #include #include extern const char *__progname; static int net; static void usage(void) { fprintf(stderr,"Usage: %s addr port\n",__progname); exit(1); } #ifdef V4ONLY #include static void open_connection(const char *addr, const char *port) { struct sockaddr_in sin; net = socket(AF_INET,SOCK_STREAM,0); if (net < 0) { fprintf(stderr,"%s: socket: %s\n",__progname,strerror(errno)); exit(1); } bzero(&sin,sizeof(sin)); /* XXX API botch */ if (! inet_aton(addr,&sin.sin_addr)) { fprintf(stderr,"%s: %s: bad address\n",__progname,addr); exit(1); } sin.sin_port = htons(atoi(port)); sin.sin_family = AF_INET; #ifndef LINUX_WORKAROUND sin.sin_len = sizeof(sin); #endif if (connect(net,(void *)&sin,sizeof(sin)) < 0) { fprintf(stderr,"%s: connect (%s/%d): %s\n", __progname,inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),strerror(errno)); exit(1); } } #else #include static void open_connection(const char *addr, const char *port) { struct addrinfo hints; struct addrinfo *ai0; struct addrinfo *ai; int e; char ihost[NI_MAXHOST]; char iserv[NI_MAXSERV]; hints.ai_flags = AI_NUMERICHOST; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(addr,port,&hints,&ai0); if (e) { fprintf(stderr,"%s: %s/%s: %s\n",__progname,addr,port,gai_strerror(e)); exit(1); } if (! ai0) { fprintf(stderr,"%s: %s/%s: lookup succeeded but returned no addresses\n", __progname,addr,port); exit(1); } for (ai=ai0;ai;ai=ai->ai_next) { net = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (net < 0) { fprintf(stderr,"%s: socket (AF %d): %s\n", __progname,ai->ai_family,strerror(errno)); continue; } if (connect(net,ai->ai_addr,ai->ai_addrlen) < 0) { e = errno; if (getnameinfo( ai->ai_addr, ai->ai_addrlen, &ihost[0], sizeof(ihost), &iserv[0], sizeof(iserv), NI_NUMERICHOST | NI_NUMERICSERV )) { fprintf(stderr,"%s: connect (can't get text: %s): %s\n", __progname,strerror(errno),strerror(e)); } else { fprintf(stderr,"%s: connect (%s/%s): %s\n", __progname,&ihost[0],&iserv[0],strerror(e)); } close(net); continue; } return; } exit(1); } #endif int main(int, char **); int main(int ac, char **av) { char rbuf[8192]; int rlen; int woff; int wlen; if (ac != 3) usage(); open_connection(av[1],av[2]); while (1) { rlen = read(net,&rbuf[0],sizeof(rbuf)); if (rlen < 0) { fprintf(stderr,"%s: net read: %s\n",__progname,strerror(errno)); exit(1); } if (rlen == 0) exit(0); woff = 0; while (woff < rlen) { wlen = write(1,&rbuf[woff],rlen-woff); if (wlen < 0) { fprintf(stderr,"%s: output write: %s\n",__progname,strerror(errno)); exit(1); } if (wlen == 0) { fprintf(stderr,"%s: output zero write\n",__progname); exit(1); } woff += wlen; } } }