1 /*
2  *  A spy program to reveal X11  traffic
3  *
4  *	James Peterson, 1988
5  *
6  * Copyright (C) 1988 MCC
7  *
8  * Permission to use, copy, modify, distribute, and sell this software and its
9  * documentation for any purpose is hereby granted without fee, provided that
10  * the above copyright notice appear in all copies and that both that
11  * copyright notice and this permission notice appear in supporting
12  * documentation, and that the name of MCC not be used in
13  * advertising or publicity pertaining to distribution of the software without
14  * specific, written prior permission.  MCC makes no
15  * representations about the suitability of this software for any purpose.  It
16  * is provided "as is" without express or implied warranty.
17  *
18  * MCC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20  * EVENT SHALL MCC BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
22  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
23  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
24  * PERFORMANCE OF THIS SOFTWARE.
25  *
26  */
27 /*
28  * Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
29  *
30  * Permission is hereby granted, free of charge, to any person obtaining a
31  * copy of this software and associated documentation files (the "Software"),
32  * to deal in the Software without restriction, including without limitation
33  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
34  * and/or sell copies of the Software, and to permit persons to whom the
35  * Software is furnished to do so, subject to the following conditions:
36  *
37  * The above copyright notice and this permission notice (including the next
38  * paragraph) shall be included in all copies or substantial portions of the
39  * Software.
40  *
41  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
44  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
46  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
47  * DEALINGS IN THE SOFTWARE.
48  *
49  */
50 
51 #include "scope.h"
52 #include "nas.h"
53 #include "extensions.h"
54 
55 #include <ctype.h>
56 #include <unistd.h>
57 
58 #include <sys/types.h>          /* needed by sys/socket.h and netinet/in.h */
59 #include <sys/uio.h>            /* for struct iovec, used by socket.h */
60 #include <sys/socket.h>         /* for AF_INET, SOCK_STREAM, ... */
61 #include <sys/ioctl.h>          /* for FIONCLEX, FIONBIO, ... */
62 #if !defined(FIOCLEX) && defined(HAVE_SYS_FILIO_H)
63 #include <sys/filio.h>
64 #endif
65 #include <fcntl.h>
66 #include <netinet/in.h>         /* struct sockaddr_in */
67 #include <netinet/tcp.h>
68 #include <netdb.h>              /* struct servent * and struct hostent * */
69 #include <errno.h>              /* for EINTR, EADDRINUSE, ... */
70 
71 #include <locale.h>
72 #ifdef HAVE_LANGINFO_H
73 #include <langinfo.h>
74 #endif
75 
76 
77 /* ********************************************** */
78 /*                                                */
79 /* ********************************************** */
80 
81 #define DefaultPort 6000
82 
83 char ServerHostName[MAXHOSTNAMELEN];
84 char AudioServerHostName[MAXHOSTNAMELEN];
85 long ServerBasePort = DefaultPort;
86 static long ServerInPort = 1;
87 static long ServerOutPort = 0;
88 static long ServerDisplay = 0;
89 static char *RawFile;
90 
91 static FD ConnectToClient(FD ConnectionSocket);
92 static void DataFromClient(FD fd);
93 static void SetUpStdin(void);
94 static void SetUpRawFile(const char *filename);
95 
96 char TranslateText = 0;
97 char IsUTF8locale = 0;
98 char ScopeEnabled = 1;
99 char HandleSIGUSR1 = 0;
100 char DoAudio = 0;
101 char TerminateClose = 0;
102 int Interrupt = 0;
103 
104 struct FDDescriptor *FDD = NULL;
105 int MaxFD = 0;
106 int nFDsInUse = 0;
107 fd_set ReadDescriptors;
108 fd_set WriteDescriptors;
109 fd_set BlockedReadDescriptors;
110 int HighestFD;
111 
112 short debuglevel = 0;
113 short Verbose = 0;
114 short XVerbose = 0;
115 short NasVerbose = 0;
116 short Raw = 0;
117 int ScopePort = 0;
118 char *ScopeHost = NULL;
119 
120 
121 typedef struct _BreakPoint {
122     struct _BreakPoint *next;
123     int number;
124     unsigned char request;
125     int minorop;
126     Boolean enabled;
127 } BP;
128 
129 typedef enum _CMDResult { CMDProceed, CMDDebug, CMDSyntax, CMDError } CMDResult;
130 
131 static CMDResult CMDStep(int argc, char **argv);
132 static CMDResult CMDCont(int argc, char **argv);
133 static CMDResult CMDBreak(int argc, char **argv);
134 static CMDResult CMDDelete(int argc, char **argv);
135 static CMDResult CMDDisable(int argc, char **argv);
136 static CMDResult CMDEnable(int argc, char **argv);
137 static CMDResult CMDLevel(int argc, char **argv);
138 static CMDResult CMDAudio(int argc, char **argv);
139 static CMDResult CMDQuit(int argc, char **argv) _X_NORETURN;
140 static CMDResult CMDHelp(int argc, char **argv);
141 
142 typedef struct _CMDFunc {
143     const char *name;
144     const char *alias;
145      CMDResult(*func) (int argc, char **argv);
146     const char *usage;
147     const char *help;
148 } CMDFuncRec;
149 typedef const CMDFuncRec *CMDFuncPtr;
150 
151 static const CMDFuncRec CMDFuncs[] = {
152     {"audio", "a", CMDAudio, "[a]udio",
153      "Set audio output level\n"},
154     {"break", "b", CMDBreak, "[b]reak",
155      "Create breakpoints\n"},
156     {"cont", "c", CMDCont, "[c]ont",
157      "Continue scoping\n"},
158     {"delete", "del", CMDDelete, "[del]ete",
159      "Delete breakpoints\n"},
160     {"disable", "d", CMDDisable, "[d]isable",
161      "Disable breakpoints\n"},
162     {"enable", "e", CMDEnable, "[e]nable",
163      "Enable breakpoints\n"},
164     {"help", "?", CMDHelp, "help",
165      "Print this list\n"},
166     {"level", "l", CMDLevel, "[l]evel",
167      "Set output level\n"},
168     {"quit", "q", CMDQuit, "[q]uit",
169      "Quit Xscope\n"},
170     {"step", "s", CMDStep, "[s]tep",
171      "Step trace one request\n"},
172 };
173 
174 #define NumCMDFuncs (sizeof CMDFuncs / sizeof CMDFuncs[0])
175 
176 #ifndef FALSE
177 #define FALSE 0
178 #define TRUE 1
179 #endif
180 
181 static int
CMDStringToInt(char * s,int * v)182 CMDStringToInt(char *s, int *v)
183 {
184     int sign = 1;
185 
186     switch (*s) {
187     case '-':
188         sign = -1;
189         s++;
190         break;
191     case '+':
192         s++;
193         break;
194     }
195 
196     if (!strncmp(s, "0x", 2)) {
197         sscanf(s + 2, "%x", v);
198     }
199     else if (*s == '0') {
200         sscanf(s, "%o", v);
201     }
202     else if (isdigit(*s)) {
203         sscanf(s, "%d", v);
204     }
205     else if (*s == '\'') {
206         s++;
207         if (*s == '\\') {
208             s++;
209             switch (*s) {
210             case 'n':
211                 *v = '\n';
212                 break;
213             case 't':
214                 *v = '\t';
215                 break;
216             default:
217                 *v = (int) *s;
218                 break;
219             }
220         }
221         else
222             *v = (int) *s;
223         s++;
224         if (*s != '\'')
225             return FALSE;
226     }
227     else
228         return FALSE;
229     *v *= sign;
230     return TRUE;
231 }
232 
233 static CMDFuncPtr
CMDStringToFunc(const char * name)234 CMDStringToFunc(const char *name)
235 {
236     int i;
237 
238     for (i = 0; i < NumCMDFuncs; i++) {
239         if (!strcmp(name, CMDFuncs[i].name) || !strcmp(name, CMDFuncs[i].alias)) {
240             return &CMDFuncs[i];
241         }
242     }
243     return NULL;
244 }
245 
246 static int
CMDSplitIntoWords(char * line,char ** argv)247 CMDSplitIntoWords(char *line, char **argv)
248 {
249     char quotechar;
250     int argc;
251 
252     argc = 0;
253     while (*line) {
254         while (isspace(*line))
255             line++;
256         if (!*line)
257             break;
258         if (*line == '"') {
259             quotechar = *line++;
260             *argv++ = line;
261             argc++;
262             while (*line && *line != quotechar)
263                 line++;
264             if (*line)
265                 *line++ = '\0';
266         }
267         else {
268             *argv++ = line;
269             argc++;
270             while (*line && !isspace(*line))
271                 line++;
272             if (*line)
273                 *line++ = '\0';
274         }
275     }
276     *argv = NULL;
277     return argc;
278 }
279 
280 static CMDResult
CMDHelp(int argc,char ** argv)281 CMDHelp(int argc, char **argv)
282 {
283     int i;
284     CMDFuncPtr func;
285 
286     if (argc == 1) {
287         for (i = 0; i < NumCMDFuncs; i++)
288             printf("%-10s%s\n", CMDFuncs[i].name, CMDFuncs[i].usage);
289     }
290     else {
291         for (i = 1; i < argc; i++) {
292             func = CMDStringToFunc(argv[i]);
293             if (!func) {
294                 printf("%-10s unknown command\n", argv[i]);
295                 return CMDSyntax;
296             }
297             printf("%-10s %s\n%s", func->name, func->usage, func->help);
298         }
299     }
300     return CMDDebug;
301 }
302 
303 static void
CMDSyntaxError(int argc,char ** argv)304 CMDSyntaxError(int argc, char **argv)
305 {
306     printf("Syntax error in:");
307     while (*argv)
308         printf(" %s", *argv++);
309     printf("\n");
310 }
311 
312 void
ReadCommands(void)313 ReadCommands(void)
314 {
315     int argc;
316     char line[1024];
317     char *argv[20];
318     CMDResult result;
319     CMDFuncPtr func;
320     static int here;
321 
322     if (here)
323         return;
324     here = 1;
325     for (;;) {
326         printf("> ");
327         if (!fgets(line, sizeof line, stdin)) {
328             if (feof(stdin)) {
329                 strncpy(line, "quit", sizeof(line));
330             }
331             else {
332                 printf("Error: %s\n", strerror(errno));
333                 break;
334             }
335         }
336         argc = CMDSplitIntoWords(line, argv);
337         if (argc > 0) {
338             func = CMDStringToFunc(argv[0]);
339             if (!func)
340                 CMDSyntaxError(argc, argv);
341             else {
342                 result = (*func->func) (argc, argv);
343                 switch (result) {
344                 case CMDSyntax:
345                     CMDSyntaxError(argc, argv);
346                     break;
347                 case CMDError:
348                     printf("Error\n");
349                     break;
350                 case CMDProceed:
351                     here = 0;
352                     return;
353                 default:
354                     break;
355                 }
356             }
357         }
358     }
359     printf("...\n");
360     here = 0;
361 }
362 
363 int SingleStep;
364 int BreakPoint;
365 BP *breakPoints;
366 int breakPointNumber;
367 
368 void
TestBreakPoints(const unsigned char * buf,long n)369 TestBreakPoints(const unsigned char *buf, long n)
370 {
371     BP *bp;
372 
373     for (bp = breakPoints; bp; bp = bp->next) {
374         if (bp->enabled) {
375             if (bp->request == buf[0]) {
376                 if (bp->request < EXTENSION_MIN_REQ)    /* Core protocol, not extension */
377                     break;
378                 else if ((bp->minorop == -1) || (bp->minorop == buf[1]))
379                     /* extension, either matching minor opcode or all minor opcodes */
380                     break;
381             }
382         }
383     }
384     if (bp) {
385         if (bp->request < EXTENSION_MIN_REQ)
386             printf("Breakpoint on request %d\n", bp->request);
387         else if (bp->minorop == -1)
388             printf("Breakpoint on extension %d\n", bp->request);
389         else
390             printf("Breakpoint on extension %d, minor request %d\n",
391                    bp->request, bp->minorop);
392         ReadCommands();
393     }
394 }
395 
396 static void
setBreakPoint(void)397 setBreakPoint(void)
398 {
399     Boolean b = false;
400     BP *bp;
401     FD fd;
402 
403     if (SingleStep)
404         b = true;
405     else {
406         for (bp = breakPoints; bp; bp = bp->next) {
407             if (bp->enabled) {
408                 b = true;
409                 break;
410             }
411         }
412     }
413     if (b != BreakPoint) {
414         BreakPoint = b;
415         for (fd = 0; fd < HighestFD; fd++) {
416             if (FDD[fd].Busy && FDD[fd].InputHandler == DataFromClient) {
417                 if (BreakPoint)
418                     SetBufLimit(fd);
419                 else
420                     ClearBufLimit(fd);
421             }
422         }
423     }
424 }
425 
426 static CMDResult
CMDBreak(int argc,char ** argv)427 CMDBreak(int argc, char **argv)
428 {
429     BP *bp, **prev;
430     int request, minorop;
431     char *minorname;
432 
433     if (argc == 1) {
434         for (bp = breakPoints; bp; bp = bp->next) {
435             printf("%3d: %s %3d ", bp->number,
436                    bp->enabled ? "enabled" : "disabled", bp->request);
437             PrintENUMERATED(&bp->request, 1, TD[REQUEST].ValueList);
438             if (bp->minorop != -1)
439                 printf(":%d", bp->minorop);
440             printf("\n");
441         }
442     }
443     else {
444         for (prev = &breakPoints; *prev; prev = &(*prev)->next);
445         while (*++argv) {
446             if ((minorname = strchr(*argv, ':'))) {
447                 *minorname = '\0';
448                 if (!CMDStringToInt(minorname + 1, &minorop) ||
449                     (minorop < 0) || (minorop > 255)) {
450                     *minorname = ':';   /* restore string for error message */
451                     return CMDSyntax;
452                 }
453             }
454             else {
455                 minorop = -1;
456             }
457             request = GetXRequestFromName(*argv);
458             if (request < 0 && !CMDStringToInt(*argv, &request))
459                 return CMDSyntax;
460             if ((request < EXTENSION_MIN_REQ) && (minorname != NULL)) {
461                 *minorname = ':';       /* restore string for error message */
462                 return CMDSyntax;
463             }
464             bp = malloc(sizeof(BP));
465             bp->number = ++breakPointNumber;
466             bp->request = request;
467             bp->minorop = minorop;
468             bp->enabled = true;
469             bp->next = NULL;
470             *prev = bp;
471             prev = &bp->next;
472         }
473     }
474     setBreakPoint();
475     return CMDDebug;
476 }
477 
478 static CMDResult
CMDCont(int argc,char ** argv)479 CMDCont(int argc, char **argv)
480 {
481     SingleStep = 0;
482     return CMDProceed;
483 }
484 
485 static CMDResult
CMDDisable(int argc,char ** argv)486 CMDDisable(int argc, char **argv)
487 {
488     BP *bp;
489     int number;
490 
491     if (argc == 1) {
492         printf("Disabling all breakpoints...\n");
493         for (bp = breakPoints; bp; bp = bp->next)
494             bp->enabled = false;
495     }
496     else {
497         while (*++argv) {
498             if (!CMDStringToInt(*argv, &number))
499                 return CMDSyntax;
500             for (bp = breakPoints; bp; bp = bp->next)
501                 if (bp->number == number) {
502                     bp->enabled = false;
503                     break;
504                 }
505             if (!bp) {
506                 printf("No such breakpoint %s\n", *argv);
507             }
508         }
509     }
510     setBreakPoint();
511     return CMDDebug;
512 }
513 
514 static CMDResult
CMDDelete(int argc,char ** argv)515 CMDDelete(int argc, char **argv)
516 {
517     BP *bp, **prev;
518     int number;
519 
520     if (argc == 1) {
521         printf("Deleting all breakpoints...\n");
522         while ((bp = breakPoints) != NULL) {
523             breakPoints = bp->next;
524             free(bp);
525         }
526     }
527     else {
528         while (*++argv) {
529             if (!CMDStringToInt(*argv, &number))
530                 return CMDSyntax;
531             for (prev = &breakPoints; (bp = *prev) != NULL; prev = &bp->next) {
532                 if (bp->number == number) {
533                     *prev = bp->next;
534                     free(bp);
535                     break;
536                 }
537             }
538             if (!bp) {
539                 printf("No such breakpoint %s\n", *argv);
540             }
541         }
542     }
543     setBreakPoint();
544     return CMDDebug;
545 }
546 
547 static CMDResult
CMDEnable(int argc,char ** argv)548 CMDEnable(int argc, char **argv)
549 {
550     BP *bp;
551     int number;
552 
553     if (argc == 1) {
554         printf("Enabling all breakpoints...\n");
555         for (bp = breakPoints; bp; bp = bp->next)
556             bp->enabled = true;
557     }
558     else {
559         while (*++argv) {
560             if (!CMDStringToInt(*argv, &number))
561                 return CMDSyntax;
562             for (bp = breakPoints; bp; bp = bp->next)
563                 if (bp->number == number) {
564                     bp->enabled = true;
565                     break;
566                 }
567             if (!bp) {
568                 printf("No such breakpoint %s\n", *argv);
569             }
570         }
571     }
572     setBreakPoint();
573     return CMDDebug;
574 }
575 
576 static CMDResult
CMDStep(int argc,char ** argv)577 CMDStep(int argc, char **argv)
578 {
579     SingleStep = 1;
580     setBreakPoint();
581     return CMDProceed;
582 }
583 
584 #ifdef __SUNPRO_C
585 /* prevent "Function has no return statement" error for CMDQuit */
586 #pragma does_not_return(exit)
587 #endif
588 
589 static CMDResult
CMDQuit(int argc,char ** argv)590 CMDQuit(int argc, char **argv)
591 {
592     printf("exiting...\n");
593     exit(0);
594 }
595 
596 static CMDResult
CMDLevel(int argc,char ** argv)597 CMDLevel(int argc, char **argv)
598 {
599     int level;
600 
601     if (argc == 1)
602         printf("Level: %d\n", XVerbose);
603     else if (argc == 2 && CMDStringToInt(argv[1], &level))
604         XVerbose = level;
605     else
606         return CMDSyntax;
607     return CMDDebug;
608 }
609 
610 static CMDResult
CMDAudio(int argc,char ** argv)611 CMDAudio(int argc, char **argv)
612 {
613     int level;
614 
615     if (argc == 1)
616         printf("Audio Level: %d\n", NasVerbose);
617     else if (argc == 2 && CMDStringToInt(argv[1], &level))
618         NasVerbose = level;
619     else
620         return CMDSyntax;
621     return CMDDebug;
622 }
623 
624 /* ********************************************** */
625 /*                                                */
626 /*                                                */
627 /* ********************************************** */
628 
629 short
GetServerport(void)630 GetServerport(void)
631 {
632     short port;
633 
634     enterprocedure("GetServerport");
635 
636     port = ServerBasePort + ServerOutPort + ServerDisplay;
637     debug(4, (stderr, "Server service is on port %d\n", port));
638     return (port);
639 }
640 
641 static short
GetScopePort(void)642 GetScopePort(void)
643 {
644     short port;
645 
646     enterprocedure("GetScopePort");
647 
648     port = ServerBasePort + ServerInPort + ServerDisplay;
649     debug(4, (stderr, "scope service is on port %d\n", port));
650     return (port);
651 }
652 
653 /* ********************************************** */
654 /*                                                */
655 /* ********************************************** */
656 
657 static void _X_NORETURN
Usage(void)658 Usage(void)
659 {
660     fprintf(stderr,
661             "Usage: xscope\n"
662             "              [-h<server-host>]\n"
663             "              [-i<in-port>]\n"
664             "              [-o<out-port>]\n"
665             "              [-d<display-number>]\n"
666             "              [-v<n>]  -- verbose output\n"
667             "              [-r]  -- raw output\n"
668             "              [-a<n>]  -- audio verbose output\n"
669             "              [-q]  -- quiet output\n"
670             "              [-D<debug-level>]\n"
671             "              [-I]  -- start in interactive mode\n"
672             "              [-S<n>] -- start/stop on SIGUSR1\n"
673             "              [-V]  -- output version information and exit\n"
674             "              [-t]  -- terminate when all clients close\n");
675     exit(1);
676 }
677 
678 static void
InitializeLocale(void)679 InitializeLocale(void)
680 {
681     setlocale(LC_CTYPE, "");
682 
683 #ifdef HAVE_LANGINFO_H
684     {
685         const char *charmap = nl_langinfo (CODESET);
686 
687         if (charmap != NULL && strcmp(charmap, "UTF-8") == 0)
688             IsUTF8locale = 1;
689     }
690 #endif
691 }
692 
693 static void
ScanArgs(int argc,char ** argv)694 ScanArgs(int argc, char **argv)
695 {
696     XVerbose = 1; /* default verbose-ness level */
697     NasVerbose = 1;
698     Raw = 0;
699 
700     /* Scan argument list */
701     while (--argc > 0) {
702         ++argv;
703         if (**argv == '-')
704             switch (*++*argv) {
705                 /*
706                    debug levels:
707                    2 - trace each procedure entry
708                    4 - I/O, connections
709                    8 - Scope internals
710                    16 - Message protocol
711                    32 - 64 - was malloc, now unused
712                    128 - 256 - really low level
713                  */
714             case 'D':
715                 debuglevel = atoi(++*argv);
716                 if (debuglevel == 0)
717                     debuglevel = 255;
718                 XVerbose = 7;
719                 debug(1, (stderr, "debuglevel = %d\n", debuglevel));
720                 break;
721 
722             case 'I':
723                 Interrupt = 1;
724                 break;
725 
726             case 'S':
727                 HandleSIGUSR1 = 1;
728                 ScopeEnabled = atoi(++*argv);
729                 break;
730 
731             case 'V':          /* print version & exit */
732                 printf("%s\n", PACKAGE_STRING);
733                 exit(0);
734 
735             case 'q':          /* quiet mode */
736                 XVerbose = 0;
737                 debug(1, (stderr, "Verbose = %d\n", XVerbose));
738                 break;
739 
740             case 'r':          /* raw mode */
741                 Raw = 1;
742                 debug(1, (stderr, "Raw = %d\n", Raw));
743                 break;
744 
745             case 'v':          /* verbose mode */
746                 XVerbose = atoi(++*argv);
747                 debug(1, (stderr, "Verbose = %d\n", XVerbose));
748                 break;
749 
750             case 'o':
751                 ServerOutPort = atoi(++*argv);
752                 if (ServerOutPort <= 0)
753                     ServerOutPort = 0;
754                 debug(1, (stderr, "ServerOutPort = %ld\n", ServerOutPort));
755                 break;
756 
757             case 'd':
758                 ServerDisplay = atoi(++*argv);
759                 if (ServerDisplay <= 0)
760                     ServerDisplay = 0;
761                 debug(1, (stderr, "ServerDisplay = %ld\n", ServerDisplay));
762                 break;
763 
764             case 'i':
765                 ServerInPort = atoi(++*argv);
766                 if (ServerInPort <= 0)
767                     ServerInPort = 0;
768                 debug(1, (stderr, "ServerInPort = %ld\n", ServerInPort));
769                 break;
770 
771             case 'h':
772                 if (++*argv != NULL && **argv != '\0'
773                     && (strlen(*argv) < sizeof(ServerHostName)))
774                     strcpy(ServerHostName, *argv);
775                 debug(1, (stderr, "ServerHostName = %s\n", ServerHostName));
776                 break;
777 
778             case 'f':
779                 if (++*argv != NULL && **argv != '\0')
780                     RawFile = *argv;
781                 else if (argv[1] != NULL) {
782                     RawFile = argv[1];
783                     argv++; argc--;
784                 }
785                 debug(1, (stderr, "RawFile = %s\n", RawFile));
786                 break;
787 
788             case 'T':
789                 TranslateText = 1;
790                 break;
791 
792             case 'A':
793                 DoAudio = 1;
794                 break;
795 
796             case 'a':          /* verbose mode */
797                 NasVerbose = atoi(++*argv);
798                 debug(1, (stderr, "NasVerbose = %d\n", NasVerbose));
799                 break;
800 
801             case 'n':          /* NAS server host */
802                 if (++*argv != NULL && **argv != '\0')
803                     (void) strcpy(AudioServerHostName, *argv);
804                 debug(1, (stderr, "AudioServerHostName = %s\n",
805                           AudioServerHostName));
806                 break;
807             case 't':
808                 TerminateClose = 1;
809                 break;
810 
811             default:
812                 fprintf(stderr, "Unknown option %c\n", **argv);
813                 Usage();
814                 break;
815 
816             }
817         else {
818             /* file argument to scope -- error */
819             Usage();
820         }
821     }
822 
823     /* check for different port numbers or different machines */
824     if (ServerInPort == ServerOutPort)
825         if (ServerHostName[0] == '\0') {
826             fprintf(stderr, "Can't have xscope on same port as server (%ld)\n",
827                     ServerInPort);
828             Usage();
829         }
830 
831 }
832 
833 
834 /* ********************************************** */
835 /*                                                */
836 /* ********************************************** */
837 
838 int
main(int argc,char ** argv)839 main(int argc, char **argv)
840 {
841     InitializeLocale();
842     ScanArgs(argc, argv);
843     InitializeFD();
844     InitializeX11();
845     if (DoAudio)
846         InitializeAudio();
847     SetUpStdin();
848     if (RawFile) {
849         SetUpRawFile(RawFile);
850     } else {
851         SetUpConnectionSocket(GetScopePort(), NewConnection);
852         if (DoAudio)
853             SetUpConnectionSocket(GetScopePort() + 2000, NewAudio);
854     }
855     SetSignalHandling();
856 
857     return MainLoop();
858 }
859 
860 void
TimerExpired(void)861 TimerExpired(void)
862 {
863     debug(16, (stderr, "Timer tick\n"));
864 }
865 
866 /* ********************************************** */
867 /*                                                */
868 /* ********************************************** */
869 
870 /*
871   here is where we would add code to allow control from
872   the keyboard.  We would want to read a command and
873   interpret it.  Possibilties:
874 
875   (a) verbose level setting
876   (b) reset time
877   (c) save X requests to a file.
878   (d) replay X requests from a file.
879   (e) allow fake events, errors to be generated.
880 */
881 
882 static void
ReadStdin(FD fd)883 ReadStdin(FD fd)
884 {
885     char buf[2048];
886     long n;
887 
888     enterprocedure("ReadStdin");
889     n = read(fd, buf, 2048);
890     debug(4, (stderr, "read %ld bytes from stdin\n", n));
891 }
892 
893 static void
SetUpStdin(void)894 SetUpStdin(void)
895 {
896     enterprocedure("SetUpStdin");
897     UsingFD(fileno(stdin), ReadStdin, NULL, NULL);
898 }
899 
900 /* ************************************************************ */
901 /*								*/
902 /*								*/
903 /* ************************************************************ */
904 
905 /*
906   xscope is really meant to look at one client at a time.  However,
907   it can easily handle multiple clients and servers.  To do so,
908   we need to have a pair of FDs: one for the client and one for the
909   server for that client.  If either goes away, so does the other.
910   We need to be able to identify the other FD of a pair, so that if
911   we get input from one, we can write it to the other.
912 */
913 
914 static long clientNumber = 0;
915 struct fdinfo *FDinfo;
916 
917 void
SetUpPair(FD client,FD server)918 SetUpPair(FD client, FD server)
919 {
920     if (client >= 0) {
921         clientNumber += 1;
922         FDinfo[client].Server = false;
923         FDinfo[client].pair = server;
924         FDinfo[client].ClientNumber = clientNumber;
925         if (FDinfo[client].buffer == NULL) {
926             FDinfo[client].buffer = calloc(1, BUFFER_SIZE);
927             if (FDinfo[client].buffer == NULL)
928                 panic("unable to allocate client buffer");
929         }
930         FDinfo[client].bufcount = 0;
931         FDinfo[client].buflimit = -1;
932         FDinfo[client].bufdelivered = 0;
933         if (server >= 0) {
934             FDinfo[server].Server = true;
935             FDinfo[server].pair = client;
936             FDinfo[server].ClientNumber = FDinfo[client].ClientNumber;
937             if (FDinfo[server].buffer == NULL) {
938                 FDinfo[server].buffer = calloc(1, BUFFER_SIZE);
939                 if (FDinfo[server].buffer == NULL)
940                     panic("unable to allocate server buffer");
941             }
942             FDinfo[server].bufcount = 0;
943             FDinfo[server].buflimit = -1;
944             FDinfo[server].bufdelivered = 0;
945         }
946     }
947     else if (server >= 0) {
948         close(server);
949         NotUsingFD(server);
950     }
951 }
952 
953 static void
ResetPair(FD client,FD server)954 ResetPair(FD client, FD server)
955 {
956     if (client >= 0) {
957         free(FDinfo[client].buffer);
958         FDinfo[client].buffer = NULL;
959         FDinfo[client].bufcount = 0;
960         FDinfo[client].buflimit = -1;
961         FDinfo[client].bufdelivered = 0;
962     }
963     if (server >= 0) {
964         free(FDinfo[server].buffer);
965         FDinfo[server].buffer = NULL;
966         FDinfo[server].bufcount = 0;
967         FDinfo[server].buflimit = -1;
968         FDinfo[server].bufdelivered = 0;
969     }
970 }
971 
972 static void
CloseConnection(FD fd)973 CloseConnection(FD fd)
974 {
975     debug(4, (stderr, "close %d and %d\n", fd, FDPair(fd)));
976     ResetPair(ClientHalf(fd), ServerHalf(fd));
977     StopClientConnection(ServerHalf(fd));
978     StopServerConnection(ClientHalf(fd));
979 
980     CloseFD(fd);
981     CloseFD(FDPair(fd));
982 
983     if (TerminateClose)
984         exit(0);
985 }
986 
987 /* ************************************************************ */
988 
989 FD
FDPair(FD fd)990 FDPair(FD fd)
991 {
992     return (FDinfo[fd].pair);
993 }
994 
995 FD
ClientHalf(FD fd)996 ClientHalf(FD fd)
997 {
998     if (FDinfo[fd].Server)
999         return (FDinfo[fd].pair);
1000     return (fd);
1001 }
1002 
1003 FD
ServerHalf(FD fd)1004 ServerHalf(FD fd)
1005 {
1006     if (FDinfo[fd].Server)
1007         return (fd);
1008     return (FDinfo[fd].pair);
1009 }
1010 
1011 const char *
ClientName(FD fd)1012 ClientName(FD fd)
1013 {
1014     static char name[12];
1015 
1016     if (clientNumber <= 1)
1017         return ("");
1018     snprintf(name, sizeof(name), " %d", FDinfo[fd].ClientNumber);
1019     return (name);
1020 }
1021 
1022 int
ClientNumber(FD fd)1023 ClientNumber(FD fd)
1024 {
1025     return FDinfo[fd].ClientNumber;
1026 }
1027 
1028 /* ********************************************** */
1029 /*                                                */
1030 /* ********************************************** */
1031 
1032 /*
1033  * Write as much of the queued data as the receiver will accept
1034  * Block reads from the sender until the receiver gets all of the
1035  * data
1036  */
1037 void
FlushFD(FD fd)1038 FlushFD(FD fd)
1039 {
1040     long BytesToWrite = FDinfo[fd].bufcount - FDinfo[fd].bufstart;
1041     unsigned char *p = FDinfo[fd].buffer + FDinfo[fd].bufstart;
1042     int PeerFD;
1043 
1044     PeerFD = FDPair(fd);
1045     if (FDinfo[fd].buflimit >= 0) {
1046         if (FDinfo[fd].bufdelivered + BytesToWrite > FDinfo[fd].buflimit)
1047             BytesToWrite = FDinfo[fd].buflimit - FDinfo[fd].bufdelivered;
1048     }
1049     while (BytesToWrite > 0) {
1050         int n1 = write(fd, (char *) p, (int) BytesToWrite);
1051 
1052         debug(4, (stderr, "write %d bytes\n", n1));
1053         if (n1 < 0) {
1054             if (errno != EWOULDBLOCK && errno != EAGAIN) {
1055                 perror("Error on write");
1056                 if (PeerFD >= 0)
1057                     CloseConnection(PeerFD);
1058                 BytesToWrite = 0;
1059             }
1060             break;
1061         }
1062         else {
1063             FDinfo[fd].bufstart += n1;
1064             FDinfo[fd].bufdelivered += n1;
1065             BytesToWrite -= n1;
1066             p += n1;
1067         }
1068     }
1069     if (FDinfo[fd].bufstart == FDinfo[fd].bufcount) {
1070         if (PeerFD >= 0) {
1071             FD_CLR(PeerFD, &BlockedReadDescriptors);
1072         }
1073         FD_CLR(fd, &WriteDescriptors);
1074         FDinfo[fd].bufcount = FDinfo[fd].bufstart = 0;
1075     }
1076     else {
1077         if (PeerFD >= 0) {
1078             FD_SET(PeerFD, &BlockedReadDescriptors);
1079         }
1080         if (FDinfo[fd].buflimit != FDinfo[fd].bufdelivered) {
1081             FD_SET(fd, &WriteDescriptors);
1082         }
1083     }
1084 }
1085 
1086 /* when we get data from a client, we read it in, copy it to the
1087    server for this client, then dump it to the client. Note, we don't
1088    have to have a server, if there isn't one. */
1089 
1090 static void
DataFromClient(FD fd)1091 DataFromClient(FD fd)
1092 {
1093     long n;
1094     FD ServerFD;
1095 
1096     Verbose = XVerbose;
1097     enterprocedure("DataFromClient");
1098     ServerFD = FDPair(fd);
1099     if (ServerFD < 0) {
1100         ServerFD = ConnectToServer(false);
1101         if (ServerFD < 0) {
1102             CloseConnection(fd);
1103             return;
1104         }
1105         SetUpPair(fd, ServerFD);
1106     }
1107 
1108     n = read(fd, FDinfo[ServerFD].buffer, BUFFER_SIZE);
1109     debug(4, (stderr, "read %ld bytes from Client%s\n", n, ClientName(fd)));
1110     if (n < 0) {
1111         PrintTime();
1112         perror("Client --> read error:");
1113         CloseConnection(fd);
1114         return;
1115     }
1116     if (n == 0) {
1117         PrintTime();
1118         if (Verbose >= 0)
1119             fprintf(stdout, "Client%s --> EOF\n", ClientName(fd));
1120         CloseConnection(fd);
1121         return;
1122     }
1123 
1124     FDinfo[ServerFD].bufcount = n;
1125     FDinfo[ServerFD].bufstart = 0;
1126 
1127     FlushFD(ServerFD);
1128     /* also report the bytes to standard out */
1129     ReportFromClient(fd, FDinfo[ServerFD].buffer, n);
1130 }
1131 
1132 /* ********************************************** */
1133 /*                                                */
1134 /* ********************************************** */
1135 
1136 /* similar situation for the server, but note that if there is no client,
1137    we close the connection down -- don't need a server with no client. */
1138 
1139 static void
DataFromServer(FD fd)1140 DataFromServer(FD fd)
1141 {
1142     long n;
1143     FD ClientFD;
1144 
1145     Verbose = XVerbose;
1146     ClientFD = FDPair(fd);
1147     if (ClientFD < 0) {
1148         CloseConnection(fd);
1149         return;
1150     }
1151 
1152     enterprocedure("DataFromServer");
1153     n = read(fd, (char *) FDinfo[ClientFD].buffer, BUFFER_SIZE);
1154     debug(4, (stderr, "read %ld bytes from Server%s\n", n, ClientName(fd)));
1155     if (n < 0) {
1156         PrintTime();
1157         perror("read error <- Server");
1158         CloseConnection(fd);
1159         return;
1160     }
1161     if (n == 0) {
1162         PrintTime();
1163         if (Verbose >= 0)
1164             fprintf(stdout, "EOF <-- Server%s\n", ClientName(fd));
1165         CloseConnection(fd);
1166         return;
1167     }
1168 
1169     FDinfo[ClientFD].bufcount = n;
1170     FDinfo[ClientFD].bufstart = 0;
1171     FlushFD(ClientFD);
1172 
1173     /* also report the bytes to standard out */
1174     ReportFromServer(fd, FDinfo[ClientFD].buffer, n);
1175 }
1176 
1177 
1178 /* ************************************************************ */
1179 /*								*/
1180 /*     Create New Connection to a client program and to Server  */
1181 /*								*/
1182 /* ************************************************************ */
1183 
1184 void
NewConnection(FD fd)1185 NewConnection(FD fd)
1186 {
1187     FD ClientFD = ConnectToClient(fd);
1188     FD ServerFD = ConnectToServer(true);
1189 
1190     SetUpPair(ClientFD, ServerFD);
1191 }
1192 
1193 
1194 /* ************************************************************ */
1195 
1196 static FD
ConnectToClient(FD ConnectionSocket)1197 ConnectToClient(FD ConnectionSocket)
1198 {
1199     FD ClientFD;
1200     XtransConnInfo trans_conn = NULL;
1201 
1202 #ifdef USE_XTRANS
1203     XtransConnInfo listen_conn;
1204     int status;
1205 #else
1206     struct sockaddr_in from;
1207     int len = sizeof(from);
1208     int ON = 1; /* used in ioctl */
1209 #endif
1210 
1211     enterprocedure("ConnectToClient");
1212 
1213 #ifdef USE_XTRANS
1214     listen_conn = GetXTransConnInfo(ConnectionSocket);
1215     if ((trans_conn = _X11TransAccept(listen_conn, &status)) == NULL) {
1216         debug(4, (stderr, "Failed to accept connection\n"));
1217         return -1;
1218     }
1219     _X11TransSetOption(trans_conn, TRANS_NONBLOCKING, 1);
1220     ClientFD = _X11TransGetConnectionNumber(trans_conn);
1221 #else
1222     ClientFD = AcceptConnection(ConnectionSocket);
1223 #endif
1224     debug(4, (stderr, "Connect To Client: FD %d\n", ClientFD));
1225     if (ClientFD < 0 && errno == EWOULDBLOCK) {
1226         debug(4, (stderr, "Almost blocked accepting FD %d\n", ClientFD));
1227         panic("Can't connect to Client");
1228     }
1229     if (ClientFD < 0) {
1230         debug(4, (stderr, "NewConnection: error %d\n", errno));
1231         panic("Can't connect to Client");
1232     }
1233 
1234     UsingFD(ClientFD, DataFromClient, FlushFD, trans_conn);
1235 #ifndef USE_XTRANS
1236     ioctl(ClientFD, FIOCLEX, 0);
1237     ioctl(ClientFD, FIONBIO, &ON);
1238 #endif
1239     StartClientConnection(ClientFD);
1240     return (ClientFD);
1241 }
1242 
1243 /* ************************************************************ */
1244 /*								*/
1245 /*								*/
1246 /* ************************************************************ */
1247 
1248 
1249 FD
ConnectToServer(Boolean report)1250 ConnectToServer(Boolean report)
1251 {
1252     FD ServerFD;
1253     XtransConnInfo trans_conn = NULL;   /* transport connection object */
1254     short port;
1255 
1256     enterprocedure("ConnectToServer");
1257 
1258     port = GetServerport();
1259 
1260     if (port == ScopePort &&
1261         ((ServerHostName[0] == '\0') || strcmp(ServerHostName, ScopeHost) == 0))
1262     {
1263         char error_message[100];
1264 
1265         snprintf(error_message, sizeof(error_message),
1266                  "Trying to attach to myself: %s,%d\n", ServerHostName, port);
1267         panic(error_message);
1268     }
1269 
1270     ServerFD = MakeConnection(ServerHostName, port, report, &trans_conn);
1271 
1272     debug(4, (stderr, "Connect To Server: FD %d\n", ServerFD));
1273     if (ServerFD >= 0) {
1274         UsingFD(ServerFD, DataFromServer, FlushFD, trans_conn);
1275         StartServerConnection(ServerFD);
1276     }
1277     return (ServerFD);
1278 }
1279 
1280 
1281 /* ************************************************************ */
1282 /*                                                              */
1283 /* Read & decode data from a pre-recorded raw file (-r -v0)     */
1284 /*                                                              */
1285 /* ************************************************************ */
1286 
1287 static FILE *raw;
1288 static FD *fdMap;	/* map of fds from file to current runtime */
1289 static int fdMapSize;
1290 
1291 static void
SetUpRawClient(FD * returnClientFD,FD * returnServerFD)1292 SetUpRawClient(FD *returnClientFD, FD *returnServerFD)
1293 {
1294     FD ClientFD, ServerFD;
1295 
1296     /* need to fake a pair of fd's */
1297     ClientFD = open("/dev/null", O_RDONLY);
1298     if (ClientFD < 1) {
1299         perror("/dev/null");
1300         panic("Can't open /dev/null for reading");
1301     }
1302     ServerFD = dup(ClientFD);
1303     if (ServerFD < 1) {
1304         perror("ClientFD");
1305         panic("Can't dup ClientFD for reading");
1306     }
1307     UsingFD(ClientFD, NULL, NULL, NULL);
1308     UsingFD(ServerFD, NULL, NULL, NULL);
1309     SetUpPair(ClientFD, ServerFD);
1310     StartClientConnection(ClientFD);
1311     StartServerConnection(ServerFD);
1312     *returnClientFD = ClientFD;
1313     *returnServerFD = ServerFD;
1314 }
1315 
1316 static inline FD
GetRawFDMap(FD origFD)1317 GetRawFDMap(FD origFD)
1318 {
1319     if (origFD >= fdMapSize)
1320         return -1;
1321     return fdMap[origFD];
1322 }
1323 
1324 static void
SetRawFDMap(FD origFD,FD currentFD)1325 SetRawFDMap(FD origFD, FD currentFD)
1326 {
1327     if (origFD >= fdMapSize) {
1328         int newSize = fdMapSize + 32;
1329         if (origFD >= newSize)
1330             newSize = origFD;
1331         fdMap = realloc(fdMap, newSize);
1332         if (fdMap == NULL)
1333             panic("Can't allocate memory for fdMap");
1334         memset(fdMap + fdMapSize, -1, newSize - fdMapSize);
1335         fdMapSize = newSize;
1336     }
1337     fdMap[origFD] = currentFD;
1338     debug(16, (stderr, "FD Map %d => %d\n", origFD, currentFD));
1339 }
1340 
1341 static void
UnmapRawFD(FD currentFD)1342 UnmapRawFD(FD currentFD)
1343 {
1344     FD fd;
1345 
1346     for (fd = 0; fd < fdMapSize; fd++) {
1347         if (fdMap[fd] == currentFD) {
1348             debug(16, (stderr, "Clearing FD Map %d => %d\n", fd, currentFD));
1349             fdMap[fd] = -1;
1350             return;
1351         }
1352     }
1353 }
1354 
1355 
1356 static void
DataFromRawFile(FD rawfd)1357 DataFromRawFile(FD rawfd)
1358 {
1359     Boolean fromClient = true;
1360     FD fd = -1;
1361     char *in, *n;
1362     unsigned char *out;
1363     static FD newServerFD;
1364 
1365     enterprocedure("DataFromRawFile");
1366 
1367     Verbose = XVerbose;
1368 
1369     for (;;) {
1370 
1371         /* If we already read a line last time, process it first,
1372            else get the next line for processing */
1373         if (FDinfo[rawfd].bufcount == 0) {
1374             if (fgets(FDinfo[rawfd].buffer, BUFFER_SIZE, raw) == NULL) {
1375                 CloseConnection(rawfd);
1376                 exit(0);
1377             }
1378             FDinfo[rawfd].bufcount = strlen(FDinfo[rawfd].buffer);
1379             debug(16, (stderr, "raw input = %s", FDinfo[rawfd].buffer));
1380         }
1381 
1382         in = FDinfo[rawfd].buffer;
1383 
1384         /* lines starting with space indicate change of sender */
1385         if (isspace(*in)) {
1386             /* If we already have data in buffer, process it */
1387             if ((fd != -1) && (FDinfo[fd].bufcount > 0)) {
1388                 debug(16, (stderr, "reporting %d bytes from: %s %s\n",
1389                            FDinfo[fd].bufcount,
1390                            fromClient ? "client" : "server",
1391                            ClientName(fd)));
1392                 if (fromClient)
1393                     ReportFromClient(fd, FDinfo[fd].buffer,
1394                                      FDinfo[fd].bufcount);
1395                 else
1396                     ReportFromServer(fd, FDinfo[fd].buffer,
1397                                      FDinfo[fd].bufcount);
1398                 FDinfo[fd].bufcount = 0;
1399                 return;
1400             }
1401             else {
1402                 /* Need to parse header to figure out which direction */
1403                 while (isspace(*in)) {
1404                     in++;
1405                 }
1406                 if (strncmp(in, "Client Connect (fd ", 19) == 0) {
1407                     /* New client connection */
1408                     FD ClientFD, ServerFD, origFD;
1409 
1410                     origFD = strtol(in + 19, NULL, 10);
1411 
1412                     SetUpRawClient(&ClientFD, &ServerFD);
1413                     SetRawFDMap(origFD, ClientFD);
1414                     fd = ClientFD;
1415                     fromClient = true;
1416                     newServerFD = ServerFD;
1417                 }
1418                 else if (strncmp(in, "Server Connect (fd ", 19) == 0) {
1419                     /* New client connection */
1420                     FD origFD;
1421 
1422                     if (newServerFD == 0)
1423                         panic("Server connection without matching client");
1424 
1425                     origFD = strtol(in + 19, NULL, 10);
1426                     SetRawFDMap(origFD, newServerFD);
1427                     fd = newServerFD;
1428                     fromClient = false;
1429                     newServerFD = 0;
1430                 }
1431                 else if (strstr(in, " --> EOF")) {
1432                     /* Closing client connection */
1433                     n = strstr(in, ": Client");
1434                     if (n != NULL) {
1435                         int clientid;
1436 
1437                         in = n + 8;
1438                         if (isdigit(*in))
1439                             clientid = strtol(in, NULL, 10);
1440                         else
1441                             clientid = 1;
1442 
1443                         for (fd = 0; fd < HighestFD; fd++) {
1444                             if (FDinfo[fd].ClientNumber == clientid) {
1445                                 if (Verbose >= 0) {
1446                                     PrintTime();
1447                                     fprintf(stdout, "Client%s --> EOF\n",
1448                                             ClientName(fd));
1449                                 }
1450                                 UnmapRawFD(fd);
1451                                 UnmapRawFD(FDPair(fd));
1452                                 CloseConnection(fd);
1453                                 break;
1454                             }
1455                         }
1456                     }
1457                     fd = -1;
1458                 }
1459                 else {
1460                     FD origFD;
1461 
1462                     if ((strncmp(in, "Request ", 8) == 0))
1463                         fromClient = true;
1464                     else
1465                         fromClient = false;
1466 
1467                     in = strstr(in, "(fd ");
1468                     if (in == NULL) {
1469                         warn("Did not find fd string in input entry");
1470                         warn(FDinfo[rawfd].buffer);
1471                         FDinfo[rawfd].bufcount = 0;
1472                         continue;
1473                     }
1474 
1475                     origFD = strtol(in + 4, NULL, 10);
1476                     fd = GetRawFDMap(origFD);
1477                     if (fd == -1) {
1478                         debug(16, (stderr, "origFD = %d\n", origFD));
1479                         warn("Unknown fd in input entry");
1480                         FDinfo[rawfd].bufcount = 0;
1481                         continue;
1482                     }
1483                 }
1484                 debug(16, (stderr, "raw data from: %s\n",
1485                            fromClient ? "client" : "server"));
1486                 /* skip over rest of header */
1487                 n = strchr(in, ':');
1488                 if (n != NULL)
1489                     in = n + 1;
1490                 while (isspace(*in))
1491                     in++;
1492             }
1493         }
1494 
1495         if (fd == -1) {
1496             /* dump data we don't know what to do with */
1497             FDinfo[rawfd].bufcount = 0;
1498             continue;
1499         }
1500 
1501         out = FDinfo[fd].buffer + FDinfo[fd].bufcount;
1502 
1503         while (*in != '\0') {
1504             unsigned char val;
1505 
1506             if ((in[0] >= '0') && in[0] <= '9') {
1507                 val = (in[0] - '0') << 4;
1508             }
1509             else if ((in[0] >= 'a') && in[0] <= 'f') {
1510                 val = (in[0] - 'a' + 0x0a) << 4;
1511             }
1512             else {
1513                 warn(in);
1514                 warn("invalid raw file input");
1515                 break;
1516             }
1517 
1518             if ((in[1] >= '0') && in[1] <= '9') {
1519                 val += (in[1] - '0');
1520             }
1521             else if ((in[1] >= 'a') && in[1] <= 'f') {
1522                 val += (in[1] - 'a' + 0x0a);
1523             }
1524             else {
1525                 warn(in + 1);
1526                 warn("invalid raw file input");
1527                 break;
1528             }
1529 
1530             if (in[2] == ' ') {
1531                 in += 3;
1532             } else {
1533                 in += 2;
1534             }
1535             while ((*in == '\r') || (*in == '\n'))
1536                 in++;
1537 
1538             *out++ = val;
1539             FDinfo[fd].bufcount++;
1540 
1541             /* If buffer is full, process what we have now */
1542             if (FDinfo[fd].bufcount >= BUFFER_SIZE) {
1543                 if (fromClient)
1544                     ReportFromClient(fd, FDinfo[fd].buffer,
1545                                      FDinfo[fd].bufcount);
1546                 else
1547                     ReportFromServer(fd, FDinfo[fd].buffer,
1548                                      FDinfo[fd].bufcount);
1549                 FDinfo[fd].bufcount = 0;
1550                 out = FDinfo[fd].buffer;
1551             }
1552         }
1553         FDinfo[rawfd].bufcount = 0;
1554     }
1555 }
1556 
1557 static void
SetUpRawFile(const char * filename)1558 SetUpRawFile(const char *filename)
1559 {
1560     FD fd;
1561     enterprocedure("SetUpRawFile");
1562 
1563     if (strcmp(filename, "-") == 0)
1564         raw = stdin;
1565     else
1566         raw = fopen(filename, "r");
1567     if (raw == NULL) {
1568         perror(filename);
1569         panic("Can't open raw file for reading");
1570     }
1571     fd = fileno(raw);
1572     debug(4, (stderr, "Opened raw file %s: FD %d\n", filename, fd));
1573 
1574     if (FDinfo[fd].buffer == NULL) {
1575         FDinfo[fd].buffer = calloc(1, BUFFER_SIZE);
1576         if (FDinfo[fd].buffer == NULL)
1577             panic("unable to allocate client buffer");
1578     }
1579     FDinfo[fd].bufcount = 0;
1580     FDinfo[fd].buflimit = -1;
1581     FDinfo[fd].bufdelivered = 0;
1582 
1583     UsingFD(fd, DataFromRawFile, NULL, NULL);
1584 }
1585