#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <getopt.h>
#include "dns.h"
#include "socket.h"
#include "buffer.h"
#include "ip6.h"
#include "str.h"
#include "uint16.h"
#include "str.h"
#include "iopause.h"
#include "fmt.h"
#include "byte.h"
#include "ndelay.h"
#include "errmsg.h"
#include <sys/poll.h>
#include <string.h>
static char seed[128];
char mc6ip[16]={0xff,2,0,0,0,0,0,0,0,0,0,0,'n','c','p','0'};
char mc4ip[4]={224,'n','c','p'};
char bcip[4]={0xff,0xff,0xff,0xff};
static char message[]="ncp-lowfat-1.2.2";
#if 0
long int socket_sendfile(int destfd,int sourcefd,long int maxcopy) {
char buf[16*1024];
int len,written;
long int done=0;
while (maxcopy-done>0) {
if (maxcopy-done>16*1024)
len=read(sourcefd,buf,16*1024);
else
len=read(sourcefd,buf,maxcopy-done);
len=read(sourcefd,buf,16*1024);
if (len<0) return -1;
if (len==0) return done;
written=write(destfd,buf,len);
if (written<0) return -1;
if (written<len) return done+written;
done+=written;
}
return done;
}
#endif
int v6mcsock() {
int fd;
if ((fd=socket_udp6())!=-1) {
if (socket_bind6_reuse(fd,(char *)V6any,8002,0)!=-1) {
socket_mcloop6(fd,1);
return fd;
}
close(fd);
}
return -1;
}
int v4mcsock() {
int fd;
if ((fd=socket_udp4())!=-1) {
if (socket_bind4_reuse(fd,(char *)V6any,8002)!=-1) {
socket_mcloop4(fd,1);
return fd;
}
close(fd);
}
return -1;
}
int v4bcsock() {
int fd;
if ((fd=socket_udp4())!=-1) {
if (socket_bind4_reuse(fd,(char *)V6any,8002)!=-1)
if (socket_broadcast(fd)!=-1)
return fd;
close(fd);
}
return -1;
}
int v6tcpsock() {
int s;
if ((s=socket_tcp6())!=-1) {
if (socket_bind6_reuse(s,(char *)V6any,8002,0)!=-1)
if (socket_listen(s,1)!=-1)
return s;
close(s);
}
return -1;
}
int v4tcpsock() {
int s;
if ((s=socket_tcp4())!=-1) {
if (socket_bind4_reuse(s,(char *)V6any,8002)!=-1)
if (socket_listen(s,1)!=-1)
return s;
close(s);
}
return -1;
}
void npush(int argc,char *argv[]) {
int fd;
int s,s4;
char **newargv;
int i;
int fds[4];
int forcebc=0;
int whined[3];
int fl=0;
struct pollfd pfd[2];
if (*argv) {
if (str_equal(*argv,"--usage") || str_equal(*argv,"--help"))
die(0,"usage: npush [filename...]\nWill send stdin when no filenames are given");
if (str_equal(*argv,"-b")) {
forcebc=1; ++argv; --argc;
}
}
fds[0]=fds[1]=fds[2]=-1;
if ((fds[0]=v6mcsock())==-1)
carpsys("could not create IPv6 multicast UDP socket");
if ((fds[1]=v4mcsock())==-1)
carpsys("could not create IPv4 multicast UDP socket");
if (forcebc || (fds[0]==-1 && fds[1]==-1))
if ((fds[2]=v4bcsock())==-1)
carpsys("could not create IPv4 broadcast UDP socket");
if ((s=v6tcpsock())==-1)
carpsys("could not create IPv6 TCP socket");
if ((s4=v4tcpsock())==-1)
if (errno!=EADDRINUSE)
carpsys("could not create IPv4 TCP socket");
if (fds[0]==-1 && fds[1]==-1 && fds[2]==-1)
die(111,"can not continue without at least one UDP socket");
if (s==-1 && s4==-1)
die(111,"can not continue without at least one TCP socket");
whined[0]=whined[1]=whined[2]=0;
if (s!=-1) {
pfd[0].fd=s;
pfd[0].events=POLLIN;
++fl;
}
if (s4!=-1) {
pfd[fl].fd=s4;
pfd[fl].events=POLLIN;
++fl;
}
for (;;) {
int i;
if (fds[0]!=-1)
if (socket_send6(fds[0],message,str_len(message),mc6ip,8002,0)==-1) {
if (!whined[0]) carpsys("IPv6 multicast sendmsg failed");
whined[0]=1;
}
if (fds[1]!=-1)
if (socket_send4(fds[1],message,str_len(message),mc4ip,8002)==-1) {
if (!whined[1]) carpsys("IPv4 multicast sendmsg failed");
whined[1]=1;
}
if (fds[2]!=-1)
if (socket_send4(fds[2],message,str_len(message),bcip,8002)==-1) {
if (!whined[2]) carpsys("IPv4 broadcast sendmsg failed");
whined[2]=1;
}
if ((i=poll(pfd,fl,200))==-1)
die(111,"poll");
if (i>0) {
if (s!=-1 && (pfd[0].revents&POLLIN)) {
fd=socket_accept6(s,0,0,0);
if (fd==-1) carp("accept"); else break;
}
if (s4!=-1 && (pfd[fl-1].revents&POLLIN)) {
fd=socket_accept4(s4,0,0);
if (fd==-1) carp("accept"); else break;
}
}
}
if (fds[0]!=-1) close(fds[0]);
if (fds[1]!=-1) close(fds[1]);
if (fds[2]!=-1) close(fds[2]);
ndelay_off(fd);
close(s);
if (!*argv) { /* if command line is empty, send \0 and stdin */
char c=0;
char buf[1500];
int len,written;
if (write(fd,&c,1)!=1)
diesys(111,"write failed");
do {
len=read(0,buf,1500);
if (len==-1)
diesys(111,"read failed");
if (len==0)
break;
written=write(fd,buf,len);
if (written==-1)
diesys(111,"write failed");
if (written<len)
diesys(111,"short write");
} while (1);
close(fd);
exit(0);
}
close(1);
close(0);
if (dup2(fd,1) == -1 || close(fd)==-1)
diesys(111,"unable to set up stdout");
newargv=(char **)alloca((argc+2)*sizeof(char*));
newargv[0]="tar";
newargv[1]="cpvvf";
newargv[2]="-";
i=3;
while (*argv)
newargv[i++]=*argv++;
newargv[i]=0;
execvp(newargv[0],newargv);
diesys(111,"unable to run tar");
exit(0);
}
void npoll(int argc,char *argv[]) {
/* TODO: look for UDP packets, connect to sending IP.
* read 1 byte. If it is \0, dump the rest to stdout.
* Otherwise, create a pipe to tar x, write the byte followed by the
* socket data to the pipe. */
char buf[1600];
char ip[16];
char portstr[10];
uint16 port;
char **newargv;
int pipefd[2];
int s;
pid_t pid;
stralloc out={0};
int outidx=0;
uint32 scope_id=0;
if (*argv && (str_equal(*argv,"--usage") || str_equal(*argv,"--help")))
die(0,"usage: npoll [hostname...]\nWith hostname, will retrieve files/stdin from hostname.\n"
"Without hostname, will look for announcement packets from npush to find sender.");
if (*argv) {
stralloc fqdn={0};
stralloc tmp={0};
if (!stralloc_copys(&tmp,*argv))
die(111,"out of memory");
if (dns_ip6_qualify(&out,&fqdn,&tmp) == -1)
die(111,"temporary unable to figure out IP address for ",*argv);
if (out.len < 16)
die(111,"no IP address for ",*argv);
byte_copy(ip,16,out.s);
buf[fmt_ip6(buf,ip)]=0;
carp("connecting to ",buf);
} else {
if ((s=socket_udp6())==-1)
diesys(111,"could not create socket");
if (socket_bind6_reuse(s,(char *)V6any,8002,0)==-1)
diesys(111,"could not bind");
socket_mcjoin6(s,mc6ip,0);
socket_mcjoin4(s,mc4ip,(char *)V6any);
ndelay_off(s);
for (;;) {
if (socket_recv6(s,buf,1550,ip,&port,&scope_id)==-1)
diesys(111,"could not receive UDP packet");
if (port == 8002)
break;
}
buf[fmt_ip6(buf,ip)]=0;
portstr[fmt_ulong(portstr,port)]=0;
if (scope_id)
carp("got packet from ",buf," port ",portstr," on interface ",socket_getifname(scope_id));
else
carp("got packet from ",buf," port ",portstr);
close(s);
}
for (;;) {
s = socket_tcp6();
if (s==-1)
diesys(111,"socket");
if (socket_bind6(s,(char *)V6any,0,0) == -1)
diesys(111,"bind");
ndelay_off(s);
if (socket_connect6(s,ip,8002,scope_id) == -1) {
/* if (timeoutconn6(s,ip,8002,scope_id,60) == -1) { */
if (out.len>outidx+16) {
outidx+=16;
byte_copy(ip,16,out.s+outidx);
buf[fmt_ip6(buf,ip)]=0;
carp("connecting to ",buf);
close(s);
continue;
}
diesys(111,"connection to ",*argv," failed");
} else
break;
}
ndelay_off(s);
if (read(s,buf,1)!=1)
diesys(111,"read");
if (buf[0]==0) { /* stdin/stdout mode */
int len,written;
do {
len=read(s,buf,1500);
if (len==-1)
diesys(111,"read");
if (len==0)
break;
written=write(1,buf,len);
if (written==-1)
diesys(111,"write");
if (written<len)
die(111,"short write");
} while (1);
close(s);
exit(0);
}
/* unfortunately, since we read the first byte, we can't just exec
* tar now, we need to create a pipe, fork, exec tar and copy the
* bytes. */
if (pipe(pipefd)==-1)
diesys(111,"pipe failed");
switch (pid=fork()) {
case 0:
close(pipefd[1]);
close(0);
if (dup2(pipefd[0],0)==-1 || close(pipefd[0])==-1)
diesys(111,"unable to set up stdin");
newargv=(char **)alloca(4*sizeof(char *));
#if 1
newargv[0]="tar";
newargv[1]="xvvpf";
newargv[2]="-";
newargv[3]=0;
#else
newargv[0]="strace";
newargv[1]="tar";
newargv[2]="xvvpf";
newargv[3]="-";
newargv[4]=0;
#endif
execvp(newargv[0],newargv);
diesys(111,"unable to run tar");
case -1:
diesys(111,"fork");
}
close(pipefd[0]);
ndelay_off(1);
if (write(pipefd[1],buf,1)==-1)
diesys(111,"write");
{
int len,written;
do {
len=read(s,buf,1500);
if (len==-1)
diesys(111,"read");
if (len==0)
break;
written=write(pipefd[1],buf,len);
if (written==-1)
diesys(111,"write");
if (written<len)
die(111,"short write");
} while (1);
}
close(s);
close(pipefd[1]);
waitpid(pid,0,0);
exit(0);
}
void ncpserver(int argc,char *argv[]) {
char remoteip[16];
int fd,s;
uint16 remoteport;
char **newargv;
carp("server mode. waiting for connection.");
s = socket_tcp6();
if (s == -1)
diesys(111,"socket");
if (socket_bind6_reuse(s,(char *)V6any,8002,0) == -1)
diesys(111,"bind");
if (socket_listen(s,1) == -1)
diesys(111,"listen");
ndelay_off(s);
if ((fd=socket_accept6(s,remoteip,&remoteport,0)) == -1)
diesys(111,"accept");
close(0);
if (dup2(fd,0)==-1 || close(fd)==-1)
diesys(111,"dup2");
newargv=(char **)alloca(4*sizeof(char *));
newargv[0]="tar";
newargv[1]="xvvpf";
newargv[2]="-";
newargv[3]=0;
execvp(newargv[0],newargv);
diesys(111,"unable to run tar");
}
void ncpclient(int argc,char *argv[]) {
int s,i;
char **newargv;
stralloc fqdn={0};
stralloc out={0};
stralloc tmp={0};
if (str_equal(*argv,"--usage") || str_equal(*argv,"--help"))
die(0,"usage: server mode: ncp hostname filename [filename...]\n"
" client mode: ncp");
if (!stralloc_copys(&tmp,*argv))
die(111,"out of memory");
if (dns_ip6_qualify(&out,&fqdn,&tmp) == -1)
diesys(111,"temporary unable to figure out IP address for ",*argv);
if (out.len < 16)
die(111,"no IP address for ",*argv);
s = socket_tcp6();
if (s==-1)
diesys(111,"socket");
if (socket_bind6(s,(char *)V6any,0,0) == -1)
diesys(111,"bind");
if (socket_connect6(s,out.s,8002,0) == -1)
diesys(111,"connection to ",*argv);
ndelay_off(s);
close(1);
if (dup2(s,1)==-1 || close(s))
diesys(111,"dup2");
newargv=(char **)alloca((argc+2)*sizeof(char *));
newargv[0]="tar";
newargv[1]="cpvvf";
newargv[2]="-";
i=3;
argv++;
while (*argv)
newargv[i++]=*argv++;
newargv[i]=0;
execvp(newargv[0],newargv);
diesys(111,"unable to run tar");
}
int main(int argc,char *argv[]) {
char *argv0=*argv;
dns_random_init(seed);
if (!argv0) die(111,"no argv[0]?!");
argv0+=str_rchr(argv0,'/');
if (*argv0=='/') argv0++; else argv0=*argv;
errmsg_iam(argv0);
argv++;
if (*argv && str_equal(*argv,"--version"))
die(0,message," (libowfat)\nWritten by Felix von Leitner <felix-ncp@fefe.de>\n"
"This is free software; copy and use it as you like. No warranty.\n");
if (str_equal(argv0,"npush"))
npush(argc,argv);
else if (str_equal(argv0,"npoll"))
npoll(argc,argv);
else if (!*argv)
ncpserver(argc,argv);
else
ncpclient(argc,argv);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1