ref: f31b4ce3b34f7191f7cfdae7b3d88e8e27e6b179
dir: /server.c/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <thread.h>
#include <libsec.h>
#include "neoventi.h"
static void vtsend(VtConn conn, char *buf, u16int size, u8int tag, int drop);
// Handles an error on `conn` handling client request `tbuf`
// Only the tag must be preserved in the buffer
static void
vterr(VtConn conn, char *tbuf, char *msg, ...)
{
u16int len;
va_list args;
va_start(args, msg);
msg = vsmprint(msg, args);
werrstr(msg);
free(msg);
va_end(args);
if(tbuf != nil){
len = snprint(tbuf+6, 0x10000, "neoventi: %r");
U16PUT(tbuf+4, len);
vtsend(conn, tbuf, len+4, VtRerror, 1);
}
longjmp(conn.bounce, 1);
}
static void
vthangup(VtConn conn)
{
longjmp(conn.bounce, 2);
}
// Convenience function: reads a venti packet from conn into buf
static void
vtrecv(VtConn conn, char *buf)
{
switch(read(conn.fd, buf, MaxPacketSize)){
case 0:
vthangup(conn);
case 1:
vterr(conn, nil, "received a single byte for message size");
default:
return;
}
}
static void
vtversion(VtConn conn, char *buf)
{
long n, i;
// Response is one line of unknown size; discard bytes until EOL
if(fprint(conn.fd, "venti-02-neoventi\n") == 18){
for(;;){
n = read(conn.fd, buf, 1);
if(n != 1)
break;
if(buf[0] == '\n')
return;
}
}
fail:
// If the handshake fails, make it clear there's a problem and give up
fprint(conn.fd, "FUCK OFF\n");
vterr(conn, nil, "handshake failed");
}
static void
vtsend(VtConn conn, char *buf, u16int size, u8int type, int drop)
{
U16PUT(buf, size);
buf[2] = type;
// +2 because we need to send the u16 size as well!
if(write(conn.fd, buf, size+2) != size+2){
if(drop)
fprint(2, "failed to submit error packet: %r\n");
else
vterr(conn, buf, "failed to write packet: %r");
}
}
static void
vtread(VtConn conn, char *buf)
{
VtAddress addr;
if(!vtreadlookup((u8int*)buf + 4, &addr))
vterr(conn, buf, "read error: %r");
if(!readclump((uchar*)buf+4, addr))
vterr(conn, buf, "clump read failed: %r");
vtsend(conn, buf, addr.size+2, VtRread, 0);
}
static void
vtwrite(VtConn conn, char *buf, u16int len)
{
u8int score[20];
uchar buf2[MaxPacketSize];
VtAddress addr;
sha1((uchar*)buf+8, len, score, nil);
if(vtreadlookup(score, &addr)){
if(addr.size != len)
vterr(conn, buf, "hash collision detected, addr size mismatch");
if(!readclump(buf2, addr))
vterr(conn, buf, "clump read failed: %r");
if(memcmp(buf2, buf+8, len) != 0)
vterr(conn, buf, "hash collision detected, memcmp divergence on write: len %d, buf %s\n", len, buf2);
} else {
if(!vtwriteclump(buf+8, len, score))
vterr(conn, buf, "data insertion failed: %r");
}
memcpy(buf+4, score, 20);
vtsend(conn, buf, 22, VtRwrite, 0);
}
static int
vtconnhandle(VtConn conn, char *buf, u16int len)
{
switch(buf[2]){
case VtTread:
vtread(conn, buf);
return 1;
case VtTwrite:
vtwrite(conn, buf, len-6);
return 1;
case VtTgoodbye:
vthangup(conn);
case VtTsync:
return 1;
default:
vterr(conn, buf, "TODO unimplemented request type %d", buf[2]);
}
return 0;
}
static void
vthello(VtConn conn, char *buf)
{
vtversion(conn, buf);
vtrecv(conn, buf);
if(buf[2] != VtThello)
vterr(conn, buf, "received message before hello: %d", buf[2]);
if(buf[4] != 0 || buf[5] != 2 || buf[6] != '0' || buf[7] != '2')
vterr(conn, buf, "unsupported protocol version requested in Thello: %d %d %d %d", buf[4], buf[5], buf[6], buf[7]);
buf[6] = 'n';
buf[7] = 'o';
vtsend(conn, buf, 8, VtRhello, 0);
}
static void
vtloop(VtConn conn, char *packetbuf)
{
long n;
u16int sz, len;
for(;;){
n = 0;
while(n < 2){
// Can read 2 bytes even if we've got one; packet must be at least
// 3 bytes anyways.
sz = read(conn.fd, packetbuf+n, 2);
if(sz <= 0)
vthangup(conn);
n += sz;
}
len = U16GET((uchar*)packetbuf);
while(n < len){
sz = read(conn.fd, packetbuf+n, len+2-n);
if(sz <= 0)
vthangup(conn);
n += sz;
}
vtconnhandle(conn, packetbuf, len);
}
}
static void
handleproc(void *fd)
{
char buf[MaxPacketSize];
VtConn conn;
conn.fd = (int)(usize)fd;
switch(setjmp(conn.bounce)){
case 0:
vthello(conn, buf);
vtloop(conn, buf);
break;
case 1:
fprint(2, "abandoning client: %r\n");
break;
case 2:
// Deliberate disconnect, don't log.
break;
default:
sysfatal("internal error: unexpected bounce code");
}
close(conn.fd);
}
static void
handle(int ctl, char *dir)
{
int fd = accept(ctl, dir);
if(fd < 0){
fprint(2, "failed to accept connection\n");
return;
}
proccreate(handleproc, (void*)fd, 512*1024);
}
void
serve(char *addr)
{
char adir[NETPATHLEN], dir[NETPATHLEN];
int fd, ctl;
fd = announce(addr, adir);
if(fd < 0)
sysfatal("%r");
procsetname("neoventi/server");
for(ctl = listen(adir, dir); ctl >= 0; ctl = listen(adir, dir)){
handle(ctl, dir);
close(ctl);
}
fprint(2, "server has died\n");
}