/* This file is in the public domain. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* I don't know who thought it'd be cute to invade the user's namespace with these delightful little surprises...(they come from , which is pulled in via -> -> ). */ #undef setbit #undef clrbit #undef isset #undef isclr /* More damned namespace pollution! */ #define setstate lb_setstate #include "lb.h" #include "getput.h" #include "pollloop.h" #include "timer-socket.h" extern const char *__progname; static const char *rnddev = "/dev/urandom"; static const unsigned char pre_s2c[2] = { 0x73, 0x2d }; static const unsigned char pre_c2s[2] = { 0x63, 0x2d }; static const unsigned char post_s2c[2] = { 0x3e, 0x63 }; static const unsigned char post_c2s[2] = { 0x3e, 0x73 }; #define CKSUM_CHUNK 64 static int dwnum; static const char *diskdev; static const char *host; static const char *port; static const char *keyfile; typedef struct ioq IOQ; typedef struct ob OB; typedef struct tmo TMO; struct tmo { int fd; void (*fn)(void *); void *arg; } ; struct ioq { OB *o; OB **ot; int oqlen; char *ibuf; int ilen; } ; struct ob { OB *link; const char *data; int left; char *free; } ; static int rndfd; static int diskfd; static int netfd; static int netid; static int ctlfd; static int datafd; static int net_up; static char *keydata; static int keylen; static unsigned long int bitmapcount; static int bitmaplevels; static unsigned int **bitmaps; static unsigned int disksize; static IOQ netq; static void (*net_read_fn)(void *); static void *net_read_arg; static struct addrinfo *server_ai0; static struct addrinfo *server_ai; static int mightwork; static unsigned char crypto_crnd[16]; static unsigned char crypto_srnd[16]; static int crypto_srfill; static ARC4_STATE c2senc; static ARC4_STATE s2cenc; static unsigned int hand; static unsigned char net_cksums[4+(CKSUM_CHUNK*20)]; static int netsumx; static unsigned char lcl_data[CKSUM_CHUNK*512]; static int lclsumx; static int nsumx; static unsigned char lcl_cksums[CKSUM_CHUNK*20]; static int state; #define S__UNSET 1 #define S_NOCONN 2 #define S_SCANNING 3 #define S_CATCHUP 4 #define S_LIVE 5 #define S_RESCAN 6 #define S_P_NOCONN 7 #define S_P_RECORD 8 #define S_P_IDLING 9 static time_t last_scan_progress; static int titlestate; static int titleval; static char *pending_status; static int status_id; static time_t last_status; static unsigned char rmsg; static const unsigned char pongmsg[1] = { LB_PONG }; static int dwstate; #define DW_OFF 0 #define DW_NUM 1 #define DW_FULL 2 static int signalpipe[2]; /* forward declarations */ static void connect_try(void); static void connect_start(void); static void new_msg(void); static void usage(void) __attribute__((__noreturn__)); static void usage(void) { fprintf(stderr,"Usage: %s diskwatchunit diskdev host port keyfile\n",__progname); exit(1); } static void *dequal(volatile const void *v) { void *tmp; tmp = &v; return(*(void **)tmp); } static void handleargs(int ac, char **av) { if (ac != 6) usage(); dwnum = atoi(av[1]); diskdev = av[2]; host = av[3]; port = av[4]; keyfile = av[5]; } static int openit(const char *path, int how, const char *what) { int fd; fd = open(path,how,0); if (fd < 0) { fprintf(stderr,"%s: can't open %s %s: %s\n",__progname,what,path,strerror(errno)); exit(1); } return(fd); } static int readd(unsigned int bno, void *buf, int nblks, int mustwork) { int r; int e; if (lseek(diskfd,bno*(off_t)512,SEEK_SET) < 0) { e = errno; fprintf(stderr,"%s: readd: seek to block %u: %s\n",__progname,bno,strerror(errno)); if (mustwork) exit(1); return(e); } r = read(diskfd,buf,nblks*512); if (r < 0) { e = errno; if (!mustwork && (e == EINVAL)) return(EINVAL); if (nblks == 1) { fprintf(stderr,"%s: readd: read block %u: %s\n",__progname,bno,strerror(errno)); } else { fprintf(stderr,"%s: readd: read blocks %u..%u: %s\n",__progname,bno,bno+nblks-1,strerror(errno)); } if (mustwork) exit(1); return(e); } if (r == 0) return(-1); if (r == nblks*512) return(0); fprintf(stderr,"%s: readd: read returned %d, wanted %d\n",__progname,r,nblks*512); if (mustwork) exit(1); return(EIO); } static void getdisksize(void) { unsigned int l; unsigned int m; unsigned int h; char blk[512]; l = 1; h = 1; while (readd(h,&blk[0],1,0) == 0) { l = h; h <<= 1; } while (h-l > 1) { m = (h + l) >> 1; if (readd(m,&blk[0],1,0) == 0) l = m; else h = m; } disksize = h; if (disksize < 1) exit(0); } static void initbitmaps(void) { unsigned int l; int i; l = disksize; bitmaplevels = 1; while (l > 32) { l = (l + 31) >> 5; bitmaplevels ++; } bitmaps = malloc(bitmaplevels*sizeof(*bitmaps)); l = disksize; for (i=0;i> 5; bitmaps[i] = malloc(l*sizeof(unsigned int)); bzero(bitmaps[i],l*sizeof(unsigned int)); } bitmapcount = 0; } static void docmd(const void *cmd, int len) { int w; w = write(ctlfd,cmd,len); if (w < 0) { fprintf(stderr,"%s: sending diskwatch command %.*s: %s\n",__progname,len,(const char *)cmd,strerror(errno)); exit(1); } } static void ioq_init(IOQ *q) { q->o = 0; q->ot = &q->o; q->ibuf = 0; q->oqlen = 0; } static void ioq_flusho(IOQ *q) { OB *b; while ((b = q->o)) { q->o = b->link; free(b->free); free(b); } q->ot = &q->o; q->oqlen = 0; } static void ioq_queue_copy(IOQ *q, const void *data, int len) { OB *b; b = malloc(sizeof(OB)+len); b->link = 0; bcopy(data,b+1,len); *q->ot = b; q->ot = &b->link; b->data = (void *)(b+1); b->left = len; b->free = 0; q->oqlen += len; } static void ioq_queue_free(IOQ *q, void *data, int len) { OB *b; b = malloc(sizeof(OB)); b->link = 0; *q->ot = b; q->ot = &b->link; b->data = data; b->left = len; b->free = data; q->oqlen += len; } static void setbit(unsigned int b) { int l; int sb; for (l=bitmaplevels-1;l>=0;l--) { sb = b >> (l * 5); if (! (bitmaps[l][sb>>5] & (1U << (sb & 31)))) { if (l == 0) bitmapcount ++; bitmaps[l][sb>>5] |= 1U << (sb & 31); } } } static void clrbit(unsigned int b) { int l; int sb; unsigned int v; for (l=0;l> (l * 5); v = bitmaps[l][sb>>5]; if (v & (1U << (sb & 31))) { if (l == 0) bitmapcount --; bitmaps[l][sb>>5] = v &= ~(1U << (sb & 31)); } if (v) break; } } static const char *statename(int s) { switch (s) { case S__UNSET: return("S__UNSET"); break; case S_NOCONN: return("S_NOCONN"); break; case S_SCANNING: return("S_SCANNING"); break; case S_CATCHUP: return("S_CATCHUP"); break; case S_LIVE: return("S_LIVE"); break; case S_RESCAN: return("S_RESCAN"); break; case S_P_NOCONN: return("S_P_NOCONN"); break; case S_P_RECORD: return("S_P_RECORD"); break; case S_P_IDLING: return("S_P_IDLING"); break; default: { static char errbuf[64]; sprintf(&errbuf[0],"?%d",s); return(&errbuf[0]); } break; } } static void net_wcrypted(const void *data, int len) { char *buf; buf = malloc(len); arc4_crypt(&c2senc,data,len,buf); ioq_queue_free(&netq,buf,len); } static void send_data_to_server(unsigned int bno, const void *data) { unsigned char bbuf[5]; bbuf[0] = LB_DATA; #ifdef BLATHER fprintf(stderr,"sending %s %u\n",msg_name(LB_DATA),bno); #endif put_4(&bbuf[1],bno); net_wcrypted(&bbuf[0],5); net_wcrypted(data,512); } static void setstate(int new) { state = new; #ifdef BLATHER fprintf(stderr,"state = %s\n",statename(new)); #endif } static void start_catchup(void) { setstate(S_CATCHUP); hand = 0; } static void scandone(void) { unsigned char bbuf[5]; start_catchup(); bbuf[0] = LB_SIZE; #ifdef BLATHER fprintf(stderr,"sending %s %u\n",msg_name(LB_SIZE),disksize); #endif put_4(&bbuf[1],disksize); net_wcrypted(&bbuf[0],5); } static void nextsumblk(void) { nsumx = CKSUM_CHUNK; if (nsumx > disksize-hand) nsumx = disksize - hand; if (nsumx < 1) { scandone(); return; } netsumx = 0; lclsumx = 0; } static int compare_checksums(void) { int i; if ((lclsumx < nsumx) || (netsumx < nsumx)) return(0); for (i=0;i 1048576) { setbit(hand+i); } else { send_data_to_server(hand+i,&lcl_data[i*512]); } } } hand += nsumx; nextsumblk(); new_msg(); return(1); } static void scan_got_cksums(void *arg __attribute__((__unused__))) { if (get_4(&net_cksums[0]) != hand) { fprintf(stderr,"%s: scan expected next chunk %u, got %u\n",__progname,hand,get_4(&net_cksums[0])); exit(1); } netsumx = nsumx; compare_checksums(); } static void drop_net(void) { remove_poll_id(netid); close(netfd); netfd = -1; ioq_flusho(&netq); net_up = 0; } static void lost_net(void) { drop_net(); switch (state) { case S_NOCONN: case S_SCANNING: case S_CATCHUP: case S_LIVE: case S_RESCAN: setstate(S_NOCONN); break; case S_P_NOCONN: case S_P_RECORD: case S_P_IDLING: setstate(S_P_NOCONN); break; } connect_start(); } static void clear_bitmaps(void) { int l; for (l=bitmaplevels-1;l>=0;l--) { /* the count is ((((disksize-1)>>(5*l))+1)+31)>>5, but I don't expect the optimizer to be smart enough to turn that into this. */ bzero(bitmaps[l],(((disksize-1)>>(5*(l+1)))+1)*sizeof(unsigned int)); } } static void dw_off(void) { docmd("0",1); dwstate = DW_OFF; } static void dw_num(void) { docmd("1",1); dwstate = DW_NUM; } static void dw_full(void) { docmd("2",1); dwstate = DW_FULL; } static void scanprogress(void) { last_scan_progress = time(0); } static void start_scan(void) { unsigned char pkt[11]; pkt[0] = LB_RQSUMS; #ifdef BLATHER fprintf(stderr,"sending %s %u\n",msg_name(LB_RQSUMS),disksize); #endif put_4(&pkt[1],0); put_4(&pkt[5],disksize); pkt[9] = CKSUM_CHUNK; pkt[10] = CKT_SHA1; net_wcrypted(&pkt[0],11); hand = 0; clear_bitmaps(); dw_full(); nextsumblk(); setstate(S_SCANNING); scanprogress(); } static void skip_sums_msg(void *arg __attribute__((__unused__))) { new_msg(); } static void accept_sums(void (*fn)(void *)) { netq.ibuf = &net_cksums[0]; netq.ilen = 4 + (nsumx * 20); net_read_fn = fn; scanprogress(); } static void msg_start(void *arg __attribute__((__unused__))) { #ifdef BLATHER fprintf(stderr,"got %s in %s\n",msg_name(rmsg),statename(state)); #endif switch (rmsg) { case LB_PING: #ifdef BLATHER fprintf(stderr,"sending %s\n",msg_name(LB_PING)); #endif net_wcrypted(&pongmsg[0],1); new_msg(); return; break; case LB_PONG: new_msg(); return; break; } switch (state) { case S_NOCONN: case S_P_NOCONN: abort(); break; case S_SCANNING: switch (rmsg) { case LB_SUMS: accept_sums(&scan_got_cksums); break; case LB_ABORTED: fprintf(stderr,"%s: LB_ABORTED message in state %s\n",__progname,statename(state)); lost_net(); break; } break; case S_CATCHUP: case S_LIVE: fprintf(stderr,"%s: %s message in state %s\n",__progname,msg_name(rmsg),statename(state)); lost_net(); break; case S_RESCAN: switch (rmsg) { case LB_SUMS: accept_sums(&skip_sums_msg); break; case LB_ABORTED: start_scan(); break; } break; case S_P_RECORD: case S_P_IDLING: switch (rmsg) { case LB_SUMS: accept_sums(&skip_sums_msg); break; case LB_ABORTED: new_msg(); break; } break; default: abort(); break; } } static void new_msg(void) { netq.ibuf = &rmsg; netq.ilen = 1; net_read_fn = &msg_start; } static void rd_datafd(int id __attribute__((__unused__)), void *arg __attribute__((__unused__))) { int r; int pp; int pe; unsigned long long int bno; unsigned long long int dbuf[1100]; r = read(datafd,&dbuf[0],sizeof(dbuf)); if (r < 0) { if (errno == EWOULDBLOCK) { r = 0; } else { fprintf(stderr,"%s: data fd read: %s\n",__progname,strerror(errno)); exit(1); } } if (r) { pe = r / sizeof(unsigned long long int); pp = 0; while <"pkts"> (pp < pe) { bno = dbuf[pp] & 0x00ffffffffffffffULL; switch (dbuf[pp] >> 56) { case 0x00: if (bno >= disksize) { fprintf(stderr,"%s: got DATA packet from kernel for block %llu >= disksize %u\n",__progname,bno,disksize); exit(1); } dwstate = DW_FULL; switch (state) { case S_NOCONN: case S_P_NOCONN: dw_off(); break <"pkts">; case S_SCANNING: if ((bno >= hand) && (bno < hand+nsumx)) { bcopy(&dbuf[pp+1],&lcl_data[(bno-hand)*512],512); } /* fall through */ case S_CATCHUP: case S_LIVE: send_data_to_server(bno,&dbuf[pp+1]); clrbit(bno); break; case S_P_RECORD: setbit(bno); dw_num(); break; case S_P_IDLING: dw_off(); break; default: abort(); break; } pp += (512 / sizeof(unsigned long long int)) + 1; break; case 0x01: if (bno >= disksize) { fprintf(stderr,"%s: got BLKNO packet from kernel for block %llu >= disksize %u\n",__progname,bno,disksize); exit(1); } dwstate = DW_NUM; switch (state) { case S_NOCONN: case S_P_NOCONN: dw_off(); break <"pkts">; case S_SCANNING: case S_CATCHUP: setbit(bno); break; case S_LIVE: setbit(bno); start_catchup(); break; case S_P_RECORD: setbit(bno); break; case S_P_IDLING: dw_off(); break; default: abort(); break; } pp ++; break; case 0x02: dwstate = DW_OFF; switch (state) { case S_NOCONN: case S_P_NOCONN: break; case S_SCANNING: { unsigned char pkt; pkt = LB_STOPSUM; #ifdef BLATHER fprintf(stderr,"sending %s\n",msg_name(LB_STOPSUM)); #endif net_wcrypted(&pkt,1); setstate(S_RESCAN); } break; case S_CATCHUP: case S_LIVE: start_scan(); break; case S_RESCAN: case S_P_IDLING: break; case S_P_RECORD: setstate(S_P_IDLING); break; default: abort(); break; } break <"pkts">; break; default: fprintf(stderr,"bad key %02llx from kernel\n",dbuf[pp]>>56); exit(1); break; } } } } static void rd_signal(int id __attribute__((__unused__)), void *arg __attribute__((__unused__))) { unsigned char sigs[64]; int r; int i; r = read(signalpipe[0],&sigs[0],sizeof(sigs)); if (r < 0) { switch (errno) { case EWOULDBLOCK: case EINTR: r = 0; break; default: fprintf(stderr,"%s: signal pipe read: %s\n",__progname,strerror(errno)); exit(1); break; } } for (i=0;i 65536) && (time(0)-last_status < 60)) return(0); l = strlen(pending_status); if (l > 255) l = 255; pref[0] = LB_STATUS; pref[1] = l; #ifdef BLATHER fprintf(stderr,"sending %s %d %.*s\n",msg_name(LB_STATUS),l,l,pending_status); #endif net_wcrypted(&pref[0],2); net_wcrypted(pending_status,l); last_status = time(0); free(pending_status); pending_status = 0; remove_block_id(status_id); status_id = PL_NOID; return(1); } static void proctitle(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void proctitle(const char *fmt, ...) { va_list ap; char *s; va_start(ap,fmt); vasprintf(&s,fmt,ap); va_end(ap); setproctitle("%s",s); free(pending_status); pending_status = s; if (status_id == PL_NOID) status_id = add_block_fn(&status_check,0); } static int blockfn(void *arg __attribute__((__unused__))) { switch (state) { case S_NOCONN: if (titlestate != S_NOCONN) { proctitle("%s: disconnected",diskdev); titlestate = S_NOCONN; } return(0); break; case S_SCANNING: if (time(0) - last_scan_progress > 1800) { lost_net(); return(1); } { int pct; pct = (hand * 100ULL) / disksize; if ((titlestate != S_SCANNING) || (titleval != pct)) { proctitle("%s: scanning %d%%",diskdev,pct); titlestate = S_SCANNING; titleval = pct; } } if ((netq.oqlen > 1048576) && (dwstate == DW_FULL)) dw_num(); if (lclsumx < nsumx) { void *h; if (lclsumx == 0) readd(hand,&lcl_data[0],nsumx,1); h = sha1_init(); sha1_process_bytes(h,&lcl_data[lclsumx*512],512); sha1_result(h,&lcl_cksums[lclsumx*20]); lclsumx ++; return(1); } return(compare_checksums()); break; case S_CATCHUP: { static struct timeval stamp; struct timeval now; gettimeofday(&now,0); if (now.tv_sec != stamp.tv_sec) { stamp = now; titlestate = S_CATCHUP+1; } if (titlestate != S_CATCHUP) { proctitle("%s: catchup %lu",diskdev,bitmapcount); titlestate = S_CATCHUP; } } if (netq.oqlen > 1048576) return(0); { int l; unsigned int sh; l = bitmaplevels - 1; while (1) { sh = hand >> (5*l); if (bitmaps[l][sh>>5] & (1U << (sh & 31))) { if (l == 0) { unsigned char blk[512]; readd(hand,&blk[0],1,1); send_data_to_server(hand,&blk[0]); clrbit(hand); hand ++; break; } else { l --; } } else { hand = (sh + 1) << (5*l); break; } } if (hand >= disksize) { if (bitmaps[bitmaplevels-1][0]) { hand = 0; } else { setstate(S_LIVE); dw_full(); } } } return(1); break; case S_LIVE: if (titlestate != S_LIVE) { proctitle("%s: live",diskdev); titlestate = S_LIVE; } if (netq.oqlen > 1048576) { dw_num(); start_catchup(); } return(0); break; case S_RESCAN: if (titlestate != S_RESCAN) { proctitle("%s: rescan",diskdev); titlestate = S_RESCAN; } return(0); break; case S_P_NOCONN: if (titlestate != S_P_NOCONN) { proctitle("%s: passive (disconnected)",diskdev); titlestate = S_P_NOCONN; } return(0); break; case S_P_RECORD: if ((titlestate != S_P_RECORD) || (titleval != bitmapcount)) { static struct timeval stamp; struct timeval now; gettimeofday(&now,0); if (now.tv_sec != stamp.tv_sec) { stamp = now; titlestate = S_P_RECORD + 1; } if (titlestate != S_P_RECORD) { proctitle("%s: passive %lu",diskdev,bitmapcount); titlestate = S_P_RECORD; titleval = bitmapcount; } } return(0); break; case S_P_IDLING: if (titlestate != S_P_IDLING) { proctitle("%s: passive (rescan)",diskdev); titlestate = S_P_IDLING; } return(0); break; } abort(); } static void setup(void) { int n; int e; int fd; struct addrinfo hints; char buf[128]; struct stat stb; diskfd = openit(diskdev,O_RDONLY,"disk device"); if (stat(keyfile,&stb) < 0) { fprintf(stderr,"%s: stat %s: %s\n",__progname,keyfile,strerror(errno)); exit(1); } keylen = stb.st_size; keydata = malloc(keylen); fd = openit(keyfile,O_RDONLY,"key file"); n = read(fd,keydata,keylen); if (n < 0) { fprintf(stderr,"%s: reading key data from %s: %s\n",__progname,keyfile,strerror(errno)); exit(1); } if (n != keylen) { fprintf(stderr,"%s: reading key data from %s: wanted %d, got %d\n",__progname,keyfile,keylen,n); exit(1); } close(fd); hints.ai_flags = 0; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(host,port,&hints,&server_ai0); if (e) { fprintf(stderr,"%s: can't look up %s/%s: %s\n",__progname,host,port,gai_strerror(e)); exit(1); } if (! server_ai0) { fprintf(stderr,"%s: %s/%s: lookup succeeded but no addresses?\n",__progname,host,port); exit(1); } rndfd = openit(rnddev,O_RDONLY,"random source"); getdisksize(); initbitmaps(); sprintf(&buf[0],"/dev/diskwatch%dctl",dwnum); ctlfd = openit(&buf[0],O_RDWR,"diskwatch control device"); sprintf(&buf[0],"/dev/diskwatch%ddata",dwnum); datafd = openit(&buf[0],O_RDWR,"diskwatch data device"); fcntl(datafd,F_SETFL,fcntl(datafd,F_GETFL,0)|O_NONBLOCK); add_poll_fd(datafd,&rwtest_always,&rwtest_never,&rd_datafd,0,0); docmd(&buf[0],sprintf(&buf[0],"df%d",diskfd)); netfd = -1; net_up = 0; ioq_init(&netq); signal(SIGPIPE,SIG_IGN); setstate(S_NOCONN); titlestate = S__UNSET; pending_status = 0; last_status = 0; status_id = PL_NOID; add_block_fn(&blockfn,0); } static void rd_timeout(int id, void *tv) { TMO *t; t = tv; remove_poll_id(id); close(t->fd); (*t->fn)(t->arg); free(t); } static void set_timeout(unsigned int sec, unsigned int usec, void (*fn)(void *), void *arg) { int fd; struct itimerval itv; TMO *t; fd = timer_socket(); if (fd < 0) { fprintf(stderr,"%s: timer socket: %s\n",__progname,strerror(errno)); exit(1); } itv.it_value.tv_sec = sec; itv.it_value.tv_usec = usec; itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0; write(fd,&itv,sizeof(itv)); t = malloc(sizeof(TMO)); t->fd = fd; t->fn = fn; t->arg = arg; add_poll_fd(fd,&rwtest_always,&rwtest_never,&rd_timeout,0,t); } static int rtest_net(int id __attribute__((__unused__)), void *arg __attribute__((__unused__))) { return((netfd >= 0) && netq.ibuf && (netq.ilen > 0)); } static int wtest_net(int id __attribute__((__unused__)), void *arg __attribute__((__unused__))) { return((netfd >= 0) && netq.o); } static void rd_net(int id __attribute__((__unused__)), void *arg __attribute__((__unused__))) { int r; int n; char rbuf[8192]; if (netfd < 0) return; if (!netq.ibuf || (netq.ilen < 1)) return; n = netq.ilen; if (n > sizeof(rbuf)) n = sizeof(rbuf); r = read(netfd,&rbuf[0],n); if (r < 0) { switch (errno) { case EWOULDBLOCK: case EINTR: return; break; } fprintf(stderr,"%s: network read: %s\n",__progname,strerror(errno)); lost_net(); return; } if (r == 0) { fprintf(stderr,"%s: network read EOF\n",__progname); lost_net(); return; } arc4_crypt(&s2cenc,&rbuf[0],r,netq.ibuf); netq.ibuf += r; netq.ilen -= r; if (netq.ilen < 1) (*net_read_fn)(net_read_arg); } static void wr_net(int id __attribute__((__unused__)), void *arg __attribute__((__unused__))) { static int maxiov = -1; static struct iovec *iov; static int iovn; int iovl; OB *b; int w; if (netfd < 0) return; if (! netq.o) return; if (maxiov < 0) { int mib[2]; size_t oldlen; mib[0] = CTL_KERN; mib[1] = KERN_IOV_MAX; oldlen = sizeof(maxiov); if (sysctl(&mib[0],2,&maxiov,&oldlen,0,0) < 0) { fprintf(stderr,"%s: can't get kern.iov_max: %s\n",__progname,strerror(errno)); exit(1); } if (maxiov > 64) maxiov = 64; if (maxiov < 1) maxiov = 1; iov = 0; iovn = 0; } iovl = 0; for (b=netq.o;b;b=b->link) { if (iovl >= maxiov) break; if (iovl >= iovn) iov = realloc(iov,(iovn=iovl+4)*sizeof(*iov)); iov[iovl++] = (struct iovec){ .iov_base=dequal(b->data), .iov_len=b->left }; } w = writev(netfd,iov,iovl); if (w < 0) { switch (errno) { case EWOULDBLOCK: case EINTR: return; break; } fprintf(stderr,"%s: network write: %s\n",__progname,strerror(errno)); lost_net(); return; } while ((b=netq.o) && w && (w >= b->left)) { netq.o = b->link; netq.oqlen -= b->left; w -= b->left; free(b->free); free(b); } if (b) { if (w) { b->data += w; b->left -= w; netq.oqlen -= w; } } else { netq.ot = &netq.o; } if ((netq.oqlen < 0) || (netq.oqlen && !netq.o)) abort(); } static void connect_start_arg(void *arg __attribute__((__unused__))) { connect_start(); } static void rd_crypto_s3(void *arg __attribute__((__unused__))) { if (bcmp(&crypto_crnd[0],&crypto_srnd[0],16)) { drop_net(); set_timeout(60,0,&connect_start_arg,0); return; } net_up = 1; new_msg(); start_scan(); } static void rd_crypto_s2(void *arg __attribute__((__unused__))) { net_wcrypted(&crypto_srnd[0],16); netq.ibuf = &crypto_srnd[0]; netq.ilen = 16; net_read_fn = &rd_crypto_s3; } static void crypto_step2(void) { void *h; int i; int j; int k; unsigned char m[20]; unsigned char key[257]; bzero(&key[0],237); for (i=0;i<32;i++) { h = sha1_init(); sha1_process_bytes(h,keydata,keylen); sha1_process_bytes(h,&crypto_srnd[0],16); sha1_process_bytes(h,&pre_s2c[0],2); if (i) sha1_process_bytes(h,&m[0],20); sha1_process_bytes(h,&post_s2c[0],2); sha1_process_bytes(h,&crypto_crnd[0],16); sha1_process_bytes(h,keydata,keylen); sha1_result(h,&m[0]); for (j=i*7,k=0;k<20;j++,k++) key[j] += m[k]; } h = sha1_init(); sha1_process_bytes(h,&key[0],237); sha1_result(h,&key[237]); arc4_init(&s2cenc); arc4_setkey(&s2cenc,&key[0],256,65536); bzero(&key[0],237); for (i=0;i<32;i++) { h = sha1_init(); sha1_process_bytes(h,keydata,keylen); sha1_process_bytes(h,&crypto_crnd[0],16); sha1_process_bytes(h,&pre_c2s[0],2); if (i) sha1_process_bytes(h,&m[0],20); sha1_process_bytes(h,&post_c2s[0],2); sha1_process_bytes(h,&crypto_srnd[0],16); sha1_process_bytes(h,keydata,keylen); sha1_result(h,&m[0]); for (j=i*7,k=0;k<20;j++,k++) key[j] += m[k]; } h = sha1_init(); sha1_process_bytes(h,&key[0],237); sha1_result(h,&key[237]); arc4_init(&c2senc); arc4_setkey(&c2senc,&key[0],256,65536); read(rndfd,&crypto_crnd[0],16); bzero(&crypto_srnd[0],16); bzero(&key[0],sizeof(key)); net_wcrypted(&crypto_crnd[0],16); netid = add_poll_fd(netfd,&rtest_net,&wtest_net,&rd_net,&wr_net,0); netq.ibuf = &crypto_srnd[0]; netq.ilen = 16; net_read_fn = &rd_crypto_s2; net_read_arg = 0; } static void rd_crypto_s1(int id, void *arg __attribute__((__unused__))) { int n; n = read(netfd,&crypto_srnd[crypto_srfill],16-crypto_srfill); if (n > 0) { crypto_srfill += n; if (crypto_srfill >= 16) { remove_poll_id(id); crypto_step2(); } return; } if (n < 0) { switch (errno) { case EWOULDBLOCK: case EINTR: return; break; } fprintf(stderr,"%s: network read: %s\n",__progname,strerror(errno)); } else { fprintf(stderr,"%s: network read EOF\n",__progname); } drop_net(); set_timeout(60,0,&connect_start_arg,0); } static void crypto_step1(void) { read(rndfd,&crypto_crnd[0],16); ioq_queue_copy(&netq,&crypto_crnd[0],16); crypto_srfill = 0; netid = add_poll_fd(netfd,&rwtest_always,&wtest_net,&rd_crypto_s1,&wr_net,0); } static void connect_ok(void) { int on; if (state == S_P_NOCONN) { close(netfd); netfd = -1; net_up = 0; return; } on = 1; setsockopt(netfd,SOL_SOCKET,SO_KEEPALIVE,&on,sizeof(on)); crypto_step1(); } static void connect_check(int id, void *arg __attribute__((__unused__))) { int err; socklen_t errlen; remove_poll_id(id); if (state == S_P_NOCONN) { close(netfd); netfd = -1; net_up = 0; return; } errlen = sizeof(err); if ((getsockopt(netfd,SOL_SOCKET,SO_ERROR,&err,&errlen) >= 0) && !err) { connect_ok(); return; } close(netfd); server_ai = server_ai->ai_next; connect_try(); } static void connect_try(void) { while (server_ai) { netfd = socket(server_ai->ai_family,server_ai->ai_socktype,server_ai->ai_protocol); if (netfd >= 0) { mightwork = 1; fcntl(netfd,F_SETFL,fcntl(netfd,F_GETFL,0)|O_NONBLOCK); if (connect(netfd,server_ai->ai_addr,server_ai->ai_addrlen) >= 0) { connect_ok(); return; } if (errno == EINPROGRESS) { add_poll_fd(netfd,&rwtest_never,&rwtest_always,0,&connect_check,0); return; } close(netfd); } server_ai = server_ai->ai_next; } if (! mightwork) { fprintf(stderr,"%s: can't connect to %s/%s\n",__progname,host,port); exit(1); } set_timeout(60,0,&connect_start_arg,0); } static void connect_start(void) { if (state == S_P_NOCONN) return; server_ai = server_ai0; mightwork = 0; connect_try(); } static void handle_usr(int sig) { unsigned char sigc; sigc = sig; write(signalpipe[1],&sigc,1); } static void signals_init(void) { struct sigaction sa; if (pipe(&signalpipe[0]) < 0) { fprintf(stderr,"%s: signal pipe: %s\n",__progname,strerror(errno)); exit(1); } fcntl(signalpipe[0],F_SETFL,fcntl(signalpipe[0],F_GETFL,0)|O_NONBLOCK); fcntl(signalpipe[1],F_SETFL,fcntl(signalpipe[1],F_GETFL,0)|O_NONBLOCK); sa.sa_handler = &handle_usr; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGUSR1,&sa,0); sigaction(SIGUSR2,&sa,0); add_poll_fd(signalpipe[0],&rwtest_always,&rwtest_never,&rd_signal,0,0); } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); timer_socket_init(); poll_init(); setup(); signals_init(); connect_start(); while (1) { pre_poll(); if (do_poll() < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s: poll: %s\n",__progname,strerror(errno)); exit(1); } post_poll(); } }