-referees
[www.jankratochvil.net.git] / project / kix / kix.c
1 #/*
2 # KIX server version 0.99.3
3 # To compile run:           sh kix.c
4 # Help is then available:   ./kix -h
5
6 # Network configuration options background:
7 # -----------------------------------------
8 #   Each argument on command-line specifies the net adress of the peer
9 # where to send all the packets to. In the simplest configuration
10 # where you have only two hosts (with arbitrary network between them):
11 #
12 #          BOX_A <-> .. {some TCP/IP network} .. <-> BOX_B
13 #
14 # There will be the following configuration:
15 # On BOX_A: ./kix BOX_B
16 # On BOX_B: ./kix BOX_A
17
18 # You can also specify the network broadcast address - in network
19 # 192.168.1.xxx you have the netmask 255.255.255.0:
20 #
21 # BOX_A (192.168.1.1)    BOX_B (192.168.1.2)    BOX_C (192.168.1.10)
22 #   |                      |                       |
23 #   \------{Ethernet}------+------{Ethernet}-------/
24 #
25 # There will be the following configuration:
26 # On BOX_A, BOX_B and BOX_C: ./kix 192.168.1.255
27
28 # Advanced networking - packet forwarding:
29 #
30 # !!! NOT TESTED YET !!! - Please send me <short@ucw.cz> some
31 #                          success/failure report.
32 #
33 # !!! Warning: You can very easily configure packet loops by using
34 #              forwarding options (':' sign). You have been warned.
35
36 #   To optimize broadcasting behaviour you can direct some of KIX servers
37 # to forward the packets to distant/firewalled hosts. The standard
38 # behaviour of KIX when it receives the remote data broadcast packet
39 # is to distribute it to all its LOCAL clients but to never pass it
40 # further. Bu specifying some host(s) after the colon (':'):
41 #
42 # HOST_TO_FORWARD_TO:SOURCE_HOST(S)
43 #
44 # This line says: When I receive packet from SOURCE_HOST, I should
45 # pass it to HOST_TO_FORWARD_TO. Some example:
46 #
47 # BOX_X <-> .. {some TCP/IP network} <-> BOX_A (192.168.1.1)
48 #                                          |
49 #                             /-{Ethernet}-+-{Ethernet}-\
50 #                             |                         |
51 #                           BOX_B (192.168.1.2)       BOX_C (192.168.1.10)
52 #
53 # Setup with direct TCP/IP broadcasting through the network (recommended):
54 #   On BOX_X  : ./kix 192.168.1.255
55 #   On BOX_A  : ./kix BOX_X:196.168.1.0/24
56 #   On BOX_B/C: ./kix 192.168.1.255
57 #
58 # Setup with KIX-manual forwaring (more interesting but unneeded/wasteful):
59 #   On BOX_X  : ./kix BOX_A
60 #   On BOX_A  : ./kix 192.168.1.255:KIX_A KIX_X:192.168.1.0/24
61 #   On BOX_B/C: ./kix 192.168.1.255
62
63
64 # Copyright (C) 1998 Jan Kratochvil <short@ucw.cz>
65 #  Special thanks to Vojtech Pavlik <vojtech@ucw.cz>
66 #  for testing and suggestions.
67 #
68 # This program is free software; you can redistribute it and/or modify
69 # it under the terms of the GNU General Public License as published by
70 # the Free Software Foundation; you must use exactly version 2.
71 #
72 # This program is distributed in the hope that it will be useful,
73 # but WITHOUT ANY WARRANTY; without even the implied warranty of
74 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
75 # GNU General Public License for more details.
76 #
77 # You may download a copy of the GNU General Public License from URL
78 #   http://www.opensource.org/gpl-license.html
79 # If not, write to the Free Software Foundation,
80 # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
81
82 echo "Compiling KIX server..."
83 a="cc"
84 b="-s -Wall -O6 -fexpensive-optimizations -fomit-frame-pointer"
85 c="-o `basename "$0" .c` $0"
86 echo "$a $b $c"
87 if $a $b $c;then echo -n
88 else echo "$a $c"
89   if $a $c;then echo -n
90         else echo "Failed - please check the output.";exit
91         fi
92 fi
93 echo "done."
94 exit
95 */
96
97 #define MULTI_LOCAL
98 /*      MULTI_LOCAL: Support multiple localhost clients & PID detection
99  *
100  * - needed to be able to run multiple clients on this single host
101  *   communicating each other
102  *
103  * - probably supported only under Linux (/proc needed to be mounted!)
104  *   (see kaddrmapper(): UDP port -> PID mapping function)
105  *
106  * - requires "root" permissions for this KIX server to use this feature
107  *   (under normal-user will KIX behave as w/o this feature compiled in)
108  *
109  * - isn't 100% rock solid as w/D1X it has to complete the search under 100ms!
110  */
111
112 #define QUIET_PIDFAIL
113 /*      QUIET_PIDFAIL: Don't report errors on PID detection
114  *
115  * - don't report errors if the requested process couldn't be found
116  * - probably good to be enabled as error dumping will slowdown KIX and
117  *   the search will so much more fail during the next attempted search
118  */
119
120 #define IDLE_TIMEOUT 30*60
121 /*      IDLE_TIMEOUT: Idle time in seconds to terminate if run from inetd
122  */
123
124 #define SYSLOG_NAMES 1
125 #define _GNU_SOURCE 1
126
127 #include <stdio.h>
128 #include <stdlib.h>
129 #include <sys/types.h>
130 #include <sys/socket.h>
131 #include <netinet/in.h>
132 #include <stdarg.h>
133 #include <ctype.h>
134 #include <errno.h>
135 #include <string.h>
136 #include <netdb.h>
137 #include <unistd.h>
138 #include <fcntl.h>
139 #include <stddef.h>
140 #include <sys/resource.h>
141 #include <setjmp.h>
142 #include <dirent.h>
143 #include <signal.h>
144 #include <syslog.h>
145 #include <getopt.h>
146 #include <limits.h>
147
148 /* #define DEBUG */
149 #define VERSION "0.99.3"
150 #define SERVICE_PORT 4213
151 #define BUFSIZE 4096
152 #define HOSTNAME_RESOLVE
153 #define SETPRIORITY -50
154 #define INETD_SOCKET 0
155 #define PATH_PROC         "/proc"
156 #define PATH_PROC_NET_UDP PATH_PROC "/net/udp"
157 #define PATH_PROC_X_FD    PATH_PROC "/%s/fd"
158 /* NOT working as of glibc-2.0.7: */
159 #undef  DIRENT_HAVE_D_TYPE_WORKS
160
161 #ifndef DEBUG
162 #define NDEBUG
163 #endif
164 #include <assert.h>
165
166 #ifdef DEBUG
167 #define dbg(x) x
168 #else
169 #define dbg(x)
170 #endif
171
172
173 #ifndef __NORETURN
174 #if __GNUC__ >= 2
175 #define __NORETURN __attribute__((__noreturn__))
176 #else
177 #define __NORETURN
178 #endif
179 #endif
180
181 #ifndef LINE_MAX
182 #define LINE_MAX 4096
183 #endif
184
185 #define TOSTRING(x) #x
186 #define ARRSIZE(x) (sizeof((x))/sizeof(*(x)))
187
188 int mainudp,bufl,peerstot,sendoffs;
189 unsigned remaddrl;
190 unsigned char buf[BUFSIZE];
191 const int val_one=1;
192
193 typedef struct {
194         unsigned char a[6];
195         } kaddrt;
196
197 kaddrt mykaddr,remkaddr,*shipaddr=&remkaddr;
198
199 struct peer {
200         kaddrt dst;
201         unsigned cnt;
202         struct peersrc {
203                 unsigned a,m;   /* host byte-order! */
204                 }       src[1];
205         } **peers;
206
207 struct logf {
208         struct logf *next;
209         FILE *f;
210         } *logfs,**logfsp=&logfs;
211
212 const kaddrt kbroad={{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}};
213 long setprio=
214 #ifdef SETPRIORITY
215         SETPRIORITY
216 #else
217         LONG_MAX
218 #endif
219         ;
220 char noresolve=
221 #ifdef HOSTNAME_RESOLVE
222         0
223 #else
224         1
225 #endif
226         ;
227 char quiet=0,debug=0,mapperfail=0,noportprint=0,logsyslog=0,logfsdone=0;
228
229 struct cli {
230         struct cli *next;
231         kaddrt kaddr;              /* [0..3]=IN addr, [4..5]=XORed PID */
232         kaddrt gwaddr;             /* remote gateway w/SERVICE_PORT or "mykaddr" w/current UDP port */
233         unsigned short kport;      /* KIX socket */
234         pid_t clpid;               /* PID */
235         char *clname;              /* process name */
236         char opened;               /* bool, informational */
237         } *clis,*cli,**clip;
238
239 const char *pname;
240 const unsigned char iKIX[3]={'K','I','X'};
241
242 void (*errouth)(void);
243
244 char msg[LINE_MAX];
245 size_t msgi;
246 #define MSGSIZECHK() do { if (msgi>=sizeof(msg)-2) return; } while (0)
247
248 #define minit() msgi=0
249
250 static void moutf(FILE *f)
251 { fprintf(f,"%s: %s\n",pname,msg); }
252
253 static void mout(int level)
254 {
255 struct logf *lf;
256
257         if (!msgi) return;
258         MSGSIZECHK();
259         msg[msgi]='\0';
260         if (!logfsdone || (!logfs && !logsyslog)) moutf(stdout);
261         else {
262                 for (lf=logfs;lf;lf=lf->next) moutf(lf->f);
263                 if (logsyslog) syslog(level,"%s",msg);
264                 }
265 }
266
267 static void mvprintf(const char *fmt,va_list ap) __attribute__((format(printf,1,0)));
268 static void mvprintf(const char *fmt,va_list ap)
269 {
270 int i;
271         MSGSIZECHK();
272         if ((i=vsnprintf(msg+msgi,sizeof(msg)-1-msgi,fmt,ap))>=0) msgi+=i;
273 }
274 static void mprintf(const char *fmt,...) __attribute__((format(printf,1,2)));
275 static void mprintf(const char *fmt,...)
276 { va_list ap; va_start(ap,fmt); mvprintf(fmt,ap); va_end(ap); }
277
278 static void mputchar(char c)
279 { MSGSIZECHK(); msg[msgi++]=c; }
280
281 static void verrout(const char *fmt,va_list ap) __attribute__((format(printf,1,0)));
282 static void verrout(const char *fmt,va_list ap)
283 {
284 char fatal=0,showerr=0;
285 int waserr=0;
286
287         if (*fmt=='^') { fatal  =1; fmt++; }
288         if (!fatal && quiet) return;
289         if (*fmt=='&') { showerr=1; fmt++; waserr=errno; }
290         minit();
291         if (fatal) mprintf("FATAL: ");
292         if (errouth) errouth();
293         mvprintf(fmt,ap);
294         if (showerr) mprintf(" (%s)",strerror(waserr));
295         if (fatal) mputchar('!');
296         mout(LOG_ERR);
297         if (fatal) exit(EXIT_FAILURE);
298 }
299
300 static void errout(const char *fmt,...)
301 {
302 va_list ap;
303         va_start(ap,fmt);
304         verrout(fmt,ap);
305         va_end(ap);
306 }
307
308 static void chk(void *p)
309 {
310         if (p) return;
311         errout("^Out of memory");
312 }
313 #define chknew(a) chk((a)=malloc(sizeof(*(a))))
314
315 static void addrdump(const kaddrt *kap)
316 {
317         if (quiet) return;
318         if (!noresolve) {
319 struct hostent *he;
320                 he=gethostbyaddr((char *)(kap->a+0),4,AF_INET);
321                 if (he) mprintf("%s",he->h_name);
322                 }
323         mprintf("[%u.%u.%u.%u]",kap->a[0],kap->a[1],kap->a[2],kap->a[3]);
324         if (!noportprint) mprintf(":%u",ntohs(*(unsigned short *)(kap->a+4)));
325 }
326
327 static void pkterrh(void)
328 {
329 char sep;
330 unsigned i;
331
332         mprintf("PKT<");
333         addrdump(shipaddr);
334         mputchar('>');
335         if (bufl-sendoffs) {
336                 mprintf("%d/",bufl-sendoffs);
337                 sep='[';
338                 for (i=sendoffs;i<bufl;i++) {
339                         mprintf("%c%02X",sep,buf[i]);
340                         sep=' ';
341                         }
342                 mputchar('=');
343                 for (i=sendoffs;i<bufl;i++) {
344                         if (isprint(buf[i])) mputchar(buf[i]);
345                         else mputchar('.');
346                         }
347                 mputchar(']');
348                 }
349         mprintf(": ");
350 }
351
352 static void pkterr(char *fmt,...)
353 {
354 va_list ap;
355         if (quiet) return;
356         va_start(ap,fmt);
357         errouth=pkterrh;
358         verrout(fmt,ap);
359         errouth=NULL;
360         va_end(ap);
361 }
362
363 #define addreq(a,b) ((a).sin_port==(b).sin_port&&(a).sin_addr.s_addr==(b).sin_addr.s_addr)
364
365 #define getclipk(k)  getclipoffs((k),offsetof(struct cli, kaddr))
366 #define getclipgw(k) getclipoffs((k),offsetof(struct cli,gwaddr))
367 static struct cli **getclipoffs(const kaddrt *kap,size_t offs)
368 {
369 struct cli **clip,*cli;
370         for (clip=&clis;(cli=*clip);clip=&cli->next)
371                 if (!memcmp(kap,((char *)cli)+offs,6)) break;
372         return clip;
373 }
374
375 static void shipout(void)
376 {
377 int got;
378 struct sockaddr_in sin;
379
380         if (debug) pkterr("Sending...");
381         sin.sin_family=AF_INET;
382         if (!memcmp(shipaddr->a+0,mykaddr.a+0,4))
383                 sin.sin_addr.s_addr=htonl(INADDR_LOOPBACK);
384         else memcpy(&sin.sin_addr,shipaddr->a+0,4);
385         memcpy(&sin.sin_port,shipaddr->a+4,2);
386         if (fcntl(mainudp,F_SETFL,O_NONBLOCK)) errout("&fcntl(mainudp,O_NONBLOCK)");
387         if ((got=sendto(mainudp,buf+sendoffs,bufl-sendoffs,0,(struct sockaddr *)&sin,sizeof(sin)))!=bufl-sendoffs)
388                 if (errno!=ECONNREFUSED) pkterr("&sendto(%d) error, wrote %d",bufl-sendoffs,got);
389         if (fcntl(mainudp,F_SETFL,0         )) errout("&fcntl(mainudp,0)"         );
390 }
391
392 static void fillhost(const char *hname,kaddrt *kap)
393 {
394 struct hostent *he;
395         if (!(he=gethostbyname(hname))) errout("^&gethostbyname(\"%s\")",hname);
396         if (he->h_addrtype!=AF_INET || he->h_length!=4)
397                 errout("^\"%s\": addrtype!=AF_INET || length!=4",hname);
398         if (!*he->h_addr_list)
399                 errout("^\"%s\": empty address list",hname);
400         assert(sizeof(*kap)==6);
401         memcpy(kap->a+0,*he->h_addr_list,4);
402         *((unsigned short *)(kap->a+4))=htons(SERVICE_PORT);
403 }
404
405 static void kaddrdump(const kaddrt *kap)
406 {
407 unsigned char i;
408
409         if (quiet) return;
410         mprintf("KIX[0x");
411         for (i=0;i<6;i++) mprintf("%02X",kap->a[i]);
412         mputchar(']');
413 }
414
415 static void clistate(struct cli *cli,const char *fmt,...)
416 {
417 char local;
418 va_list ap;
419
420         if (quiet) return;
421         minit();
422         va_start(ap,fmt); mvprintf(fmt,ap); va_end(ap);
423         mprintf(": ");
424         dbg(mprintf("(cli=%p) ",cli));
425         if ((local=!memcmp(&cli->gwaddr,mykaddr.a+0,4))) mprintf("local ");
426         mprintf("client ");
427         kaddrdump(&cli->kaddr);
428         if (!local) { mprintf(" via "); addrdump(&cli->gwaddr); }
429         mprintf(", kixport %u, process \"%s\"/%lu, %sed",ntohs(cli->kport),cli->clname,(unsigned long)ntohl(cli->clpid),
430                 (cli->opened?"open":"clos"));
431         mout(LOG_INFO);
432 }
433
434 static void iKIXregister(void)
435 {/* KIXrDDDDDDkpPID_[NAME] */
436 struct cli **clip,*cli;
437 struct sockaddr_in addr;
438 char ost;
439
440         addr.sin_family=AF_INET;
441         if (!(cli=*(clip=getclipk((kaddrt *)(buf+4))))) {
442                 chknew(cli); *clip=cli;
443                 cli->next=NULL;
444                 cli->kaddr=*(kaddrt *)(buf+4);
445                 ost=-1;
446                 }
447         else {
448                 ost=cli->opened;
449                 free(cli->clname);
450                 }
451         cli->gwaddr=remkaddr;
452         memcpy(&cli->kport,buf+10,2);
453         memcpy(&cli->clpid,buf+12,4);
454         chk(cli->clname=malloc(bufl-16+1));
455         memcpy(cli->clname,buf+16,bufl-16);
456         cli->clname[bufl-16]='\0';
457         cli->opened=1;
458         clistate(cli,"iKIXregister%s%s  ",
459                 (ost==1?",socket opened again"             :""),
460                 (ost==0?",socket opened after being closed":""));
461 }
462
463 static void iKIXunregister(void)
464 {
465 struct cli **clip,*cli;
466
467         for (clip=&clis;(cli=*clip);clip=&cli->next) {
468                 if (memcmp(&cli->kaddr,buf+4,6)) continue;
469                 clistate(cli,"iKIXunregister%s",
470                         (!cli->opened?",already closed!":""));
471                 cli->opened=0;
472                 }
473 }
474
475 static void iKIXdata(void)
476 {/* KIXdDDDDDDssssssDKskDATA... */
477 struct cli *tocli;
478 char isbroad=!memcmp(buf+4,&kbroad,6);
479 unsigned char obyte=buf[9];
480
481         sendoffs=9;
482         buf[9]=0x03; /* net data */
483         for (tocli=clis;tocli;tocli=tocli->next) {
484                 if (tocli==cli) continue; /* Do not echo packets back */
485                 if (memcmp(tocli->gwaddr.a+0,mykaddr.a+0,4)) continue;
486                 if (memcmp(&tocli->kport,buf+16,2)) continue;
487                 if (!isbroad &&
488                         (memcmp(tocli->kaddr.a+0,buf+4  ,5) || tocli->kaddr.a[5]!=obyte)) continue;
489                 shipaddr=&tocli->gwaddr;
490                 shipout();
491                 }
492         sendoffs=0;
493         buf[9]=obyte;
494         shipaddr=&remkaddr;
495 }
496
497 static void iKIXforward(void)
498 {
499 int i,j;
500 unsigned remha=(unsigned)ntohl(*(unsigned long *)(remkaddr.a+0));
501
502         for (i=0;i<peerstot;i++) {
503 struct peersrc *ps=peers[i]->src;
504                 for (j=0;j<peers[i]->cnt;ps++,j++)
505                         if (ps->a==(remha&ps->m)) {
506                                 shipaddr=&peers[i]->dst;
507                                 shipout();
508                                 break;
509                                 }
510                 }
511         shipaddr=&remkaddr;
512 }
513
514 static void shippeers(void)
515 {
516 int i;
517
518         for (i=0;i<peerstot;i++) {
519                 shipaddr=&peers[i]->dst;
520                 shipout();
521                 }
522         shipaddr=&remkaddr;
523 }
524
525 static void sigalarm(int signo)
526 {
527         if (!quiet) {
528                 minit();
529                 mprintf("Exiting due to communications inactivity.");
530                 mout(LOG_INFO);
531                 }
532         exit(EXIT_SUCCESS);
533 }
534
535 #ifdef MULTI_LOCAL
536
537 #define KAM_LOCAL_ADDRESS "local_address"
538 #define KAM_INODE         "inode"
539 #define KAM_SOCKET_X      "socket:[%s]"
540
541 static pid_t kaddrmapper(const kaddrt *kap)
542 {
543 FILE *f;
544 char line[LINE_MAX];
545 int procfdlen,findsklen;
546 char c,*s,*s2,*serr,*ixla,*ixin;
547 jmp_buf jmp;
548 char findla[14],findsk[20],cmpsk[sizeof(findsk)];
549 long pidn;
550 const char *kamfailerr;
551 DIR *dirproc=NULL,*dirfd=NULL;
552 struct dirent *direproc,*direfd;
553
554         __NORETURN void kamfail(const char *why)
555         { errout(kamfailerr+(*why!='&'),why+(*why=='&')); mapperfail=1; longjmp(jmp,1); }
556         void cleanup(void)
557         {
558                 if (dirproc) { if (closedir(dirproc)) errout("&closedir(\"" PATH_PROC "\")");                  dirproc=NULL; }
559                 if (dirfd  ) { if (closedir(dirfd  )) errout("&closedir(\"" PATH_PROC_X_FD "\")","<unknown>"); dirfd  =NULL; }
560         }
561         char nextline(void)
562         {
563         char r;
564                 errno=0;
565                 r=!fgets(line,sizeof(line),f);
566                 if (errno) kamfail("&fgets");
567                 return(r);
568         }
569         void walkerinit(void) { c=*(s2=line); }
570         char walker(void)
571         {
572                 *(s=s2)=c;
573                 while (*s!='\n'&&isspace(*s)) s++;
574                 for (s2=s;!isspace(*s2)&&*s2&&*s2!='\n';s2++);
575                 if (!(c=*s2)) kamfail("No EOL NL");
576                 if (c=='\n') return(1);
577                 *s2='\0';
578                 return(0);
579         }
580
581         if (mapperfail) return(-1);
582         if (setjmp(jmp)) {
583                 cleanup();
584                 return(-1);
585                 }
586         kamfailerr="&%s(\"" PATH_PROC_NET_UDP "\",r/o) (multi-local disabled)";
587         if (snprintf(findla,14,"%08lX:%04X",
588                                       *(unsigned long  *)(kap->a+0) , /* !ntohl() is IMHO kernel bug */
589                 (unsigned short)ntohs(*(unsigned short *)(kap->a+4)))!=13)
590                 kamfail("Internal - snprintf()");
591         if (!(f=fopen(PATH_PROC_NET_UDP,"rt"))) kamfail("&fopen");
592         if (nextline()) kamfail("No header line");
593         ixla=NULL; ixin=NULL;
594         walkerinit();
595         for (;;) {
596                 if (walker()) break;
597                 if (!ixla && !strcmp(s,KAM_LOCAL_ADDRESS)) ixla=s;
598                 if (!ixin && !strcmp(s,KAM_INODE        )) ixin=s;
599                 }
600         if (!ixla) kamfail("\"" KAM_LOCAL_ADDRESS "\" not found");
601         if (!ixin) kamfail("\"" KAM_INODE         "\" not found");
602         findsklen=-1;
603         while (!nextline()) {
604                 walkerinit();
605                 while (s2<=ixla || s2<=ixin) {
606                         if (walker()) kamfail("Not enough columns");
607                         if (ixla==s && strcmp(s,findla)) { findsklen=-1; break; }
608                         if (ixin==s) {
609                                 findsklen=snprintf(findsk,sizeof(findsk),KAM_SOCKET_X,s);
610                                 if (findsklen<=0 || findsklen>=sizeof(findsk)-1) kamfail("&\"" KAM_INODE "\" parse error");
611                                 break;
612                                 }
613                         }
614                 if (findsklen>=0) break;
615                 }
616         if (findsklen<0) strcpy(findsk,"<unknown>");
617         else {
618                 kamfailerr="&%s(\"" PATH_PROC "\" scanning) (multi-local disabled)";
619                 if (!(dirproc=opendir(PATH_PROC))) kamfail("&opendir");
620                 while (errno=0,direproc=readdir(dirproc)) {
621 #ifdef DIRENT_HAVE_D_TYPE_WORKS
622                         if (direproc->d_type!=DT_DIR) continue;
623 #endif
624                         pidn=strtol(direproc->d_name,&serr,10);
625                         if (pidn<=0 || pidn>=LONG_MAX || *serr) continue;
626                         procfdlen=snprintf(line,sizeof(line),PATH_PROC_X_FD,direproc->d_name);
627                         if (procfdlen<=0 || procfdlen>=sizeof(line)-5) continue;
628                         errno=0;
629                         if (!(dirfd=opendir(line))) {
630                                 if (errno==EPERM || errno==EACCES) kamfail("&opendir");
631                                 continue;
632                                 }
633                         line[procfdlen]='/';
634                         while ((direfd=readdir(dirfd))) {
635 #ifdef DIRENT_HAVE_D_TYPE_WORKS
636                                 if (direfd->d_type!=DT_LNK) continue;
637 #endif
638                                 if (procfdlen+1+strlen(direfd->d_name)+1>sizeof(line)) continue;
639                                 strcpy(line+procfdlen+1,direfd->d_name);
640                                 if (readlink(line,cmpsk,findsklen+1)!=findsklen) continue;
641                                 if (memcmp(findsk,cmpsk,findsklen)) continue;
642                                 cleanup();
643                                 return(pidn);
644                                 }
645                         closedir(dirfd);
646                         }
647                 if (!direproc && errno) kamfail("&readdir");
648                 }
649 #ifndef QUIET_PIDFAIL
650         if (!quiet) {
651                 minit();
652                 mprintf("Process PID not found for ");
653                 addrdump(kap);
654                 mprintf(" (la=\"%s\", sk=\"%s\")!",findla,findsk);
655                 mout(LOG_NOTICE);
656                 }
657 #endif
658         return(-2);
659 }
660
661 /* MULTI_LOCAL: */
662 #endif
663
664 static void doversion(void)
665 {
666         puts("\
667 I'm KIX, version " VERSION " compiled on \""__DATE__" "__TIME__"\" from \""__FILE__"\" by \""__VERSION__"\"\n\
668 Copyright (C) 1998 Jan Kratochvil <short@ucw.cz>\n\
669 This is free software with ABSOLUTELY NO WARRANTY. See the sources for details:\n\
670 http://atrey.karlin.mff.cuni.cz/~short/sw/kix.c.gz\n\
671 ");
672 }
673
674 const char *longdesc[]={
675 "display this help and exit",
676 "display just version number and exit",
677 "only serious errors are reported",
678 "send all the messages (also) to this file\n\
679 "               "defaults to stdout (\"-\"), multiple occurences allowed",
680 "send all the messages to syslog as the specified facility\n\
681 "               "see openlog(3): \"daemon\", \"user\", \"local0\", ...",
682 "dump all the packets being communicated",
683 #ifdef MULTI_LOCAL
684 "disable support of multiple localhost clients",
685 #endif
686 "don't resolve hostnames",
687 "set such process priority (x<0 runs faster!)\n\
688 "               "value \"-\" will leave the priority untouched",
689 "terminate when such seconds idle, defaults to:\n\
690 "               "infinity(=0) if as standalone, IDLE_TIMEOUT if from inetd",
691 };
692
693 const struct option longopts[]={
694 {"help"   ,0,0,'h'},
695 {"version",0,0,'V'},
696 {"quiet"  ,0,0,'q'},
697 {"logfile",1,0,'l'},
698 {"syslog" ,1,0,'s'},
699 {"debug"  ,0,0,'d'},
700 #ifdef MULTI_LOCAL
701 {"single" ,0,0,'1'},
702 #endif
703 {"numeric",0,0,'n'},
704 {"pri"    ,1,0,'p'},
705 {"timeout",1,0,'t'},
706 };
707
708 static __NORETURN void dohelp(void)
709 {
710 int i;
711
712         doversion();
713         puts("\
714 Syntax: kix [-h] [-V] [-q] [-l <filename>] [-s <facility>]\n\
715             [-d]"
716 #ifdef MULTI_LOCAL
717 " [-1]"
718 #endif
719 " [-n] [-p <pri>] [-t <timeout>]\n\
720             [<to peer>[{:|,}<from peer>[/{netbits|netmask}]]...]...\n\
721 ");
722         for (i=0;i<ARRSIZE(longopts);i++) {
723 const char *cs,*cse;
724                 printf("  -%c,--%-7s  ",longopts[i].val,longopts[i].name);
725                 for (cs=longdesc[i];;cs=cse+1)
726                         if (!(cse=strchr(cs,'\n'))) {
727                                 puts(cs);
728                                 break;
729                                 }
730                         else {
731 char fbuf[32];
732                                 snprintf(fbuf,sizeof(fbuf),"%%.%ds\n%%16s",cse-cs);
733                                 printf(fbuf,cs,"");
734                                 }
735                 }
736   puts("\
737   <to   peer>   host to also send all the packets to\n\
738   <from peer>   forward packets if received from this peer\n\
739                 packets are forwarded to the <to peer>\n\
740                 netmask \"255.255.255.0\" equals netbits \"24\"\n\
741 \n\
742   inetd - For automatic run add the next line to /etc/inetd.conf:\n\
743 4213 dgram udp wait root /usr/bin/kix kix -s daemon PEER_ARGUMENTS\n\
744 ");
745         exit(EXIT_FAILURE);
746 }
747
748 int main(int argc,char **argv)
749 {
750 int i,j,optc,sot;
751 unsigned sotl=sizeof(sot);
752 char localaddr,remremote;
753 struct sockaddr_in anyaddr,remaddr;
754 long idletmout=0;
755 char setidletmout=0;
756
757         assert(sizeof(remkaddr)==6);
758         assert(sizeof(unsigned)==4);
759         pname=*argv;
760         setlinebuf(stdout);
761         setlinebuf(stderr);
762
763         if (gethostname((char *)buf,sizeof(buf))) errout("^&gethostname()");
764         fillhost((char *)buf,&mykaddr);
765
766         while ((optc=getopt_long(argc,argv,"hVql:s:d"
767 #ifdef MULTI_LOCAL
768 "1"
769 #endif
770                         "np:t:",longopts,NULL))!=EOF) switch (optc) {
771                 case 'V': doversion(); exit(EXIT_FAILURE);
772                 case 'q': quiet=1; break;
773                 case 'l': {
774 struct logf *lf;
775                         chknew(lf);
776                         if (!(lf->f=fopen(optarg,"at"))) errout("^&fopen(\"%s\",append)",optarg);
777                         lf->next=NULL;
778                         *logfsp=lf;
779                         logfsp=&lf->next;
780                         break; }
781                 case 's': {
782 int i;
783                         for (i=0;i<ARRSIZE(facilitynames);i++)
784                                 if (!strcasecmp(optarg,facilitynames[i].c_name)) break;
785                         if (i>=ARRSIZE(facilitynames)) errout("^Unknown syslog facility \"%s\"",optarg);
786                         openlog("kix",LOG_PID,facilitynames[i].c_val);
787                         logsyslog=1;
788                         break; }
789                 case 'd': debug=1; break;
790 #ifdef MULTI_LOCAL
791                 case '1': mapperfail=1; break;
792 #endif
793                 case 'n': noresolve=1; break;
794 #define NUMPARSE_ERR "^\"-%c\" number \"%s\" parse error, offending: %s"
795                 case 'p': {
796 char *serr;
797                         if (!strcmp(optarg,"-")) { setprio=LONG_MAX; break; }
798                         setprio=strtol(optarg,&serr,10);
799                         if (setprio<=INT_MIN || setprio  >=INT_MAX || *serr) errout(NUMPARSE_ERR,'p',optarg,serr);
800                         break; }
801                 case 't': {
802 char *serr;
803                         idletmout=strtol(optarg,&serr,10);
804                         if (idletmout<0      || idletmout>=INT_MAX || *serr) errout(NUMPARSE_ERR,'t',optarg,serr);
805                         break; }
806                 default: /* also 'h' */
807                         dohelp();
808                 }
809         logfsdone=1;
810         if ((peerstot=(argc-optind))) {
811                 chk(peers=malloc(sizeof(*peers)*(argc-optind)));
812                 for (i=0;i<peerstot;i++) {
813 char *s,*s2;
814 int comps=0;
815                         chk(s=strdup(argv[optind+i]));
816                         for (s2=s;*s2;s2++) if (*s2==':' || *s2==',') { *s2='\0'; comps++; }
817                         chk(peers[i]=malloc(sizeof(**peers)+sizeof(struct peersrc)*(comps-1)));
818                         fillhost(s,&peers[i]->dst);
819                         if ((peers[i]->cnt=comps)) {
820 struct peersrc *ps=peers[i]->src;
821 unsigned mask;
822 kaddrt fakehost;
823                                 while (comps--) {
824                                         while (*s) s++; s++;
825                                         mask=-1;
826                                         for (s2=s;*s2;s2++) if (*s2=='/') { mask=0; *s2++='\0'; break; }
827                                         fillhost(s,&fakehost);
828                                         ps->a=(unsigned)ntohl(*(unsigned long *)(fakehost.a+0));
829                                         if (!mask) {
830 char *serr;
831 long ll;
832                                                 ll=strtol(s2,&serr,10);
833                                                 if (ll>=0 && ll<=32) while (ll--) mask=(mask>>1U)|(1U<<31U);
834                                                 else {
835                                                         fillhost(s2,&fakehost);
836                                                         mask=(unsigned)ntohl(*(unsigned long *)(fakehost.a+0));
837                                                         }
838                                                 }
839                                         ps->m=mask;
840                                         ps->a&=mask;
841                                         while (mask&(1U<<31U)) mask<<=1U;
842                                         if (mask) errout("^Specified netmask (0x%08X) is invalid",ps->m);
843                                         ps++;
844                                         }
845                                 }
846                         free(s);
847                         }
848                 }
849         assert(peerstot>=0);
850
851         errno=0;
852         getsockopt(INETD_SOCKET,SOL_SOCKET,SO_TYPE,&sot,&sotl);
853         if (errno && errno!=ENOTSOCK) errout("&getsockopt(" TOSTRING(INETD_SOCKET) ",SO_TYPE)");
854         if (!errno && sotl==sizeof(sot) && sot==SOCK_DGRAM) mainudp=INETD_SOCKET;
855         else {
856                 mainudp=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
857                 if (mainudp==-1 || mainudp==INETD_SOCKET) errout("^&socket()");
858
859                 anyaddr.sin_family=AF_INET;
860                 anyaddr.sin_addr.s_addr=htonl(INADDR_ANY);
861                 anyaddr.sin_port=htons(SERVICE_PORT);
862                 if (bind(mainudp,(struct sockaddr *)&anyaddr,sizeof(anyaddr))) errout("^&bind(%d)",SERVICE_PORT);
863                 }
864         if (!setidletmout) idletmout=(mainudp==INETD_SOCKET?IDLE_TIMEOUT:0);
865         if (!quiet) {
866                 minit();
867                 mprintf("KIX v" VERSION " on ");
868                 addrdump(&mykaddr);
869                 mprintf(", pri %d",getpriority(PRIO_PROCESS,0));
870                 }
871         if (setprio!=LONG_MAX) {
872                 if (setpriority(PRIO_PROCESS,0,SETPRIORITY))
873                          { if (!quiet) mprintf(" left - %s",strerror(errno)); }
874                 else if (!quiet) mprintf(" -> %d",getpriority(PRIO_PROCESS,0));
875                 }
876         if (!quiet) {
877 int rtm;
878                 mprintf(", %s, ",(mainudp==INETD_SOCKET?"inetd":"standalone"));
879                 if ((rtm=idletmout)) {
880                         mprintf("timeout is");
881                         if (rtm>=3600) { mprintf(" %dhrs",rtm/3600); rtm%=3600; }
882                         if (rtm>=  60) { mprintf(" %dmin",rtm/  60); rtm%=  60; }
883                         if (rtm>=   1)   mprintf(" %dsec",rtm     );
884                         }
885                 else mprintf("no timeout");
886                 mout(LOG_INFO);
887
888                 if (peerstot) {
889                         noportprint=1;
890                         minit();
891                         mprintf("InterKIX to<from>:");
892                         for (i=0;i<peerstot;i++) {
893 struct peersrc *ps=peers[i]->src;
894                                 mputchar(' ');
895                                 addrdump(&peers[i]->dst);
896                                 for (j=0;j<peers[i]->cnt;ps++,j++) {
897 unsigned mcnt,mask;
898 kaddrt fakehost;
899                                         mputchar(j?',':'<');
900                                         *(unsigned long *)(fakehost.a+0)=(unsigned long)htonl(ps->a);
901                                         addrdump(&fakehost);
902                                         if ((mask=ps->m)==-1) continue;
903                                         for (mcnt=0;mask;mcnt++) mask<<=1U;
904                                         mprintf("/%u",mcnt);
905                                         }
906                                 if (j) mputchar('>');
907                                 }
908                         mout(LOG_INFO);
909                         noportprint=0;
910                         }
911                 }
912         if (setsockopt(mainudp,SOL_SOCKET,SO_BROADCAST,&val_one,sizeof(val_one)))
913                 errout("&setsockopt(mainudp,SO_BROADCAST)");
914         signal(SIGALRM,sigalarm);
915
916         for (;;) {
917 pktfail:
918                 remaddrl=sizeof(remaddr);
919                 alarm(idletmout);
920                 bufl=recvfrom(mainudp,&buf,sizeof(buf),0,(struct sockaddr *)&remaddr,&remaddrl);
921                 alarm(0);
922                 if (bufl==-1) { if (errno!=ECONNREFUSED) errout("&recvfrom: bufl=-1"); goto pktfail; }
923                 if (remaddr.sin_family!=AF_INET)
924                         { errout("recvfrom: sin_family %u!=AF_INET (%u)",remaddr.sin_family,AF_INET); goto pktfail; }
925                 localaddr=(ntohl(remaddr.sin_addr.s_addr)==INADDR_LOOPBACK || !memcmp(&remaddr.sin_addr,mykaddr.a+0,4));
926                 remremote=(ntohs(remaddr.sin_port)==SERVICE_PORT);
927                 if (localaddr && remremote) goto pktfail; /* Talking to yourself, Graham? */
928                 if (!localaddr && !remremote) { errout("Alien packet discarded"); goto pktfail; }
929                 memcpy(remkaddr.a+0,(remremote?(void *)&remaddr.sin_addr:(void *)(mykaddr.a+0)),4);
930                 memcpy(remkaddr.a+4,&remaddr.sin_port,2);
931                 if (debug) pkterr("received.");
932                 if (!bufl) { pkterr("Empty!"); goto pktfail; }
933                 if (bufl>=4 && !memcmp(buf,iKIX,3)) {
934                         if (!remremote) { errout("InterKIX packets not allowed from localhost"); goto pktfail; }
935                         switch (buf[3]) {
936                                 case 'r': /* register: KIXrDDDDDDkpPID_[NAME] */
937                                         if (bufl<16) { pkterr("InterKIX Register len < 16"); goto pktfail; }
938                                         iKIXregister();
939                                         iKIXforward();
940                                         break;
941                                 case 'u': /* unregister: KIXuDDDDDD */
942                                         if (bufl!=10) { pkterr("InterKIX Unregister len != 10"); goto pktfail; }
943                                         iKIXunregister();
944                                         iKIXforward();
945                                         break;
946                                 case 'd': /* data: KIXdDDDDDDssssssDKskDATA... */
947                                         if (bufl <21) { pkterr("InterKIX Data"    " len < 21" ); goto pktfail; }
948                                         cli=NULL;
949                                         iKIXdata();
950                                         iKIXforward();
951                                         break;
952                                 default:
953                                         pkterr("Unrecognized InterKIX command code 0x%02X",buf[3]);
954                                         goto pktfail;
955                                 }
956                         goto pktfail;
957                         }
958                 if (remremote) { errout("LocalKIX packets not allowed from remote"); goto pktfail; }
959                 switch (buf[0]) {
960                         case 0x01: /* open socket */
961                                 if (bufl<7) { pkterr("OpenSocket len < 7!"); goto pktfail; }
962 { unsigned char *us;
963                                 us=memchr(buf+7,0,bufl-7);
964                                 bufl+=-1+10-(us?(buf+bufl)-us:0);
965 }
966                                 memmove(buf+10,buf+1,bufl-10);
967                                 memcpy(buf+4,mykaddr.a+0,4);
968                                 *(unsigned short *)(buf+8)=
969 #ifdef MULTI_LOCAL
970                                                 (mapperfail?0:(*(unsigned short *)(buf+12))^(*(unsigned short *)(buf+14)))
971 #else
972                                                 0
973 #endif
974                                                 ;
975                                 iKIXregister();
976
977                                 if (peerstot) {
978                                         memcpy(buf,iKIX,3); buf[3]='r';
979                                         shippeers();
980                                         }
981
982                                 buf[0]=0x06; /* open response */
983                                 memset(buf+1,0,2); /* ??? g_LastPort */
984                                 bufl=3;
985                                 shipout();
986                                 break;
987                         case 0x02: /* close socket */
988                                 if (bufl<3) { pkterr("Close socket len <3!"); goto pktfail; }
989                                 cli=*getclipgw(&remkaddr);
990                                 if (!cli) { pkterr("Unregister() but before Register()!"); goto pktfail; }
991                                 memcpy(buf+4,&cli->kaddr,6);
992                                 iKIXunregister();
993
994                                 if (peerstot) {
995                                         memcpy(buf,iKIX,3); buf[3]='u';
996                                         bufl=10;
997                                         shippeers();
998                                         }
999
1000                                 buf[0]=0x07; /* close response */
1001                                 memset(buf+1,0,2); /* ??? g_LastPort */
1002                                 bufl=3;
1003                                 shipout();
1004                                 break;
1005                         case 0x03: /* net data: 3ddddddDK??DATA... -> KIXdDDDDDDssssssDKskDATA... */
1006                                 if (bufl<11) { pkterr("Net data len < 11!"); goto pktfail; }
1007                                 if (!(cli=*(clip=getclipgw(&remkaddr))))
1008                                         { pkterr("Got net data before socket open!"); goto pktfail; }
1009                                 memmove(buf+20,buf+11,bufl-1);
1010                                 bufl+=9;
1011                                 memcpy(buf+18,&cli->kport,2); /* sport */
1012                                 memcpy(buf+16,buf+7      ,2); /* dport */
1013                                 memcpy(buf+10,&cli->kaddr,6); /* saddr */
1014                                 memmove(buf+4,buf+1      ,6); /* daddr */
1015                                 memcpy(buf,iKIX,3); buf[3]='d';
1016                                 iKIXdata();
1017                                 if (!memcmp(buf+4,&kbroad,6)) shippeers();
1018                                 else if (memcmp(buf+4,mykaddr.a+0,4)) /* not local */
1019                                         for (cli=clis;cli;cli=cli->next) {
1020                                                 if (memcmp(&cli->kaddr,buf+4,6)) continue;
1021                                                 shipaddr=&cli->gwaddr;
1022                                                 shipout();
1023                                                 }
1024                                 shipaddr=&remkaddr;
1025                                 break;
1026                         case 0x05: /* Get Address */
1027                                 buf[0]=0x04; /* myipxaddress */
1028 #ifdef MULTI_LOCAL
1029 { pid_t rempid; kaddrt mapkaddr;
1030                                 *(unsigned long *)(mapkaddr.a+0)=(unsigned long)htonl(INADDR_LOOPBACK);
1031                                 memcpy(mapkaddr.a+4,remkaddr.a+4,2);
1032                                 if ((rempid=kaddrmapper(&mapkaddr))==-2) {
1033 #ifndef QUIET_PIDFAIL
1034                                         pkterr("GetAddress: no PID!");
1035 #endif
1036                                         goto pktfail; }
1037                                 *(unsigned short *)(buf+5)=(rempid==-1?0:htons(rempid^(rempid>>16)));
1038 #else
1039                                 memset(buf+5,0,2);
1040 #endif
1041                                 memcpy(buf+1,mykaddr.a+0,4);
1042                                 if (!quiet) {
1043                                         minit();
1044                                         mprintf("GetAddress: For ");
1045                                         addrdump(&remkaddr);
1046                                         mprintf(" set ");
1047                                         kaddrdump((kaddrt *)(buf+1));
1048 #ifdef MULTI_LOCAL
1049                                         mprintf(", PID");
1050                                         if (rempid==-1) mprintf(" detection failed");
1051                                         else mprintf("=%d",rempid);
1052 #endif
1053                                         mout(LOG_INFO);
1054                                         }
1055                                 bufl=7;
1056                                 shipout();
1057                                 break;
1058 #ifdef MULTI_LOCAL
1059 }
1060 #endif
1061                         default:
1062                                 pkterr("Unrecognized command code 0x%02X",buf[0]);
1063                                 goto pktfail;
1064                         }
1065                 }
1066         return(EXIT_SUCCESS);
1067 }