libnetmon.c 46.1 KB
Newer Older
1
2
/*
 * EMULAB-COPYRIGHT
3
 * Copyright (c) 2006-2007 University of Utah and the Flux Group.
4
5
6
7
8
9
 *
 * libnetmon, a library for monitoring network traffic sent by a process. See
 * README for instructions.
 */
#include "libnetmon.h"

10
11
12
13
14
15
/*
 * So that we can find out the name of the process that we're instrumenting
 */
extern int argc;
extern char** argv;

16
17
18
19
20
/*
 * Die with a standard Emulab-type error message format. In the future, I might
 * try to modify this to simply unlink our wrapper functions so that the app
 * can continue to run.
 */
21
static void croak(const char *format, ...) {
22
    va_list ap;
23
    fprintf(stderr,"*** ERROR\n    libnetmon: ");
24
    va_start(ap, format);
25
26
    vfprintf(stderr,format, ap);
    va_end(ap);
27
    fflush(stderr);
28
    abort();
29
30
31
32
33
34
35
36
}

/*
 * Set up our data structures, and go find the real versions of the functions
 * we're going to wrap.
 */
void lnm_init() {

37
    static bool initialized = false;
38
    char *sockpath;
39
    char *ctrl_sockpath;
40
    char *filepath;
41
    char *std_netmond;
42

43
    if (initialized == false) {
44
45
46
47
48
        DEBUG(printf("Initializing\n"));

        /*
         * Set up the array we use to track which FDs we're tracking
         */
49
        monitorFDs = (fdRecord*)NULL;
50
51
52
        fdSize = 0;
        allocFDspace();

53
54
55
56
57
58
        /*
         * By default, monitor TCP sockets only
         */
        monitor_tcp = true;
        monitor_udp = false;

59
60
61
62
63
64
        /*
         * Figure out which version of the output format we're supposed to use
         */
        char *outversion_s;
        outversion_s = getenv("LIBNETMON_OUTPUTVERSION");
        if (outversion_s) {
65
            if (!sscanf(outversion_s,"%u",&output_version) == 1) {
66
                croak("Bad output version: %s\n",outversion_s);
67
68
69
70
71
72
            }
        } else {
            output_version = 1;
        }
        DEBUG(printf("Using output version %i\n",output_version));

73
74
75
76
77
78
79
80
81
        /*
         * Find out if we're supposed to monitor UDP sockets. Set to a non-zero
         * int to monitor
         */
        char *monitor_udp_s;
        monitor_udp_s = getenv("LIBNETMON_MONITORUDP");
        if (monitor_udp_s) {
            int flag;
            if (!sscanf(outversion_s,"%i",&flag) == 1) {
82
                croak("Bad value for LIBNETMON_MONITORUDP: %s\n",
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
                        monitor_udp_s);
            }
            if (flag) {
                monitor_udp = true;
            } else {
                monitor_udp = false;
            }
        }

        /*
         * Get our PID
         * TODO: We need to handle fork() and its variants - our children could
         * get different PIDs
         */
        pid = getpid();

99
100
101
102
        /*
         * Default to reporting on everything - the individual reporting
         * options will get turned on later if report_all is true
         */
103
        report_io = report_sockopt = report_connect = report_init = false;
104
105
106
107
108
109
110
        report_all = true;
        char *reports_s;
        reports_s = getenv("LIBNETMON_REPORTS");
        if (reports_s) {
            lnm_parse_reportopt(reports_s);
        }

111
112
113
        /*
         * Find the real versions of the library functions we're going to wrap
         */
114
115
#define FIND_REAL_FUN(FUN) \
          real_##FUN = (FUN##_proto_t*)dlsym(RTLD_NEXT,#FUN); \
116
          if (!real_##FUN) \
117
              croak("Unable to get the address of " #FUN "(): %s\n", \
118
119
                    dlerror()); \

120
121
122
123
124
        FIND_REAL_FUN(socket);
        FIND_REAL_FUN(close);
        FIND_REAL_FUN(connect);
        FIND_REAL_FUN(write);
        FIND_REAL_FUN(send);
125
        FIND_REAL_FUN(setsockopt);
126
127
128
129
        FIND_REAL_FUN(read);
        FIND_REAL_FUN(recv);
        FIND_REAL_FUN(recvmsg);
        FIND_REAL_FUN(accept);
130
        FIND_REAL_FUN(sendto);
131
        FIND_REAL_FUN(sendmsg);
132

133
        /*
134
         * Connect to netmond if we've been asked to
135
         */
136
137
138
139
140
141
142
143
144
145
146
147
148
149

        /*
         * If this is set at all, use the standard data and control sockets for
         * netmond - it's a PITA to set them by hand
         */
        std_netmond = getenv("LIBNETMON_NETMOND");
        if (std_netmond) {
            sockpath = SOCKPATH;
            ctrl_sockpath = CONTROLSOCK;
        } else {
            sockpath = getenv("LIBNETMON_SOCKPATH");
            ctrl_sockpath = getenv("LIBNETMON_CONTROL_SOCKPATH");
        }

150
        filepath = getenv("LIBNETMON_OUTPUTFILE");
151
        if (sockpath) {
152
            int sockfd, contimo;
153
154
155
            struct sockaddr_un servaddr;

            DEBUG(printf("Opening socket at path %s\n",sockpath));
156

157
158
            sockfd = real_socket(AF_LOCAL, SOCK_STREAM, 0);
            if (!sockfd) {
159
                croak("Unable to create socket\n");
160
161
162
163
164
            }

            servaddr.sun_family = AF_LOCAL;
            strcpy(servaddr.sun_path,sockpath);

165
            char *contimo_s = getenv("LIBNETMON_CONNECTTIMO");
166
            if (contimo_s && sscanf(contimo_s,"%i", &contimo) == 1)
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
                    printf("libnetmon: Setting connection timeout to %i seconds\n",
                           contimo);
            else
                    contimo = 1;

            while (1) {
                    if (real_connect(sockfd, (struct sockaddr*) &servaddr,
                                     sizeof(servaddr)) == 0) {
                            break;
                    }
                    if (contimo && --contimo == 0) {
                            croak("Unable to connect to netmond socket\n");
                    }
                    printf("libnetmon: Failed to connect, trying again...\n");
                    sleep(1);
182
183
184
185
            }

            outstream = fdopen(sockfd,"w");
            if (!outstream) {
186
                croak("fdopen() failed on socket\n");
187
188
189
190
            }

            DEBUG(printf("Done opening socket\n"));

191
192
193
194
195
196
197
        } else if (filepath) {
            /*
             * Also allow output to a seperate file, so that we don't get
             * mixed in with the program's own stdout
             */
            outstream = fopen(filepath,"w+");
            if (!outstream) {
198
                croak("Unable to open output file\n");
199
            }
200
201
202
203
        } else {
            outstream = stdout;
        }

204
205
206
        /*
         * Connect to the netmond's control socket if we've been asked to
         */
207
        if (ctrl_sockpath) {
208
209
            struct sockaddr_un servaddr;

210
            DEBUG(printf("Opening control socket at path %s\n",ctrl_sockpath));
211

212
213
            controlfd = real_socket(AF_LOCAL, SOCK_STREAM, 0);
            if (!controlfd) {
214
                croak("Unable to create socket\n");
215
216
217
            }

            servaddr.sun_family = AF_LOCAL;
218
            strcpy(servaddr.sun_path,ctrl_sockpath);
219
220
221

            if (real_connect(controlfd, (struct sockaddr*) &servaddr,
                             sizeof(servaddr))) {
222
                croak("Unable to connect to netmond control socket\n");
223
224
225
226
227
228
229
230
            }

            /*
             * Set non-blocking, so we can quickly test for presence of
             * packets on the control socket.
             *
             * Note: Another possibility would be to use O_ASYNC on this
             * file descriptor, so that we get a signal when data is
231
             * available. But, this could interact poorly with apps that
232
233
234
235
             * use this signal themselves, so this is probably not a
             * good idea.
             */
            if (fcntl(controlfd, F_SETFL, O_NONBLOCK)) {
236
                croak("Unable to set control socket nonblocking\n");
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
            }

            /*
             * Ask the server for the parameters we're supposed to use
             */
            control_query();
            lnm_control_wait();
            lnm_control();

            DEBUG(printf("Done opening control socket\n"));

        } else {
            controlfd = -1;
        }

252
253
254
255
256
        /*
         * If we got all the way through that with report_all set, turn on the
         * others now
         */
        if (report_all) {
257
            report_io = report_sockopt = report_connect = report_init = true;
258
259
        }

260
261
262
263
        /*
         * Check to see if we're supposed to force a specific socket buffer
         * size
         */
264
265
266
267
268
269
        char *bufsize_s;
        if ((bufsize_s = getenv("LIBNETMON_SOCKBUFSIZE"))) {
            if (sscanf(bufsize_s,"%i",&forced_bufsize) == 1) {
                printf("libnetmon: Forcing socket buffer size %i\n",
                        forced_bufsize);
            } else {
270
                croak("Bad sockbufsize: %s\n",bufsize_s);
271
272
273
274
275
            }
        } else {
            forced_bufsize = 0;
        }

276
277
278
279
        /*
         * Run a function to clean up state when the program exits
         */
        if (atexit(&stopWatchingAll) != 0) {
280
            croak("Unable to register atexit() function\n");
281
282
        }

283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
        /*
         * Get our command line. I really don't like going into the /proc
         * filesystem to get it, but it beats mucking around in the stack.
         */
        char cmdfile[1024];
        char *cmdline;
        char cmdbuf[4096];
        snprintf(cmdfile,1024,"/proc/%i/cmdline",pid);
        DEBUG(printf("Opening %s\n",cmdfile));
        int cmdfd = open(cmdfile,O_RDONLY);
        /*
         * If we couldn't open it, report why in lieu of the command line
         */
        if (cmdfd < 0) {
            cmdline = strerror(errno);
        } else {
            /*
             * Read as much of the command line as we can, and null terminate
             * it
             */
            int bytesread = real_read(cmdfd,cmdbuf,sizeof(cmdbuf) - 1);
            real_close(cmdfd);
            cmdbuf[bytesread] = '\0';

            /*
             * But wait, there's more! What we got out of /proc is
             * null-separated, so we have to change it into a tidy
             * space-separated string.
             */
            for (int i = 0; i < bytesread - 1; i++) {
                if (cmdbuf[i] == '\0') {
                    cmdbuf[i] = ' ';
                }
            }

            cmdline = cmdbuf;
        }

        /*
         * XXX: Should escape 's, so as not to confuse the monitor
         */
        printlog(LOG_INIT,NO_FD,"'%s'",cmdline);

        initialized = true;
        DEBUG(printf("Done initializing\n"));
328
329
330
331
    } else {
        /* DEBUG(printf("Skipping intialization\n")); */
    }
}
332

333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
/*
 * Check for control messages
 */
void lnm_control() {
    ssize_t readrv;
    generic_m msg;

    if (controlfd < 0) {
        return;
    }

    /*
     * Socket is non-blocking
     *
     * NOTE: If read() is too slow, we might want some mechanism to only
     * check this FD every once in a while.
     */
    while ((readrv = real_read(controlfd, &msg, CONTROL_MESSAGE_SIZE))) {
        if (readrv == CONTROL_MESSAGE_SIZE) {
            /*
             * Got a whole message, process it
             */
            process_control_packet(&msg);
        } else if ((readrv < 0) && (errno == EAGAIN)) {
            /*
             * Normal case - no data ready for us
             */
            break;
        } else {
            // For now, croak. We can probably do something better
363
            croak("Error reading on control socket\n");
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
        }
    }

    return;
}

/*
 * Wait for a control message, then process it
 */
void lnm_control_wait() {
    fd_set fds;
    int selectrv;
    struct timeval tv;

    if (controlfd < 0) {
        return;
    }

    FD_ZERO(&fds);
    FD_SET(controlfd,&fds);

    tv.tv_sec = 10;
    tv.tv_usec = 0;

    DEBUG(printf("Waiting for a control message\n"));
    selectrv = select(controlfd + 1, &fds, NULL, NULL, &tv);
390
    if (selectrv == 0) {
391
        croak("Timed out waiting for a control message\n");
392
    } else if (selectrv < 0) {
393
        croak("Bad return value from select() in lnm_control_wait()\n");
394
395
396
397
398
    }

    DEBUG(printf("Done waiting for a control message\n"));

    lnm_control();
399

400
401
402
    return;
}

403
/*
404
405
 * 'Name' a file descriptor by getting a unique identifier for it - port
 * numbers, IP addresses, etc.
406
 */
407
void nameFD(int fd, const struct sockaddr *localname,
408
        const struct sockaddr *remotename) {
409
410
411
412
413
414
    struct sockaddr_in *remoteaddr, *localaddr;
    union {
        struct sockaddr sa;
        char data[128];
    } sockname;
    socklen_t namelen;
415

416
417
    if (remotename == NULL) {
        int gpn_rv;
418
        namelen = sizeof(sockname.data);
419
420
        gpn_rv = getpeername(fd,(struct sockaddr *)sockname.data,&namelen);
        if (gpn_rv != 0) {
421
            croak("Unable to get remote socket name: %s\n", strerror(errno));
422
423
424
425
426
427
428
429
        }
        /* Assume it's the right address family, since we checked that above */
        remotename = (struct sockaddr*)&(sockname.sa);
        remoteaddr = (struct sockaddr_in *) &(sockname.sa);
    } else {
        remoteaddr = (struct sockaddr_in*)remotename;
    }

430
431
432
433
    /*
     * Keep some information about the socket, so that we can print it out
     * later
     */
434
    monitorFDs[fd].remote_port = ntohs(remoteaddr->sin_port);
435
    monitorFDs[fd].remote_hostname = remoteaddr->sin_addr;
436
437
438
439
440

    /*
     * Get the local port number
     */
    int gsn_rv;
441
    if (localname == NULL) {
442
        namelen = sizeof(sockname.data);
443
444
        gsn_rv = getsockname(fd,(struct sockaddr *)sockname.data,&namelen);
        if (gsn_rv != 0) {
445
            croak("Unable to get local socket name: %s\n", strerror(errno));
446
447
448
449
450
        }
        /* Assume it's the right address family, since we checked that above */
        localaddr = (struct sockaddr_in *) &(sockname.sa);
    } else {
        localaddr = (struct sockaddr_in *) localname;
451
    }
452

453
    monitorFDs[fd].local_port = ntohs(localaddr->sin_port);
454

455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
}

/*
 * Start monitoring a new file descriptor
 */
void startFD(int fd) {
    unsigned int socktype, typesize;
    int sndsize, rcvsize;

    if (monitorFD_p(fd)) {
        printf("Warning: Tried to start monitoring an FD already in use!\n");
        stopFD(fd);
    }

    /*
     * Make sure it's an IP connection
     * XXX : Make sure the pointer is valid!
     */
    /*
     * XXX : Fix this!
    if (remotename->sa_family != AF_INET) {
        DEBUG(printf("Ignoring a non-INET socket\n"));
        return;
    }
    */
    /*
481
     * Check to make sure it's a type of socket we're supposed to be monitoring
482
483
484
     */
    typesize = sizeof(unsigned int);
    if (getsockopt(fd,SOL_SOCKET,SO_TYPE,&socktype,&typesize) != 0) {
485
        croak("Unable to get socket type: %s\n",strerror(errno));
486
    }
487
488
489
    if (!((monitor_tcp && socktype == SOCK_STREAM) ||
          (monitor_udp && socktype == SOCK_DGRAM))) {
        DEBUG(printf("Ignoring a type socket we're not monitoring\n"));
490
491
492
493
494
        return;
    }

    allocFDspaceFor(fd);

495
496
497
498
499
500
501
502
    /*
     * We may have been asked to force the socket buffer size
     */
    if (forced_bufsize) {
        int sso_rv;
        sso_rv = real_setsockopt(fd,SOL_SOCKET,SO_SNDBUF,
                &forced_bufsize, sizeof(forced_bufsize));
        if (sso_rv == -1) {
503
            croak("Unable to force out buffer size: %s\n",strerror(errno));
504
505
506
507
        }
        sso_rv = real_setsockopt(fd,SOL_SOCKET,SO_RCVBUF,
                &forced_bufsize, sizeof(forced_bufsize));
        if (sso_rv == -1) {
508
            croak("Unable to force in buffer size: %s\n",strerror(errno));
509
510
        }
    }
511
512

    /*
513
     * Find out the socket buffer sizes
514
     */
515
516
517
518
    sndsize = getNewSockbuf(fd,SO_SNDBUF);
    rcvsize = getNewSockbuf(fd,SO_RCVBUF);

    if (forced_bufsize && (sndsize > forced_bufsize)) {
519
520
        printf("Warning: Tried to force SO_SNBUF to %i but got %i\n",
                forced_bufsize, sndsize);
521
522
523
    }

    if (forced_bufsize && (rcvsize > forced_bufsize)) {
524
525
        printf("Warning: Tried to force SO_RCVBUF to %i but got %i\n",
                forced_bufsize, rcvsize);
526
527
    }

528
529
530
531
532
533
    /*
     * For version 3 and up, port numbers are not used for socket identifiers,
     * so we can report a New event now. For earlier output versions, this gets
     * reported in informConnect()
     */
    if (output_version >= 3) {
534
        printlog(LOG_NEW,fd,(socktype == SOCK_STREAM)?"TCP":"UDP");
535
536
    }

537
538
    monitorFDs[fd].monitoring = true;
    monitorFDs[fd].connected = false;
539
    monitorFDs[fd].socktype = socktype;
540

541
542
    DEBUG(printf("Watching FD %i\n",fd));

543
544
}

545
546
547
/*
 * Stop watching an FD
 */
548
549
550
551
void stopFD(int fd) {
    if (!monitorFD_p(fd)) {
        return;
    }
552

553
    DEBUG(printf("No longer watching FD %i\n",fd));
554
555
556
557

    /*
     * Let the monitor know we're done with it
     */
558
    printlog(LOG_CLOSED,fd);
559

560
    monitorFDs[fd].monitoring = false;
561
/* // XXX: If changed to dynamic memory, remove memory leak here.
562
    // XXX: Possible memory leak?
563
564
    if (monitorFDs[fd].remote_hostname != NULL) {
        monitorFDs[fd].remote_hostname = NULL;
565
    }
566
*/
567
568
}

569
570
571
572
573
574
575
576
577
578
/*
 * Stop watching all FDs - for use when the program exits
 */
void stopWatchingAll() {
    int i;
    for (i = 0; i < fdSize; i++) {
        if (monitorFD_p(i)) {
            stopFD(i);
        }
    }
579
    printlog(LOG_EXIT,NO_FD);
580
581
}

582
583
584
585
586
587
588
void printlog(logmsg_t type, int fd, ...) {
    /*
     * A set of variables that turns on and off specific parts of the output
     * string
     */
    bool print_name = true;
    bool print_id = true;
589
    bool id_is_pid = false;
590
591
592
593
594
595
596
597
    bool print_timestamp = true;
    bool print_value = true;

    /*
     * If this is set to false, we bypass logging completely.
     */
    bool print = true;

Robert Ricci's avatar
Robert Ricci committed
598
    DEBUG(printf("printlog(%i,%i,...) called\n",type,fd));
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664

    /*
     * Decide what to print based on the type of the log message and the output
     * version
     */
    switch (type) {
        case LOG_NEW:
            // Old versions didn't print a timestamp or socket type
            if (output_version < 3) { print_timestamp = print_value = false; }
            break;
        case LOG_REMOTEIP:
            // This message only showed up in version 3
            if (output_version < 3) { print = false; }
            break;
        case LOG_REMOTEPORT:
            // This message only showed up in version 3
            if (output_version < 3) { print = false; }
            break;
        case LOG_LOCALPORT:
            // This message only showed up in version 1
            if (output_version < 1) { print = false; }
            // Old version didn't include a timestamp
            if (output_version < 3) { print_timestamp = false; }
            // Only report if we're also reporting connection info
            if (!report_connect) { print = false; }
            break;
        case LOG_TCP_NODELAY:
            // Old version didn't include a timestamp
            if (output_version < 3) { print_timestamp = false; }
            // This message only showed up in version 2
            if (output_version < 2) { print = false; }
            // Allow global turning on/off of this message type
            if (!report_sockopt) { print = false; }
            break;
        case LOG_TCP_MAXSEG:
            // Old version didn't include a timestamp
            if (output_version < 3) { print_timestamp = false; }
            // This message only showed up in version 2
            if (output_version < 2) { print = false; }
            // Allow global turning on/off of this message type
            if (!report_sockopt) { print = false; }
            break;
        case LOG_SO_RCVBUF:
            // Old version didn't include a timestamp
            if (output_version < 3) { print_timestamp = false; }
            // This message only showed up in version 2
            if (output_version < 2) { print = false; }
            // Allow global turning on/off of this message type
            if (!report_sockopt) { print = false; }
            break;
        case LOG_SO_SNDBUF:
            // Old version didn't include a timestamp
            if (output_version < 3) { print_timestamp = false; }
            // This message only showed up in version 2
            if (output_version < 2) { print = false; }
            // Allow global turning on/off of this message type
            if (!report_sockopt) { print = false; }
            break;
        case LOG_CONNECTED:
            // No Value
            print_value = false;
            // This message only showed up in version 2
            if (output_version < 2) { print = false; }
            // Allow global turning on/off of this message type
            if (!report_connect) { print = false; }
            break;
665
666
667
668
669
670
671
672
        case LOG_ACCEPTED:
            // No Value
            print_value = false;
            // This message only showed up in version 2
            if (output_version < 2) { print = false; }
            // Allow global turning on/off of this message type
            if (!report_connect) { print = false; }
            break;
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
        case LOG_SEND:
            // This should be handled by calling log_packet()
            croak("LOG_SEND passed to printlog()");
            break;
        case LOG_SENDTO:
            // This should be handled by calling log_packet()
            croak("LOG_SENDTO passed to printlog()");
            break;
        case LOG_CLOSED:
            // No Value
            print_value = false;
            // Old version didn't include a timestamp
            if (output_version < 3) { print_timestamp = false; }
            // This message only showed up in version 2
            if (output_version < 2) { print = false; }
            // Allow global turning on/off of this message type
            if (!report_connect) { print = false; }
            break;
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
        case LOG_INIT:
            if (output_version < 3) { print = false; }
            if (!report_init) { print = false; }
            id_is_pid = true;
            break;
        case LOG_EXIT:
            if (output_version < 3) { print = false; }
            if (!report_init) { print = false; }
            id_is_pid = true;
            print_value = false;
            break;
        case LOG_SENDMSG:
            // In version 3, we don't actually log anything about the contents
            // of the call - we just want to make sure it isn't sneaking past
            // us. Techinically, it doesn't have to be UDP, but we'll lump it
            // in for now
            if (output_version < 3) { print = false; }
            if (!monitor_udp) { print = false; }
            print_value = false;
710
        default:
711
            croak("Invalid type (%i) passed to printlog()\n",type);
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
    }

    /*
     * If we've decided not to print anything at all, bail now
     */
    if (!print) {
        return;
    }

    /*
     * Print out the name of the command
     */
    if (print_name) {
        char *logname = log_type_names[type];
        fprintf(outstream,"%s: ",logname);
    }

    /*
730
731
     * Print out the ID of the socket the action is for. Some actions are not
     * associated with a specific socket, so we use the pid as the identifier
732
733
     */
    if (print_id) {
734
735
736
737
738
739
        if (id_is_pid) {
            fprintf(outstream,"%i ",pid);
        } else {
            fprintID(outstream,fd);
            fprintf(outstream," ");
        }
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
    }

    /*
     * Print out a timestamp
     */
    if (print_timestamp) {
        fprintTime(outstream);
        fprintf(outstream," ");
    }

    va_list ap;
    va_start(ap,fd);
    if (print_value) {
        /*
         * First variadic argument is the format string, which must be passed
         * specially to vfprintf;
         */
        char *fmt = va_arg(ap,char*);
        vfprintf(outstream,fmt,ap);
    }
    va_end(ap);

    fprintf(outstream,"\n");
}

765
766
767
/*
 * Print the unique identifier for a connection to the given filestream
 */
768
769
void fprintID(FILE *f, int fd) {

770
771
772
773
774
775
    if (fd == NO_FD) {
        croak("NO_FD passed to fprintID");
    } else if (fd < 0) {
        croak("Negative pid (%i) passed to fprintID");
    }

776
777
778
779
780
781
    if (output_version <= 2) {
        /*
         * Note, we've switched from local_port to FD for the first field - this is
         * so that we can report on a connection before connect() finishes
         */
        fprintf(f,"%i:%s:%i", fd,
782
                inet_ntoa(monitorFDs[fd].remote_hostname),
783
784
785
786
                monitorFDs[fd].remote_port);
    } else if (output_version == 3) {
        fprintf(f,"%i:%i",pid,fd);
    } else {
787
        croak("Improper output version");
788
    }
789
790
791

}

792
793
794
795
796
797
798
799
800
801
/*
 * Print out the current time in standard format
 */
void fprintTime(FILE *f) {
    struct timeval time;
    /*
     * XXX - At some point, we may want to use something more precise than
     * gettimeofday()
     */
    if (gettimeofday(&time,NULL)) {
802
        croak("Error in gettimeofday()\n");
803
804
805
806
807
    }
    fprintf(outstream,"%lu.%08lu",time.tv_sec, time.tv_usec);
}


808
809
810
811
812
813
/*
 * Handle a control message from netmond
 */
void process_control_packet(generic_m *m) {
    max_socket_m *maxmsg;
    out_ver_m *vermsg;
814
    reports_m *reportmsg;
815
    monitorudp_m *monitorudpmsg;
816
817

    DEBUG(printf("Processing control packet\n"));
818

819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
    switch (m->type) {
        case CM_MAXSOCKSIZE:
            /*
             * The server told us what the socket buffer sizes should be
             */
            maxmsg = (max_socket_m *)m;

            if (maxmsg->force == 0) {
                forced_bufsize = 0;
            } else {
                forced_bufsize = maxmsg->force_size;
            }

            if (maxmsg->limit == 0) {
                max_bufsize = 0;
            } else {
                max_bufsize = maxmsg->limit_size;
            }

            DEBUG(printf("Set forced_bufsize = %i, max_bufsize = %i\n",
                        forced_bufsize, max_bufsize));
            break;
        case CM_OUTPUTVER:
            /*
             * The server told us which output version to use
             */
            vermsg = (out_ver_m *)m;
            output_version = vermsg->version;

            DEBUG(printf("Set output version to %i\n", output_version));

850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
            break;
        case CM_REPORTS:
            /*
             * The server told us which things it wants us to report
             */
            reportmsg = (reports_m *)m;

            /*
             * Just for the heck of it, null out the last character to prevent
             * any dumb string functions from going off the end
             */
            reportmsg->reports[CONTROL_MESSAGE_PAYLOAD_SIZE - 1] = '\0';

            DEBUG(printf("Setting reports to %s\n", reportmsg->reports));

            lnm_parse_reportopt(reportmsg->reports);

867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
            break;
        case CM_MONITORDUP:
            /*
             * The server is telling us whether it wants us to monitor UDP
             * sockets
             */
            monitorudpmsg = (monitorudp_m*)m;

            if (monitorudpmsg->enable) {
                DEBUG(printf("Enabling UDP monitoring\n"));
                monitor_udp = true;
            } else {
                DEBUG(printf("Disabling UDP monitoring\n"));
                monitor_udp = false;
            }

883
884
            break;
        default:
885
            croak("Got an unexpected control message type: %i\n",
886
                   (void *)m->type);
887
888
889
890
891
892
893
894
895
896
897
    }
}

/*
 * Send out a query to the control socket
 */
void control_query() {
    generic_m msg;
    query_m *qmsg;

    if (!controlfd) {
898
        croak("control_query() called without control socket\n");
899
900
901
902
903
904
905
    }

    qmsg = (query_m *)&msg;
    qmsg->type = CM_QUERY;

    if ((real_write(controlfd,&msg,CONTROL_MESSAGE_SIZE) !=
                CONTROL_MESSAGE_SIZE)) {
906
        croak("Error writing control query\n");
907
908
909
910
911
912
    }

    return;

}

913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
/*
 * Parse a list of reports, setting the appropriate flags
 */
static void lnm_parse_reportopt(char *s) {
    char *p = s;

    /*
     * Turn 'all' off, since if they ask for specific ones, we only
     * give them those - but, we might set it back to true if they
     * say 'all'...
     */
    report_all = false;

    while (p != NULL && *p != '\0') {

        /*
         * Find out how many chars before the next option or end of
         * string, whichever comes first
         */
        int numchars;
        char* nextcomma = strchr(p,',');
        if (nextcomma == NULL) {
            numchars = strlen(p);
        } else {
            numchars = nextcomma - p;
        }

        /*
         * Not-so-fancy parsing
         */
        if (!strncmp(p,"all",numchars)) {
            report_all = true;
        } else if (!strncmp(p,"io",numchars)) {
            report_io = true;
        } else if (!strncmp(p,"connect",numchars)) {
            report_connect = true;
        } else if (!strncmp(p,"sockopt",numchars)) {
            report_sockopt = true;
951
952
        } else if (!strncmp(p,"init",numchars)) {
            report_init = true;
953
        } else {
954
            croak("Bad report: %s\n",p);
955
956
957
958
959
960
961
962
963
964
965
        }

        if (nextcomma) {
            p = nextcomma + 1;
        } else {
            p = NULL;
        }
    }
}


966
void allocFDspace() {
967
    fdRecord *allocRV;
968
969
970
971
972
973
    unsigned int newFDSize;

    /*
     * Pick a new size, and use realloc() to grown our current allocation
     */
    newFDSize = fdSize + FD_ALLOC_SIZE;
974
975
976
    DEBUG(printf("Allocating space for %i FDs\n",newFDSize));

    allocRV = realloc(monitorFDs, newFDSize * sizeof(fdRecord));
977
    if (!allocRV) {
978
        croak("Unable to malloc space for monitorFDs array\n");
979
980
981
982
983
984
    }
    monitorFDs = allocRV;

    /*
     * Set newly-allocated entries to 0
     */
985
    bzero(monitorFDs + fdSize, (newFDSize - fdSize) * sizeof(fdRecord));
986
987
988
989
990
991

    fdSize = newFDSize;

    return;
}

992
void allocFDspaceFor(int fd) {
993
    while (fd >= fdSize) {
994
        allocFDspace();
995
996
997
    }
}

998
999
1000
1001
1002
1003
1004
1005
bool monitorFD_p(int whichFD) {
    /*
     * If this FD is greater than the size of our fd array, then we're
     * definitely not monitoring it.
     */
    if (whichFD >= fdSize) {
        return false;
    } else {
1006
        return monitorFDs[whichFD].monitoring;
1007
1008
1009
    }
}

1010
1011
1012
1013
1014
1015
1016
1017
bool connectedFD_p(int whichFD) {
    if (whichFD >= fdSize) {
        return false;
    } else {
        return monitorFDs[whichFD].connected;
    }
}

1018
/*
1019
1020
1021
 * Let the user know that a packet has been sent. This function is, for now,
 * seperate from printlog() because it prints out very different messages for
 * early versions of the output format
1022
 */
1023
void log_packet(int fd, size_t len, const struct sockaddr *srvaddr) {
1024
1025
1026
    if (!report_io) {
        return;
    }
1027
1028
1029
1030
1031
1032
    struct timeval time;
    /*
     * XXX - At some point, we may want to use something more precise than
     * gettimeofday()
     */
    if (gettimeofday(&time,NULL)) {
1033
        croak("Error in gettimeofday()\n");
1034
    }
1035
1036
    switch (output_version) {
        case 0:
1037
            fprintf(outstream,"%lu.%08lu [%i, %i]\n",time.tv_sec, time.tv_usec,
1038
1039
1040
1041
                    fd,len);
            break;
        case 1:
            fprintf(outstream,"%lu.%06lu > %s.%i (%i)\n",time.tv_sec,
1042
                    time.tv_usec, inet_ntoa(monitorFDs[fd].remote_hostname),
1043
1044
1045
1046
1047
1048
1049
                    monitorFDs[fd].remote_port, len);
            break;
        case 2:
            fprintf(outstream,"%lu.%06lu > ", time.tv_sec, time.tv_usec);
            fprintID(outstream,fd);
            fprintf(outstream," (%i)\n", len);
            break;
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
        case 3:
            /*
             * We handle send() and sendto() differently - specificially, we
             * assume sendto() calls are UDP, and thus we need to log the dest
             * IP, plus local and remote ports. (Note: It is legal to call
             * sendto() on a TCP socket, but you must give a null srvaddr,
             * which will result in us handling as a send())
             */
            if (monitorFDs[fd].socktype == SOCK_STREAM) {
                // send()
                fprintf(outstream,"Send: ");
                fprintID(outstream,fd);
                fprintf(outstream," %lu.%06lu %i\n", time.tv_sec, time.tv_usec,
                        len);
            } else {
                int local_port = monitorFDs[fd].local_port;
                char *remote_ip;
                int remote_port;
                if (srvaddr != NULL) {
                    const struct sockaddr_in *inaddr =
                        (const struct sockaddr_in*)srvaddr ;
                    /*
                     * XXX - We should cache this so we don;t have to
                     * re-compute it so many times.
                     * XXX - This is probably not thread-safe
                     */
                    remote_ip = inet_ntoa(inaddr->sin_addr);
                    remote_port = ntohs(inaddr->sin_port);
                } else {
                    if (! connectedFD_p(fd)) {
1080
                        croak("Attempted to call sendto() on an unconnected "
1081
1082
                               "socket without a srvaddr");
                    }
1083
                    remote_ip = inet_ntoa(monitorFDs[fd].remote_hostname);
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
                    remote_port = monitorFDs[fd].remote_port;
                }
                fprintf(outstream,"SendTo: ");
                fprintID(outstream,fd);
                fprintf(outstream," %lu.%06lu %i:%s:%i:%i\n",
                        time.tv_sec, time.tv_usec,
                        local_port, remote_ip, remote_port,
                        len);
            }
            break;
1094
        default:
1095
            croak("Bad output version: %i\n", (void *)output_version);
1096
    }
1097
    fflush(outstream);
1098
1099
1100
}

/*
1101
 * Inform the user that the nodelay flag has been changed
1102
 */
1103
void informNodelay(int fd) {
1104
1105
    if (monitorFDs[fd].socktype == SOCK_STREAM) {
        printlog(LOG_TCP_NODELAY, fd, "%i",monitorFDs[fd].tcp_nodelay);
1106
1107
1108
1109
    }
}

void informMaxseg(int fd) {
1110
1111
    if (monitorFDs[fd].socktype == SOCK_STREAM) {
        printlog(LOG_TCP_MAXSEG, fd, "%i",monitorFDs[fd].tcp_maxseg);
1112
1113
1114
1115
    }
}

void informBufsize(int fd, int which) {
1116
1117
1118
1119
1120
    /* TODO: Handle bad which parameter */
    printlog((which == SO_SNDBUF) ? LOG_SO_SNDBUF : LOG_SO_RCVBUF,
             fd, "%i",
             (which == SO_SNDBUF) ? monitorFDs[fd].sndbuf :
                                    monitorFDs[fd].rcvbuf);
1121
1122
1123

}

1124
void informConnect(int fd, inform_which_t which) {
1125
1126
1127
1128
1129
1130
1131
1132
1133
    /*
     * Let the monitor know about it - note: if it's a UDP socket, we've
     * already reported on it in startFD. Note that, for version 3, we
     * report the existence of the socket earler, in startFD
     */
    if ((output_version < 3) && monitorFDs[fd].socktype == SOCK_STREAM) {
        printlog(LOG_NEW, fd,
                 (monitorFDs[fd].socktype == SOCK_STREAM)?"TCP":"UDP");
    }
1134

1135
1136
1137
1138
1139
1140
1141
    /*
     * New versions of the output no longer include port numbers in the
     * identifier. So, report those now. Note that we only do this for TCP
     * sockets - UDP sockets will get this information reported with each
     * sendto()
     */
    if (monitorFDs[fd].socktype == SOCK_STREAM){
1142
        printlog(LOG_REMOTEIP,fd,"%s",inet_ntoa(monitorFDs[fd].remote_hostname));
1143
1144
        printlog(LOG_REMOTEPORT,fd,"%i",monitorFDs[fd].remote_port);
    }
1145

1146
1147
1148
1149
1150
1151
1152
    /*
     * Some things we report on for every connection
     */
    informNodelay(fd);
    informMaxseg(fd);
    informBufsize(fd, SO_RCVBUF);
    informBufsize(fd, SO_SNDBUF);
1153

1154
1155
1156
1157
1158
    if (which == INFORM_CONNECT) {
        printlog(LOG_CONNECTED,fd);
    } else {
        printlog(LOG_ACCEPTED,fd);
    }
1159
1160
1161
}

int getNewSockbuf(int fd, int which) {
1162
1163
    socklen_t newsize;
    socklen_t optsize;
1164
1165
    optsize = sizeof(newsize);
    if (getsockopt(fd,SOL_SOCKET,which,&newsize,&optsize)) {
1166
1167
1168
        croak("Unable to get socket buffer size");
        /* Make GCC happy - won't get called */
        return 0;
1169
    } else {
1170
1171
1172
1173
1174
        if (which == SO_SNDBUF) {
            monitorFDs[fd].sndbuf = newsize;
        } else {
            monitorFDs[fd].rcvbuf = newsize;
        }
1175

1176
        return newsize;
1177
1178
    }
}
1179
1180

/*
1181
 * Library function wrappers
1182
1183
1184
 */
int socket(int domain, int type, int protocol) {
    int returnedFD;
1185
    lnm_init();
1186
1187
1188
    DEBUG(printf("socket() called\n"));
    returnedFD = real_socket(domain, type, protocol);
    if (returnedFD > 0) {
1189
        startFD(returnedFD);
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
    }

    return returnedFD;
}

/*
 * We're only going to bother to monitor FDs after they have succeeded in
 * connecting to some host.
 *
 * TODO: Allow for some filters:
 *      Only certain hosts? (eg. not loopback)
 */
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) {

    int rv;
    lnm_init();
1206
1207
    lnm_control();

1208
1209
    DEBUG(printf("connect() called on %i\n",sockfd));

1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
    /*
     * So, this is a bit messy, but we gotta do it this way. We report on the
     * socket _before_ calling the real connect. This help keep the stub
     * from getting too far behind - otherwise, it can't start until the real
     * app has finished the three way handshake and is well on its way to
     * sending data. If connect() fails, we'll report a socket close below.
     */

    /*
     * Find out some things about the address we're trying to connect to. We
     * decided in socket() if we're going to monitor this connection or not
     * TODO: We really should verify that serv_addr is a legal pointer
     */
    if (monitorFD_p(sockfd)) {
        nameFD(sockfd,NULL,serv_addr);
1225
        informConnect(sockfd,INFORM_CONNECT);
1226
1227
    }

1228
1229
    rv = real_connect(sockfd, serv_addr, addrlen);

1230
    if (!monitorFD_p(sockfd)) {
1231
1232
1233
1234
        /*
         * Just pass the result back
         */
        return rv;
1235
1236
    }

1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
    /*
     * There are actually some cases when connect() can 'fail', but we
     * still want to watch the FD
     */
    if ((rv == 0) ||
           ((errno == EISCONN) ||     /* Socket is already connected */
            (errno == EINPROGRESS) || /* Non blocking socket */
            (errno == EINTR) ||       /* Connect will happen in background */
            (errno == EALREADY))) {    /* In progress in background */
        /*
         * TODO: In the case of the 'errors' that mean the socket is
         * connecting in the background, we really should make sure that
         * it actually connects - but this could be tricky. The caller is
         * supposed to select() on the FD to find out when it's ready, but
         * if they don't, and just write to it, we won't find out. So, for
         * now, just assume that the connect() will succeed.
         */
1254
        monitorFDs[sockfd].connected = true;
1255
1256

        /*
1257
         * Get the local port number so that we can monitor it
1258
         */
1259
        struct sockaddr_in localaddr;
1260
        socklen_t namelen = sizeof(localaddr);
1261
        if (getsockname(sockfd, (struct sockaddr*)&localaddr,&namelen) != 0) {
1262
            croak("Unable to get local socket name: %s\n", strerror(errno));
1263
1264
        }
        int local_port = ntohs(localaddr.sin_port);
1265
        monitorFDs[sockfd].local_port = local_port;
1266

1267
1268
1269
1270
1271
        /*
         * Report on the local port number
         */
        if (monitorFDs[sockfd].socktype == SOCK_STREAM) {
            printlog(LOG_LOCALPORT,sockfd,"%i",local_port);
1272
        }
1273
1274
    } else {
        /*
1275
         * Do this in case the connection really did fail
1276
         */
1277
        stopFD(sockfd);
1278
1279
1280
1281
1282
    }

    return rv;
}

1283
1284
1285
1286
1287
1288
1289
1290
/*
 * We will also watch for accept()ed connections
 */
int accept(int s, struct sockaddr * addr,
        socklen_t * addrlen) {

    int rv;
    lnm_init();
1291
1292
    lnm_control();

1293
1294
1295
1296
    DEBUG(printf("accept() called on %i\n",s));

    rv = real_accept(s, addr, addrlen);

1297
    if (!monitorFD_p(s)) {
1298
1299
        return rv;
    }
1300

1301
1302
    if (rv > 0) {
        /*
1303
         * Got a new client! Start it up, name it, and report on its local port
1304
         */
1305
        startFD(rv);
1306
1307
        nameFD(rv,NULL,addr);
        informConnect(rv,INFORM_ACCEPT);
1308
        // XXX Accessors
1309
1310
        monitorFDs[rv].connected = 1;
        printlog(LOG_LOCALPORT,rv,"%i",monitorFDs[rv].local_port);
1311
1312
1313
1314
1315
1316
    }

    return rv;

}

1317
1318
1319
1320
1321
1322
1323
/*
 * When the user closes a socket, we can stop monitoring it
 */
int close(int d) {
    int rv;

    lnm_init();
1324
    lnm_control();
1325
1326
1327

    rv = real_close(d);

1328
    if (!rv && monitorFD_p(d)) {
1329
        DEBUG(printf("Detected a closed socket with close()\n"));
1330
        stopFD(d);
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
    }

    return rv;
}

/*
 * Wrap the send() function so that we can log messages sent to any of the
 * socket's we're monitoring.
 *
 * TODO: Need to write wrappers for other functions that can be used to send
 * data on a socket:
 * writev
 * others?
 */
ssize_t send(int s, const void *msg, size_t len, int flags) {
    ssize_t rv;

    lnm_init();
1349
    lnm_control();
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361

    /*
     * Wait until _after_ the packet is sent to log it, since the call might
     * block, and we basically want to report when the kernel acked receipt of
     * the packet
     */
    /*
     * TODO: There are a LOT of error cases, flags, etc, that we should handle.
     */
    rv = real_send(s,msg,len,flags);

    if ((rv > 0) && monitorFD_p(s)) {
1362
        log_packet(s,rv,NULL);
1363
1364
1365
1366
1367
    }

    return rv;

}
1368
1369
1370
1371
1372