blob: 9847f0ac92d027329701a7870f2ef03ccb984958 [file] [log] [blame]
/* 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
}