|  | /* Copyright (c) 2014 The Regents of the University of California | 
|  | * Barret Rhoden <brho@cs.berkeley.edu> | 
|  | * See LICENSE for details. | 
|  | * | 
|  | * Echo server, runs on port 23.  Main purpose is low-level network debugging | 
|  | * and to show how networking commands in plan 9 correspond to BSD sockets | 
|  | * (which are now a part of our sysdeps in glibc). | 
|  | * | 
|  | * If you want to build the BSD sockets version, you need to comment out the | 
|  | * #define for PLAN9NET. | 
|  | * | 
|  | * based off http://www2.informatik.hu-berlin.de/~apolze/LV/plan9.docs/net.V | 
|  | * and http://en.wikibooks.org/wiki/C_Programming/Networking_in_UNIX */ | 
|  |  | 
|  | /* Comment this out for BSD sockets */ | 
|  | #define PLAN9NET | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <parlib/parlib.h> | 
|  | #include <unistd.h> | 
|  | #include <parlib/event.h> | 
|  | #include <benchutil/measure.h> | 
|  | #include <parlib/uthread.h> | 
|  | #include <parlib/timing.h> | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <sys/stat.h> | 
|  | #include <fcntl.h> | 
|  |  | 
|  | #ifdef PLAN9NET | 
|  |  | 
|  | #include <iplib/iplib.h> | 
|  |  | 
|  | #else | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #include <sys/socket.h> | 
|  | #include <netinet/in.h> | 
|  | #include <arpa/inet.h> | 
|  |  | 
|  | #endif | 
|  |  | 
|  | int main() | 
|  | { | 
|  | int ret; | 
|  | int afd, dfd, lcfd; | 
|  | char adir[40], ldir[40]; | 
|  | int n; | 
|  | char buf[256]; | 
|  |  | 
|  | #ifdef PLAN9NET | 
|  | printf("Using Plan 9's networking stack\n"); | 
|  | /* This clones a conversation (opens /net/tcp/clone), then reads the cloned | 
|  | * fd (which is the ctl) to givure out the conv number (the line), then | 
|  | * writes "announce [addr]" into ctl.  This "announce" command often has a | 
|  | * "bind" in it too.  plan9 bind just sets the local addr/port.  TCP | 
|  | * announce also does this.  Returns the ctlfd. */ | 
|  | afd = announce9("tcp!*!23", adir, 0); | 
|  |  | 
|  | if (afd < 0) { | 
|  | perror("Announce failure"); | 
|  | return -1; | 
|  | } | 
|  | printf("Announced on line %s\n", adir); | 
|  | #else | 
|  | printf("Using the BSD socket shims over Plan 9's networking stack\n"); | 
|  | int srv_socket, con_socket; | 
|  | struct sockaddr_in dest, srv = {0}; | 
|  | srv.sin_family = AF_INET; | 
|  | srv.sin_addr.s_addr = htonl(INADDR_ANY); | 
|  | srv.sin_port = htons(23); | 
|  | socklen_t socksize = sizeof(struct sockaddr_in); | 
|  |  | 
|  | /* Equiv to cloning a converstation in plan 9.  The shim returns the data FD | 
|  | * for the conversation. */ | 
|  | srv_socket = socket(AF_INET, SOCK_STREAM, 0); | 
|  | if (srv_socket < 0) { | 
|  | perror("Socket failure"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /* bind + listen is equiv to announce() in plan 9.  Note that the "bind" | 
|  | * command is used, unlike in the plan9 announce. */ | 
|  | /* Binds our socket to the given addr/port in srv. */ | 
|  | ret = bind(srv_socket, (struct sockaddr*)&srv, sizeof(struct sockaddr_in)); | 
|  | if (ret < 0) { | 
|  | perror("Bind failure"); | 
|  | return -1; | 
|  | } | 
|  | /* marks the socket as a listener/server */ | 
|  | ret = listen(srv_socket, 1); | 
|  | if (ret < 0) { | 
|  | perror("Listen failure"); | 
|  | return -1; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* at this point, the server has done all the prep necessary to be able to | 
|  | * sleep/block/wait on an incoming connection. */ | 
|  |  | 
|  | #ifdef PLAN9NET | 
|  | /* Opens the conversation's listen file.  This blocks til someone connects. | 
|  | * When they do, a new conversation is created, and that open returned an FD | 
|  | * for the new conv's ctl.  listen() reads that to find out the conv number | 
|  | * (the line) for this new conv.  listen() returns the ctl for this new | 
|  | * conv. */ | 
|  | lcfd = listen9(adir, ldir, 0); | 
|  |  | 
|  | if (lcfd < 0) { | 
|  | perror("Listen failure"); | 
|  | return -1; | 
|  | } | 
|  | printf("Listened and got line %s\n", ldir); | 
|  |  | 
|  | /* Writes "accept [NUM]" into the ctlfd, then opens the conv's data file and | 
|  | * returns that fd.  Writing "accept" is a noop for most of our protocols. | 
|  | * */ | 
|  | dfd = accept9(lcfd, ldir); | 
|  | if (dfd < 0) { | 
|  | perror("Accept failure"); | 
|  | return -1; | 
|  | } | 
|  | #else | 
|  | /* returns an FD for a new socket. */ | 
|  | dfd = accept(srv_socket, (struct sockaddr*)&dest, &socksize); | 
|  | if (dfd < 0) { | 
|  | perror("Accept failure"); | 
|  | return -1; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* echo until EOF */ | 
|  | printf("Server read: "); | 
|  | while ((n = read(dfd, buf, sizeof(buf))) > 0) { | 
|  | for (int i = 0; i < n; i++) | 
|  | printf("%c", buf[i]); | 
|  | fflush(stdout); | 
|  | write(dfd, buf, n); | 
|  | } | 
|  |  | 
|  | #ifdef PLAN9NET | 
|  | close(dfd);		/* data fd for the new conv, from listen */ | 
|  | close(lcfd);	/* ctl fd for the new conv, from listen */ | 
|  | close(afd);		/* ctl fd for the listening conv */ | 
|  | #else | 
|  | close(dfd);		/* new connection socket, from accept */ | 
|  | close(srv_socket); | 
|  | #endif | 
|  | } |