/* * CS 290 Sample Code #1 - a clear channel TCP client. * * This client copies all input received via stdin (keyboard or redirect) * to the network connection it has established, and sends all output * from the network connection to stdout. * * Compilation instructions: * * HPUX: gcc sample1.c -o sample1-hpux -DHAS_HERROR * Solaris: gcc sample1.c -o sample1-solaris -lsocket -lnsl * SunOS: gcc sample1.c -o sample1-sunos -lnsl * FreeBSD: gcc sample1.c -o sample1-fbsd -DHAS_HERROR * Linux: gcc sample1.c -o sample1-linux -DHAS_HERROR * * Solaris and SunOS can't use herror() - they're kind of braindead. * Solaris needs to be linked against both the socket and ns libraries, * SunOS against the NS library, and HPUX is the smartest of them all. * * 02/09/97 - Initial revision - Dave Andersen * */ #include /* HPUX requires for getopt */ #include /* Everyone else requires for getopt :) */ #include /* fprintf, stderr, etc */ #include /* Required for socket types and such */ #include #include /* Internet networking headers */ #include #include #include /* Used for the ioctl stuff */ #include /* (does not relate) */ #include /* * herror() is a very cool function introduced in 4.3BSD Tahoe * - unfortunatly, Sun decided to leave it out. We replace * - it with a less-informative printf for Sun boxes. */ #ifndef HAS_HERROR #define herror(A) printf("%s\n", A) #endif /* * Compatability cruft just in case our system isn't POSIX compliant.. * * This isn't really networking, just file descriptor stuff that we * shouldn't have to be bothered with. :) */ #ifndef STDIN_FILENO #define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 #endif /* * Our getremote() function fills in the details in to the sockaddr * structure passed to it. It returns 0 on success, or -1 on failure. * It prints the reason for failure to stderr */ int getremote (char *remote_host, int remote_port, struct sockaddr_in *sock) { register struct hostent *hostptr; if ( (hostptr = gethostbyname( remote_host )) == NULL) { /* The namelookup on the hostname failed. This is bad. */ fprintf(stderr, "Error resolving address for "); herror(remote_host); return -1; } /* Now that we've looked up the hosts, we go through this little * bit of magic to set the sockaddr_in structure up. Note that * by making use of the h_length field returned in the struct * hostent don't bind ourselves to a particular byte size. * * Also note that the addresses returned here are already in * network byte order - no conversion should be necessary. * * Also note that gethostbyname uses static storage in the library * to store the results. Future calls to gethostbyname() will * overwrite the current values, so we have to copy them in to our * own structure, as we do here: */ sock->sin_family = hostptr->h_addrtype; bcopy(hostptr->h_addr, (caddr_t)&sock->sin_addr, hostptr->h_length); /* * Here, however, we need to convert our host byte order remote port * to a network byte order port so that TCP can handle it. */ sock->sin_port = htons(remote_port); return 0; } void usage() { printf("syntax: sample1 \n\n"); } int connect_remote(struct sockaddr_in *sock) { int sockfd; /* Our socket descriptor */ struct protoent *proto; /* * First, we use getprotobyname to get the protocol number for * what we're trying to do. This isn't amazinglyl necessary - * many implementations just specify 0, the generic IP protocol * number, but we're trying to be anal here. */ if ( ( proto = getprotobyname("tcp")) == NULL) { perror("Could not get protocol number for TCP"); return -1; } if ( (sockfd = socket(AF_INET, /* Internet socket */ SOCK_STREAM, /* Streaming (TCP), not dgram (UDP) */ proto->p_proto)) < 0) { perror("Could not obtain a socket"); return -1; } if ( (connect(sockfd, (struct sockaddr *) sock, sizeof(struct sockaddr_in))) < 0) { perror("Error connecting to remote host"); return -1; } return sockfd; } /* A note about handle_io - we're cheating here, because the * point of this sample is the connection establishment code, * not how to handle a select() loop, which you may or may not * need for your assignment. * * Select lets you deal with data as it comes in on file descriptors * and when you can write out to those FDs. * * We're not blocking on reading here, but we're assuming we can always * write - an assumption we should not depend upon. */ void handle_io(int netin, int netout, int userin, int userout) { fd_set readset; int largest_fd = 0; char buffer[1024]; int numread; /* Non-blocking i/o. You can ignore. */ ioctl(netin, F_SETFL, O_NONBLOCK); ioctl(netout, F_SETFL, O_NONBLOCK); while (1) { FD_ZERO(&readset); FD_SET(netin, &readset); FD_SET(userin, &readset); largest_fd = netin; if (netout > netin) largest_fd = netout; largest_fd += 1; if (select(largest_fd, &readset, NULL, NULL, NULL) < 0) { perror("Error in select loop"); exit(-1); } if (FD_ISSET(netin, &readset)) { numread = read(netin, buffer, 1024); if (numread == 0) { /* The network closed its connection on us. */ fprintf(stderr, "Connection closed by remote host\n"); exit(-1); } write(userout, buffer, numread); } if (FD_ISSET(userin, &readset)) { numread = read(userin, buffer, 1024); if (numread == 0) { /* The network closed its connection on us. */ fprintf(stderr, "End of file reached (user side), exiting\n"); exit(-1); } write(netout, buffer, numread); } } } /* * A skeleton main - it just calls a few functions and then trusts that * they'll handle everything. */ int main(int argc, char **argv) { struct sockaddr_in sock; char *remote_host; int remote_port; int netfd = -1; /* Our network file descriptor - remote end */ if (argc != 3) { usage(); exit(-1); } remote_host = argv[1]; remote_port = atoi(argv[2]); /* Ordinarily, do more checking here.. */ /* This will segfault if not a number. */ if (getremote(remote_host, remote_port, &sock)) { exit(-1); } netfd = connect_remote(&sock); if (netfd < 0) exit(-1); /* Connect_remote failed */ handle_io(netfd, netfd, STDIN_FILENO, STDOUT_FILENO); exit(0); }