#/* # KIX server version 0.99.3 # To compile run: sh kix.c # Help is then available: ./kix -h # Network configuration options background: # ----------------------------------------- # Each argument on command-line specifies the net adress of the peer # where to send all the packets to. In the simplest configuration # where you have only two hosts (with arbitrary network between them): # # BOX_A <-> .. {some TCP/IP network} .. <-> BOX_B # # There will be the following configuration: # On BOX_A: ./kix BOX_B # On BOX_B: ./kix BOX_A # You can also specify the network broadcast address - in network # 192.168.1.xxx you have the netmask 255.255.255.0: # # BOX_A (192.168.1.1) BOX_B (192.168.1.2) BOX_C (192.168.1.10) # | | | # \------{Ethernet}------+------{Ethernet}-------/ # # There will be the following configuration: # On BOX_A, BOX_B and BOX_C: ./kix 192.168.1.255 # Advanced networking - packet forwarding: # # !!! NOT TESTED YET !!! - Please send me some # success/failure report. # # !!! Warning: You can very easily configure packet loops by using # forwarding options (':' sign). You have been warned. # # To optimize broadcasting behaviour you can direct some of KIX servers # to forward the packets to distant/firewalled hosts. The standard # behaviour of KIX when it receives the remote data broadcast packet # is to distribute it to all its LOCAL clients but to never pass it # further. Bu specifying some host(s) after the colon (':'): # # HOST_TO_FORWARD_TO:SOURCE_HOST(S) # # This line says: When I receive packet from SOURCE_HOST, I should # pass it to HOST_TO_FORWARD_TO. Some example: # # BOX_X <-> .. {some TCP/IP network} <-> BOX_A (192.168.1.1) # | # /-{Ethernet}-+-{Ethernet}-\ # | | # BOX_B (192.168.1.2) BOX_C (192.168.1.10) # # Setup with direct TCP/IP broadcasting through the network (recommended): # On BOX_X : ./kix 192.168.1.255 # On BOX_A : ./kix BOX_X:196.168.1.0/24 # On BOX_B/C: ./kix 192.168.1.255 # # Setup with KIX-manual forwaring (more interesting but unneeded/wasteful): # On BOX_X : ./kix BOX_A # On BOX_A : ./kix 192.168.1.255:KIX_A KIX_X:192.168.1.0/24 # On BOX_B/C: ./kix 192.168.1.255 # Copyright (C) 1998 Jan Kratochvil # Special thanks to Vojtech Pavlik # for testing and suggestions. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; you must use exactly version 2. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You may download a copy of the GNU General Public License from URL # http://www.opensource.org/gpl-license.html # If not, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. echo "Compiling KIX server..." a="cc" b="-s -Wall -O6 -fexpensive-optimizations -fomit-frame-pointer" c="-o `basename "$0" .c` $0" echo "$a $b $c" if $a $b $c;then echo -n else echo "$a $c" if $a $c;then echo -n else echo "Failed - please check the output.";exit fi fi echo "done." exit */ #define MULTI_LOCAL /* MULTI_LOCAL: Support multiple localhost clients & PID detection * * - needed to be able to run multiple clients on this single host * communicating each other * * - probably supported only under Linux (/proc needed to be mounted!) * (see kaddrmapper(): UDP port -> PID mapping function) * * - requires "root" permissions for this KIX server to use this feature * (under normal-user will KIX behave as w/o this feature compiled in) * * - isn't 100% rock solid as w/D1X it has to complete the search under 100ms! */ #define QUIET_PIDFAIL /* QUIET_PIDFAIL: Don't report errors on PID detection * * - don't report errors if the requested process couldn't be found * - probably good to be enabled as error dumping will slowdown KIX and * the search will so much more fail during the next attempted search */ #define IDLE_TIMEOUT 30*60 /* IDLE_TIMEOUT: Idle time in seconds to terminate if run from inetd */ #define SYSLOG_NAMES 1 #define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #define DEBUG */ #define VERSION "0.99.3" #define SERVICE_PORT 4213 #define BUFSIZE 4096 #define HOSTNAME_RESOLVE #define SETPRIORITY -50 #define INETD_SOCKET 0 #define PATH_PROC "/proc" #define PATH_PROC_NET_UDP PATH_PROC "/net/udp" #define PATH_PROC_X_FD PATH_PROC "/%s/fd" /* NOT working as of glibc-2.0.7: */ #undef DIRENT_HAVE_D_TYPE_WORKS #ifndef DEBUG #define NDEBUG #endif #include #ifdef DEBUG #define dbg(x) x #else #define dbg(x) #endif #ifndef __NORETURN #if __GNUC__ >= 2 #define __NORETURN __attribute__((__noreturn__)) #else #define __NORETURN #endif #endif #ifndef LINE_MAX #define LINE_MAX 4096 #endif #define TOSTRING(x) #x #define ARRSIZE(x) (sizeof((x))/sizeof(*(x))) int mainudp,bufl,peerstot,sendoffs; unsigned remaddrl; unsigned char buf[BUFSIZE]; const int val_one=1; typedef struct { unsigned char a[6]; } kaddrt; kaddrt mykaddr,remkaddr,*shipaddr=&remkaddr; struct peer { kaddrt dst; unsigned cnt; struct peersrc { unsigned a,m; /* host byte-order! */ } src[1]; } **peers; struct logf { struct logf *next; FILE *f; } *logfs,**logfsp=&logfs; const kaddrt kbroad={{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}; long setprio= #ifdef SETPRIORITY SETPRIORITY #else LONG_MAX #endif ; char noresolve= #ifdef HOSTNAME_RESOLVE 0 #else 1 #endif ; char quiet=0,debug=0,mapperfail=0,noportprint=0,logsyslog=0,logfsdone=0; struct cli { struct cli *next; kaddrt kaddr; /* [0..3]=IN addr, [4..5]=XORed PID */ kaddrt gwaddr; /* remote gateway w/SERVICE_PORT or "mykaddr" w/current UDP port */ unsigned short kport; /* KIX socket */ pid_t clpid; /* PID */ char *clname; /* process name */ char opened; /* bool, informational */ } *clis,*cli,**clip; const char *pname; const unsigned char iKIX[3]={'K','I','X'}; void (*errouth)(void); char msg[LINE_MAX]; size_t msgi; #define MSGSIZECHK() do { if (msgi>=sizeof(msg)-2) return; } while (0) #define minit() msgi=0 static void moutf(FILE *f) { fprintf(f,"%s: %s\n",pname,msg); } static void mout(int level) { struct logf *lf; if (!msgi) return; MSGSIZECHK(); msg[msgi]='\0'; if (!logfsdone || (!logfs && !logsyslog)) moutf(stdout); else { for (lf=logfs;lf;lf=lf->next) moutf(lf->f); if (logsyslog) syslog(level,"%s",msg); } } static void mvprintf(const char *fmt,va_list ap) __attribute__((format(printf,1,0))); static void mvprintf(const char *fmt,va_list ap) { int i; MSGSIZECHK(); if ((i=vsnprintf(msg+msgi,sizeof(msg)-1-msgi,fmt,ap))>=0) msgi+=i; } static void mprintf(const char *fmt,...) __attribute__((format(printf,1,2))); static void mprintf(const char *fmt,...) { va_list ap; va_start(ap,fmt); mvprintf(fmt,ap); va_end(ap); } static void mputchar(char c) { MSGSIZECHK(); msg[msgi++]=c; } static void verrout(const char *fmt,va_list ap) __attribute__((format(printf,1,0))); static void verrout(const char *fmt,va_list ap) { char fatal=0,showerr=0; int waserr=0; if (*fmt=='^') { fatal =1; fmt++; } if (!fatal && quiet) return; if (*fmt=='&') { showerr=1; fmt++; waserr=errno; } minit(); if (fatal) mprintf("FATAL: "); if (errouth) errouth(); mvprintf(fmt,ap); if (showerr) mprintf(" (%s)",strerror(waserr)); if (fatal) mputchar('!'); mout(LOG_ERR); if (fatal) exit(EXIT_FAILURE); } static void errout(const char *fmt,...) { va_list ap; va_start(ap,fmt); verrout(fmt,ap); va_end(ap); } static void chk(void *p) { if (p) return; errout("^Out of memory"); } #define chknew(a) chk((a)=malloc(sizeof(*(a)))) static void addrdump(const kaddrt *kap) { if (quiet) return; if (!noresolve) { struct hostent *he; he=gethostbyaddr((char *)(kap->a+0),4,AF_INET); if (he) mprintf("%s",he->h_name); } mprintf("[%u.%u.%u.%u]",kap->a[0],kap->a[1],kap->a[2],kap->a[3]); if (!noportprint) mprintf(":%u",ntohs(*(unsigned short *)(kap->a+4))); } static void pkterrh(void) { char sep; unsigned i; mprintf("PKT<"); addrdump(shipaddr); mputchar('>'); if (bufl-sendoffs) { mprintf("%d/",bufl-sendoffs); sep='['; for (i=sendoffs;inext) if (!memcmp(kap,((char *)cli)+offs,6)) break; return clip; } static void shipout(void) { int got; struct sockaddr_in sin; if (debug) pkterr("Sending..."); sin.sin_family=AF_INET; if (!memcmp(shipaddr->a+0,mykaddr.a+0,4)) sin.sin_addr.s_addr=htonl(INADDR_LOOPBACK); else memcpy(&sin.sin_addr,shipaddr->a+0,4); memcpy(&sin.sin_port,shipaddr->a+4,2); if (fcntl(mainudp,F_SETFL,O_NONBLOCK)) errout("&fcntl(mainudp,O_NONBLOCK)"); if ((got=sendto(mainudp,buf+sendoffs,bufl-sendoffs,0,(struct sockaddr *)&sin,sizeof(sin)))!=bufl-sendoffs) if (errno!=ECONNREFUSED) pkterr("&sendto(%d) error, wrote %d",bufl-sendoffs,got); if (fcntl(mainudp,F_SETFL,0 )) errout("&fcntl(mainudp,0)" ); } static void fillhost(const char *hname,kaddrt *kap) { struct hostent *he; if (!(he=gethostbyname(hname))) errout("^&gethostbyname(\"%s\")",hname); if (he->h_addrtype!=AF_INET || he->h_length!=4) errout("^\"%s\": addrtype!=AF_INET || length!=4",hname); if (!*he->h_addr_list) errout("^\"%s\": empty address list",hname); assert(sizeof(*kap)==6); memcpy(kap->a+0,*he->h_addr_list,4); *((unsigned short *)(kap->a+4))=htons(SERVICE_PORT); } static void kaddrdump(const kaddrt *kap) { unsigned char i; if (quiet) return; mprintf("KIX[0x"); for (i=0;i<6;i++) mprintf("%02X",kap->a[i]); mputchar(']'); } static void clistate(struct cli *cli,const char *fmt,...) { char local; va_list ap; if (quiet) return; minit(); va_start(ap,fmt); mvprintf(fmt,ap); va_end(ap); mprintf(": "); dbg(mprintf("(cli=%p) ",cli)); if ((local=!memcmp(&cli->gwaddr,mykaddr.a+0,4))) mprintf("local "); mprintf("client "); kaddrdump(&cli->kaddr); if (!local) { mprintf(" via "); addrdump(&cli->gwaddr); } mprintf(", kixport %u, process \"%s\"/%lu, %sed",ntohs(cli->kport),cli->clname,(unsigned long)ntohl(cli->clpid), (cli->opened?"open":"clos")); mout(LOG_INFO); } static void iKIXregister(void) {/* KIXrDDDDDDkpPID_[NAME] */ struct cli **clip,*cli; struct sockaddr_in addr; char ost; addr.sin_family=AF_INET; if (!(cli=*(clip=getclipk((kaddrt *)(buf+4))))) { chknew(cli); *clip=cli; cli->next=NULL; cli->kaddr=*(kaddrt *)(buf+4); ost=-1; } else { ost=cli->opened; free(cli->clname); } cli->gwaddr=remkaddr; memcpy(&cli->kport,buf+10,2); memcpy(&cli->clpid,buf+12,4); chk(cli->clname=malloc(bufl-16+1)); memcpy(cli->clname,buf+16,bufl-16); cli->clname[bufl-16]='\0'; cli->opened=1; clistate(cli,"iKIXregister%s%s ", (ost==1?",socket opened again" :""), (ost==0?",socket opened after being closed":"")); } static void iKIXunregister(void) { struct cli **clip,*cli; for (clip=&clis;(cli=*clip);clip=&cli->next) { if (memcmp(&cli->kaddr,buf+4,6)) continue; clistate(cli,"iKIXunregister%s", (!cli->opened?",already closed!":"")); cli->opened=0; } } static void iKIXdata(void) {/* KIXdDDDDDDssssssDKskDATA... */ struct cli *tocli; char isbroad=!memcmp(buf+4,&kbroad,6); unsigned char obyte=buf[9]; sendoffs=9; buf[9]=0x03; /* net data */ for (tocli=clis;tocli;tocli=tocli->next) { if (tocli==cli) continue; /* Do not echo packets back */ if (memcmp(tocli->gwaddr.a+0,mykaddr.a+0,4)) continue; if (memcmp(&tocli->kport,buf+16,2)) continue; if (!isbroad && (memcmp(tocli->kaddr.a+0,buf+4 ,5) || tocli->kaddr.a[5]!=obyte)) continue; shipaddr=&tocli->gwaddr; shipout(); } sendoffs=0; buf[9]=obyte; shipaddr=&remkaddr; } static void iKIXforward(void) { int i,j; unsigned remha=(unsigned)ntohl(*(unsigned long *)(remkaddr.a+0)); for (i=0;isrc; for (j=0;jcnt;ps++,j++) if (ps->a==(remha&ps->m)) { shipaddr=&peers[i]->dst; shipout(); break; } } shipaddr=&remkaddr; } static void shippeers(void) { int i; for (i=0;idst; shipout(); } shipaddr=&remkaddr; } static void sigalarm(int signo) { if (!quiet) { minit(); mprintf("Exiting due to communications inactivity."); mout(LOG_INFO); } exit(EXIT_SUCCESS); } #ifdef MULTI_LOCAL #define KAM_LOCAL_ADDRESS "local_address" #define KAM_INODE "inode" #define KAM_SOCKET_X "socket:[%s]" static pid_t kaddrmapper(const kaddrt *kap) { FILE *f; char line[LINE_MAX]; int procfdlen,findsklen; char c,*s,*s2,*serr,*ixla,*ixin; jmp_buf jmp; char findla[14],findsk[20],cmpsk[sizeof(findsk)]; long pidn; const char *kamfailerr; DIR *dirproc=NULL,*dirfd=NULL; struct dirent *direproc,*direfd; __NORETURN void kamfail(const char *why) { errout(kamfailerr+(*why!='&'),why+(*why=='&')); mapperfail=1; longjmp(jmp,1); } void cleanup(void) { if (dirproc) { if (closedir(dirproc)) errout("&closedir(\"" PATH_PROC "\")"); dirproc=NULL; } if (dirfd ) { if (closedir(dirfd )) errout("&closedir(\"" PATH_PROC_X_FD "\")",""); dirfd =NULL; } } char nextline(void) { char r; errno=0; r=!fgets(line,sizeof(line),f); if (errno) kamfail("&fgets"); return(r); } void walkerinit(void) { c=*(s2=line); } char walker(void) { *(s=s2)=c; while (*s!='\n'&&isspace(*s)) s++; for (s2=s;!isspace(*s2)&&*s2&&*s2!='\n';s2++); if (!(c=*s2)) kamfail("No EOL NL"); if (c=='\n') return(1); *s2='\0'; return(0); } if (mapperfail) return(-1); if (setjmp(jmp)) { cleanup(); return(-1); } kamfailerr="&%s(\"" PATH_PROC_NET_UDP "\",r/o) (multi-local disabled)"; if (snprintf(findla,14,"%08lX:%04X", *(unsigned long *)(kap->a+0) , /* !ntohl() is IMHO kernel bug */ (unsigned short)ntohs(*(unsigned short *)(kap->a+4)))!=13) kamfail("Internal - snprintf()"); if (!(f=fopen(PATH_PROC_NET_UDP,"rt"))) kamfail("&fopen"); if (nextline()) kamfail("No header line"); ixla=NULL; ixin=NULL; walkerinit(); for (;;) { if (walker()) break; if (!ixla && !strcmp(s,KAM_LOCAL_ADDRESS)) ixla=s; if (!ixin && !strcmp(s,KAM_INODE )) ixin=s; } if (!ixla) kamfail("\"" KAM_LOCAL_ADDRESS "\" not found"); if (!ixin) kamfail("\"" KAM_INODE "\" not found"); findsklen=-1; while (!nextline()) { walkerinit(); while (s2<=ixla || s2<=ixin) { if (walker()) kamfail("Not enough columns"); if (ixla==s && strcmp(s,findla)) { findsklen=-1; break; } if (ixin==s) { findsklen=snprintf(findsk,sizeof(findsk),KAM_SOCKET_X,s); if (findsklen<=0 || findsklen>=sizeof(findsk)-1) kamfail("&\"" KAM_INODE "\" parse error"); break; } } if (findsklen>=0) break; } if (findsklen<0) strcpy(findsk,""); else { kamfailerr="&%s(\"" PATH_PROC "\" scanning) (multi-local disabled)"; if (!(dirproc=opendir(PATH_PROC))) kamfail("&opendir"); while (errno=0,direproc=readdir(dirproc)) { #ifdef DIRENT_HAVE_D_TYPE_WORKS if (direproc->d_type!=DT_DIR) continue; #endif pidn=strtol(direproc->d_name,&serr,10); if (pidn<=0 || pidn>=LONG_MAX || *serr) continue; procfdlen=snprintf(line,sizeof(line),PATH_PROC_X_FD,direproc->d_name); if (procfdlen<=0 || procfdlen>=sizeof(line)-5) continue; errno=0; if (!(dirfd=opendir(line))) { if (errno==EPERM || errno==EACCES) kamfail("&opendir"); continue; } line[procfdlen]='/'; while ((direfd=readdir(dirfd))) { #ifdef DIRENT_HAVE_D_TYPE_WORKS if (direfd->d_type!=DT_LNK) continue; #endif if (procfdlen+1+strlen(direfd->d_name)+1>sizeof(line)) continue; strcpy(line+procfdlen+1,direfd->d_name); if (readlink(line,cmpsk,findsklen+1)!=findsklen) continue; if (memcmp(findsk,cmpsk,findsklen)) continue; cleanup(); return(pidn); } closedir(dirfd); } if (!direproc && errno) kamfail("&readdir"); } #ifndef QUIET_PIDFAIL if (!quiet) { minit(); mprintf("Process PID not found for "); addrdump(kap); mprintf(" (la=\"%s\", sk=\"%s\")!",findla,findsk); mout(LOG_NOTICE); } #endif return(-2); } /* MULTI_LOCAL: */ #endif static void doversion(void) { puts("\ I'm KIX, version " VERSION " compiled on \""__DATE__" "__TIME__"\" from \""__FILE__"\" by \""__VERSION__"\"\n\ Copyright (C) 1998 Jan Kratochvil \n\ This is free software with ABSOLUTELY NO WARRANTY. See the sources for details:\n\ http://atrey.karlin.mff.cuni.cz/~short/sw/kix.c.gz\n\ "); } const char *longdesc[]={ "display this help and exit", "display just version number and exit", "only serious errors are reported", "send all the messages (also) to this file\n\ " "defaults to stdout (\"-\"), multiple occurences allowed", "send all the messages to syslog as the specified facility\n\ " "see openlog(3): \"daemon\", \"user\", \"local0\", ...", "dump all the packets being communicated", #ifdef MULTI_LOCAL "disable support of multiple localhost clients", #endif "don't resolve hostnames", "set such process priority (x<0 runs faster!)\n\ " "value \"-\" will leave the priority untouched", "terminate when such seconds idle, defaults to:\n\ " "infinity(=0) if as standalone, IDLE_TIMEOUT if from inetd", }; const struct option longopts[]={ {"help" ,0,0,'h'}, {"version",0,0,'V'}, {"quiet" ,0,0,'q'}, {"logfile",1,0,'l'}, {"syslog" ,1,0,'s'}, {"debug" ,0,0,'d'}, #ifdef MULTI_LOCAL {"single" ,0,0,'1'}, #endif {"numeric",0,0,'n'}, {"pri" ,1,0,'p'}, {"timeout",1,0,'t'}, }; static __NORETURN void dohelp(void) { int i; doversion(); puts("\ Syntax: kix [-h] [-V] [-q] [-l ] [-s ]\n\ [-d]" #ifdef MULTI_LOCAL " [-1]" #endif " [-n] [-p ] [-t ]\n\ [[{:|,}[/{netbits|netmask}]]...]...\n\ "); for (i=0;i host to also send all the packets to\n\ forward packets if received from this peer\n\ packets are forwarded to the \n\ netmask \"255.255.255.0\" equals netbits \"24\"\n\ \n\ inetd - For automatic run add the next line to /etc/inetd.conf:\n\ 4213 dgram udp wait root /usr/bin/kix kix -s daemon PEER_ARGUMENTS\n\ "); exit(EXIT_FAILURE); } int main(int argc,char **argv) { int i,j,optc,sot; unsigned sotl=sizeof(sot); char localaddr,remremote; struct sockaddr_in anyaddr,remaddr; long idletmout=0; char setidletmout=0; assert(sizeof(remkaddr)==6); assert(sizeof(unsigned)==4); pname=*argv; setlinebuf(stdout); setlinebuf(stderr); if (gethostname((char *)buf,sizeof(buf))) errout("^&gethostname()"); fillhost((char *)buf,&mykaddr); while ((optc=getopt_long(argc,argv,"hVql:s:d" #ifdef MULTI_LOCAL "1" #endif "np:t:",longopts,NULL))!=EOF) switch (optc) { case 'V': doversion(); exit(EXIT_FAILURE); case 'q': quiet=1; break; case 'l': { struct logf *lf; chknew(lf); if (!(lf->f=fopen(optarg,"at"))) errout("^&fopen(\"%s\",append)",optarg); lf->next=NULL; *logfsp=lf; logfsp=&lf->next; break; } case 's': { int i; for (i=0;i=ARRSIZE(facilitynames)) errout("^Unknown syslog facility \"%s\"",optarg); openlog("kix",LOG_PID,facilitynames[i].c_val); logsyslog=1; break; } case 'd': debug=1; break; #ifdef MULTI_LOCAL case '1': mapperfail=1; break; #endif case 'n': noresolve=1; break; #define NUMPARSE_ERR "^\"-%c\" number \"%s\" parse error, offending: %s" case 'p': { char *serr; if (!strcmp(optarg,"-")) { setprio=LONG_MAX; break; } setprio=strtol(optarg,&serr,10); if (setprio<=INT_MIN || setprio >=INT_MAX || *serr) errout(NUMPARSE_ERR,'p',optarg,serr); break; } case 't': { char *serr; idletmout=strtol(optarg,&serr,10); if (idletmout<0 || idletmout>=INT_MAX || *serr) errout(NUMPARSE_ERR,'t',optarg,serr); break; } default: /* also 'h' */ dohelp(); } logfsdone=1; if ((peerstot=(argc-optind))) { chk(peers=malloc(sizeof(*peers)*(argc-optind))); for (i=0;idst); if ((peers[i]->cnt=comps)) { struct peersrc *ps=peers[i]->src; unsigned mask; kaddrt fakehost; while (comps--) { while (*s) s++; s++; mask=-1; for (s2=s;*s2;s2++) if (*s2=='/') { mask=0; *s2++='\0'; break; } fillhost(s,&fakehost); ps->a=(unsigned)ntohl(*(unsigned long *)(fakehost.a+0)); if (!mask) { char *serr; long ll; ll=strtol(s2,&serr,10); if (ll>=0 && ll<=32) while (ll--) mask=(mask>>1U)|(1U<<31U); else { fillhost(s2,&fakehost); mask=(unsigned)ntohl(*(unsigned long *)(fakehost.a+0)); } } ps->m=mask; ps->a&=mask; while (mask&(1U<<31U)) mask<<=1U; if (mask) errout("^Specified netmask (0x%08X) is invalid",ps->m); ps++; } } free(s); } } assert(peerstot>=0); errno=0; getsockopt(INETD_SOCKET,SOL_SOCKET,SO_TYPE,&sot,&sotl); if (errno && errno!=ENOTSOCK) errout("&getsockopt(" TOSTRING(INETD_SOCKET) ",SO_TYPE)"); if (!errno && sotl==sizeof(sot) && sot==SOCK_DGRAM) mainudp=INETD_SOCKET; else { mainudp=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); if (mainudp==-1 || mainudp==INETD_SOCKET) errout("^&socket()"); anyaddr.sin_family=AF_INET; anyaddr.sin_addr.s_addr=htonl(INADDR_ANY); anyaddr.sin_port=htons(SERVICE_PORT); if (bind(mainudp,(struct sockaddr *)&anyaddr,sizeof(anyaddr))) errout("^&bind(%d)",SERVICE_PORT); } if (!setidletmout) idletmout=(mainudp==INETD_SOCKET?IDLE_TIMEOUT:0); if (!quiet) { minit(); mprintf("KIX v" VERSION " on "); addrdump(&mykaddr); mprintf(", pri %d",getpriority(PRIO_PROCESS,0)); } if (setprio!=LONG_MAX) { if (setpriority(PRIO_PROCESS,0,SETPRIORITY)) { if (!quiet) mprintf(" left - %s",strerror(errno)); } else if (!quiet) mprintf(" -> %d",getpriority(PRIO_PROCESS,0)); } if (!quiet) { int rtm; mprintf(", %s, ",(mainudp==INETD_SOCKET?"inetd":"standalone")); if ((rtm=idletmout)) { mprintf("timeout is"); if (rtm>=3600) { mprintf(" %dhrs",rtm/3600); rtm%=3600; } if (rtm>= 60) { mprintf(" %dmin",rtm/ 60); rtm%= 60; } if (rtm>= 1) mprintf(" %dsec",rtm ); } else mprintf("no timeout"); mout(LOG_INFO); if (peerstot) { noportprint=1; minit(); mprintf("InterKIX to:"); for (i=0;isrc; mputchar(' '); addrdump(&peers[i]->dst); for (j=0;jcnt;ps++,j++) { unsigned mcnt,mask; kaddrt fakehost; mputchar(j?',':'<'); *(unsigned long *)(fakehost.a+0)=(unsigned long)htonl(ps->a); addrdump(&fakehost); if ((mask=ps->m)==-1) continue; for (mcnt=0;mask;mcnt++) mask<<=1U; mprintf("/%u",mcnt); } if (j) mputchar('>'); } mout(LOG_INFO); noportprint=0; } } if (setsockopt(mainudp,SOL_SOCKET,SO_BROADCAST,&val_one,sizeof(val_one))) errout("&setsockopt(mainudp,SO_BROADCAST)"); signal(SIGALRM,sigalarm); for (;;) { pktfail: remaddrl=sizeof(remaddr); alarm(idletmout); bufl=recvfrom(mainudp,&buf,sizeof(buf),0,(struct sockaddr *)&remaddr,&remaddrl); alarm(0); if (bufl==-1) { if (errno!=ECONNREFUSED) errout("&recvfrom: bufl=-1"); goto pktfail; } if (remaddr.sin_family!=AF_INET) { errout("recvfrom: sin_family %u!=AF_INET (%u)",remaddr.sin_family,AF_INET); goto pktfail; } localaddr=(ntohl(remaddr.sin_addr.s_addr)==INADDR_LOOPBACK || !memcmp(&remaddr.sin_addr,mykaddr.a+0,4)); remremote=(ntohs(remaddr.sin_port)==SERVICE_PORT); if (localaddr && remremote) goto pktfail; /* Talking to yourself, Graham? */ if (!localaddr && !remremote) { errout("Alien packet discarded"); goto pktfail; } memcpy(remkaddr.a+0,(remremote?(void *)&remaddr.sin_addr:(void *)(mykaddr.a+0)),4); memcpy(remkaddr.a+4,&remaddr.sin_port,2); if (debug) pkterr("received."); if (!bufl) { pkterr("Empty!"); goto pktfail; } if (bufl>=4 && !memcmp(buf,iKIX,3)) { if (!remremote) { errout("InterKIX packets not allowed from localhost"); goto pktfail; } switch (buf[3]) { case 'r': /* register: KIXrDDDDDDkpPID_[NAME] */ if (bufl<16) { pkterr("InterKIX Register len < 16"); goto pktfail; } iKIXregister(); iKIXforward(); break; case 'u': /* unregister: KIXuDDDDDD */ if (bufl!=10) { pkterr("InterKIX Unregister len != 10"); goto pktfail; } iKIXunregister(); iKIXforward(); break; case 'd': /* data: KIXdDDDDDDssssssDKskDATA... */ if (bufl <21) { pkterr("InterKIX Data" " len < 21" ); goto pktfail; } cli=NULL; iKIXdata(); iKIXforward(); break; default: pkterr("Unrecognized InterKIX command code 0x%02X",buf[3]); goto pktfail; } goto pktfail; } if (remremote) { errout("LocalKIX packets not allowed from remote"); goto pktfail; } switch (buf[0]) { case 0x01: /* open socket */ if (bufl<7) { pkterr("OpenSocket len < 7!"); goto pktfail; } { unsigned char *us; us=memchr(buf+7,0,bufl-7); bufl+=-1+10-(us?(buf+bufl)-us:0); } memmove(buf+10,buf+1,bufl-10); memcpy(buf+4,mykaddr.a+0,4); *(unsigned short *)(buf+8)= #ifdef MULTI_LOCAL (mapperfail?0:(*(unsigned short *)(buf+12))^(*(unsigned short *)(buf+14))) #else 0 #endif ; iKIXregister(); if (peerstot) { memcpy(buf,iKIX,3); buf[3]='r'; shippeers(); } buf[0]=0x06; /* open response */ memset(buf+1,0,2); /* ??? g_LastPort */ bufl=3; shipout(); break; case 0x02: /* close socket */ if (bufl<3) { pkterr("Close socket len <3!"); goto pktfail; } cli=*getclipgw(&remkaddr); if (!cli) { pkterr("Unregister() but before Register()!"); goto pktfail; } memcpy(buf+4,&cli->kaddr,6); iKIXunregister(); if (peerstot) { memcpy(buf,iKIX,3); buf[3]='u'; bufl=10; shippeers(); } buf[0]=0x07; /* close response */ memset(buf+1,0,2); /* ??? g_LastPort */ bufl=3; shipout(); break; case 0x03: /* net data: 3ddddddDK??DATA... -> KIXdDDDDDDssssssDKskDATA... */ if (bufl<11) { pkterr("Net data len < 11!"); goto pktfail; } if (!(cli=*(clip=getclipgw(&remkaddr)))) { pkterr("Got net data before socket open!"); goto pktfail; } memmove(buf+20,buf+11,bufl-1); bufl+=9; memcpy(buf+18,&cli->kport,2); /* sport */ memcpy(buf+16,buf+7 ,2); /* dport */ memcpy(buf+10,&cli->kaddr,6); /* saddr */ memmove(buf+4,buf+1 ,6); /* daddr */ memcpy(buf,iKIX,3); buf[3]='d'; iKIXdata(); if (!memcmp(buf+4,&kbroad,6)) shippeers(); else if (memcmp(buf+4,mykaddr.a+0,4)) /* not local */ for (cli=clis;cli;cli=cli->next) { if (memcmp(&cli->kaddr,buf+4,6)) continue; shipaddr=&cli->gwaddr; shipout(); } shipaddr=&remkaddr; break; case 0x05: /* Get Address */ buf[0]=0x04; /* myipxaddress */ #ifdef MULTI_LOCAL { pid_t rempid; kaddrt mapkaddr; *(unsigned long *)(mapkaddr.a+0)=(unsigned long)htonl(INADDR_LOOPBACK); memcpy(mapkaddr.a+4,remkaddr.a+4,2); if ((rempid=kaddrmapper(&mapkaddr))==-2) { #ifndef QUIET_PIDFAIL pkterr("GetAddress: no PID!"); #endif goto pktfail; } *(unsigned short *)(buf+5)=(rempid==-1?0:htons(rempid^(rempid>>16))); #else memset(buf+5,0,2); #endif memcpy(buf+1,mykaddr.a+0,4); if (!quiet) { minit(); mprintf("GetAddress: For "); addrdump(&remkaddr); mprintf(" set "); kaddrdump((kaddrt *)(buf+1)); #ifdef MULTI_LOCAL mprintf(", PID"); if (rempid==-1) mprintf(" detection failed"); else mprintf("=%d",rempid); #endif mout(LOG_INFO); } bufl=7; shipout(); break; #ifdef MULTI_LOCAL } #endif default: pkterr("Unrecognized command code 0x%02X",buf[0]); goto pktfail; } } return(EXIT_SUCCESS); }