File: bing_probes.c

package info (click to toggle)
bing 1.3.5-5
  • links: PTS
  • area: main
  • in suites: bookworm, sid
  • size: 456 kB
  • sloc: ansic: 3,774; makefile: 51
file content (538 lines) | stat: -rw-r--r-- 17,698 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
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
328
329
330
331
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
363
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
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
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
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
/*
 *        Unofficial release 1.3
 *                B I N G
 *
 */

/* $Id: bing_probes.c,v 1.12 1999/10/24 23:28:14 fgouget Exp $ */

#include <stdio.h>
#include <errno.h>
#include <malloc.h>
#include <stdlib.h>

/* types.h provides u_short on HPUX10 and Solaris */
#include <sys/types.h>

#ifdef WIN32
#include <winsock.h>
#include "win32/types.h"
#else
#include <netinet/in_systm.h>
#include <netinet/in.h>
#endif

#include <netinet/ip.h>
#include <netinet/ip_icmp.h>

#include "bing_misc.h"
#include "bing_probes.h"
#include "mod_icmp.h"

#ifdef NO_RANDOM
#define random          rand
#define srandom         srand
#endif


/* (!!) this will have to move somewhere else */
#ifndef ICMP_ROUTERADVERT
#define ICMP_ROUTERADVERT       9               /* router advertisement */
#endif
#ifndef ICMP_ROUTERSOLICIT
#define ICMP_ROUTERSOLICIT      10              /* router solicitation */
#endif
#ifndef ICMP_TIMXCEED
#define ICMP_TIMXCEED           11              /* time exceeded, code: */
#endif
#ifndef ICMP_PARAMPROB
#define ICMP_PARAMPROB          12              /* ip header bad */
#endif
#ifndef ICMP_TSTAMP
#define ICMP_TSTAMP             13              /* timestamp request */
#endif
#ifndef ICMP_TSTAMPREPLY
#define ICMP_TSTAMPREPLY        14              /* timestamp reply */
#endif
#ifndef ICMP_IREQ
#define ICMP_IREQ               15              /* information request */
#endif
#ifndef ICMP_IREQREPLY
#define ICMP_IREQREPLY          16              /* information reply */
#endif
#ifndef ICMP_MASKREQ
#define ICMP_MASKREQ            17              /* address mask request */
#endif
#ifndef ICMP_MASKREPLY
#define ICMP_MASKREPLY          18              /* address mask reply */
#endif

/*
 * bing_probes default settings
 */

#define DEF_UDP_PORT 28933

/*
 * This structure holds the information kept in a probe handle.
 */
typedef struct {
    /*
     * Options modified via probe_set_option
     */
    unsigned char bp_om:2,      /* specifies the probe method */
         bp_fill:2,             /* specifies the type of packet filling */
         bp_unused:4;           /* not used */
    short udp_port;             /* the invalid UDP port to probe in BP_OM_UNREACH_PORT mode */
    int pattern_size;           /* the pattern size if one exists */
    char* pattern;              /* the pattern data */
    
    /*
     * Internal data house-keeping
     */
    icmp_handle icmp_handle;    /* the icmp module handle used to send icmp packets */
    struct icmp* packet;        /* the icmp packet contents, contains the header and data */
                                /* (!!) does it contain the ip header or just the icmp header? */
    int packet_options_size;    /* the options size packet+packet_options_size point to the data */
    int packet_data_size;       /* the packet data size */
    int packet_data_update;     /* 1 if the packet data needs updating */
} bp_state_t;

#define handle2state(h)        ((bp_state_t*)h)

/*
 * Some internal functions.
 */

void dump_packet(char* packet, int size)
{
    struct ip* ip;
    struct sockaddr src_addr,dst_addr;
    char* ipopt;
    int hlen;
    int j;

    /* Dump the ip header */
    ip=(struct ip*)packet;
    hlen=ip->ip_hl<<2;
    SOCKADDR_IN(&src_addr)->sin_family=AF_INET;
    SOCKADDR_IN(&src_addr)->sin_port=0;
    SOCKADDR_IN(&src_addr)->sin_addr.s_addr=ip->ip_src.s_addr;
    SOCKADDR_IN(&dst_addr)->sin_family=AF_INET;
    SOCKADDR_IN(&dst_addr)->sin_port=0;
    SOCKADDR_IN(&dst_addr)->sin_addr.s_addr=ip->ip_dst.s_addr;

    printf("\n");
    for (j=0;j<hlen;j++) {
        printf("%02x ",(unsigned char)*(packet+j));
    }
    printf("\nVr HL  TOS Len   ID   Flg off  TTL Pro cks  Src             Dst\n");
    printf(" %1x %2d  %02x  %5d %04x",
       ip->ip_v, ip->ip_hl, ip->ip_tos, (unsigned short)ip->ip_len, ip->ip_id);
    printf(" %1x   %04x", ((ip->ip_off) & 0xe000) >> 13,
       (ip->ip_off) & 0x1fff);
    printf(" %02x  %02x  %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum);
    printf(" %-15s", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr));
    printf(" %s\n", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr));

    /* dump any option bytes */
    if (hlen>sizeof(struct ip)) {
        printf("IP Options: ");
        ipopt=(char*)ip+sizeof(struct ip);         /* point to options */
        while (hlen-->sizeof(struct ip)) {
            printf("%02x", *ipopt++);
        }
        putchar('\n');
    }

    /* Dump the icmp header if any*/
    if (ip->ip_p==1) {
        struct icmp* icp;

        icp=(struct icmp*)(packet+(ip->ip_hl<<2));
        switch (icp->icmp_type) {
        case ICMP_ECHOREPLY:
            printf("Echo Reply:   code=%d  id=%d seq=%d\n",icp->icmp_code,icp->icmp_id,icp->icmp_seq);
            break;
        case ICMP_SOURCEQUENCH:
            printf("ICMP_SOURCEQUENCH");
            break;
        case ICMP_REDIRECT:
            printf("ICMP_REDIRECT");
            break;
        case ICMP_ECHO:
            printf("Echo Request: code=%d  id=%d seq=%d\n",icp->icmp_code,icp->icmp_id,icp->icmp_seq);
            break;
        case ICMP_ROUTERADVERT:
            printf("ICMP_ROUTERADVERT");
            break;
        case ICMP_ROUTERSOLICIT:
            printf("ICMP_ROUTERSOLICIT");
            break;
        case ICMP_TIMXCEED:
            printf("ICMP_TIMXCEED");
            break;
        case ICMP_PARAMPROB:
            printf("ICMP_PARAMPROB");
            break;
        case ICMP_TSTAMP:
            printf("ICMP_TSTAMP");
            break;
        case ICMP_TSTAMPREPLY:
            printf("ICMP_TSTAMPREPLY");
            break;
        case ICMP_IREQ:
            printf("ICMP_IREQ");
            break;
        case ICMP_IREQREPLY:
            printf("ICMP_IREQREPLY");
            break;
        case ICMP_MASKREQ:
            printf("ICMP_MASKREQ");
            break;
        case ICMP_MASKREPLY:
            printf("ICMP_MASKREPLY");
            break;
        default:
            printf("unknown type %d\n",icp->icmp_type);
        }
    }
    
    /* Give short info about the payload */

}

/*
  --------------------------------------------------------------------------
        Builds the data field to be sent with the packet. The data put 
    in the packet is either dynamic, e.g. random data, or static (i.e.
    does not change depending on the packet), e.g. a user specified 
    pattern. In the first case the data is built on the fly while in the
    second case it is precomputed and recomputed each time the packet 
    size increases.
  ------------+----+-----+--------------------------------------------------
  Parameter   | IN | OUT | Role
  ------------+----+-----+--------------------------------------------------
  state       | X  |     | Internal state information
  size        | X  |     | The required packet size
  ------------+----+-----+--------------------------------------------------
  RETURN      |    |  X  | No return value
  ------------+----+-----+--------------------------------------------------
*/
static int generate_data(bp_state_t* state, int size)
{
    int i;

    /* maybe reallocate the data buffer */
    if (size>state->packet_data_size) {
    /* Allocate buffers which size is a multiple of 1024 */
    state->packet_data_size=(size+state->packet_options_size+1024) & ~1023;
    state->packet=realloc(state->packet,state->packet_data_size);
        state->packet_data_size-=state->packet_options_size;
    if (state->packet==NULL)
        return -1;
    state->packet_data_update=1;
    }

    /* maybe update the data in the buffer */
    if (state->packet_data_update==1) {
        unsigned char* packet_data;

        packet_data=((unsigned char*)state->packet)+state->packet_options_size;
        switch (state->bp_fill) {
        case BP_FILL_SEQ:
            /* Default case: just put some predictible data in 
             * the packet.
             */
            for (i=0;i<state->packet_data_size;i++) {
                packet_data[i]=(unsigned char)(i & 0xff);
            }
            state->packet_data_update=0;
            break;
        case BP_FILL_PATTERN:
            /* Fill the buffer with the pattern. Since this 
             * pattern does not change we can compute it ahead 
             * of time
             */
            i=0;
            while (i<state->packet_data_size) {
                memcpy(packet_data+i,state->pattern,
                   MIN(state->pattern_size,state->packet_data_size-i));
                i+=state->pattern_size;
            }
            state->packet_data_update=0;
            break;
        case BP_FILL_RANDOM:
            /* Initialise the packet payload with random data.
             * Note that on some platforms (e.g. Win32) RAND_MAX is less
             * than INT_MAX. Thus we only use the lower byte of the returned
             * int which in turn relies on the assumption that RAND_MAX+1
             * is a multiple of 256. Fortunately this seems to always be the 
             * case.
             */
	    /*printf("Filling the packet with %d random bytes:",state->packet_data_size);*/
            for (i=0;i<state->packet_data_size;i++) {
                packet_data[i]=(unsigned char)(random() & 0xff);
		/*if (i%16==0)
		    printf("\n%3d: ",i);
		printf("%2x,",packet_data[i]);*/
            }
	    /*printf("\n");*/
	    
            /* Unlike the other types of "filling" this one must be 
             * updated with each outgoing packet, thus we do not reset
             * packet_data_update.
             */
            break;
        default:
            return -1;
        }
    }
    return 0;
}

/*
 * Implementation of the bing_probe interface.
 */

bp_handle probe_open()
{
    bp_state_t* h;

    /* Allocate the data structure */
    h=malloc(sizeof(*h));
    if (h==NULL)
        return NULL;

    /* Open the ICMP handle. This requires root privilege on Unix */
    h->icmp_handle=icmp_open();
    if (h->icmp_handle==NULL) {
        free(h);
        return NULL;
    }

    /* And initialise the other fields */
    /* (!!) what should be the default ??? */
    h->bp_om=BP_OM_ECHO_REPLY;
    h->bp_fill=BP_FILL_SEQ;
    h->udp_port=DEF_UDP_PORT;
    h->pattern_size=0;
    h->pattern=NULL;
    h->packet=NULL;
    /* (!!) and what if the options are bigger, like if we do loose source routing or some such (which means the packet would also contain the ip options, not only the icmp options */
    h->packet_options_size=(char*)h->packet->icmp_data-(char*)h->packet;
    h->packet_data_size=0;
    h->packet_data_update=1;

    return h;
}

int probe_set_option(bp_handle handle, int level, int option, void* optval, int optlen)
{
    bp_state_t* state;
    int intval;

    if (handle==NULL) {
        errno=EINVAL;
        return -1;
    }

    state=handle2state(handle);
    if (level!=SOL_BP)
        return icmp_set_option(state->icmp_handle,level,option,optval,optlen);

    switch (option) {
    case BPO_FILLING:
        intval=*((int*)optval);
        if (((intval & ~BP_FILL_MASK)!=0) || (intval==BP_FILL_MASK)) {
            errno=EINVAL;
            return -1;
        }
        if ((intval==BP_FILL_PATTERN) && (state->pattern==NULL)) {
            errno=EFAULT;
            return -1;
        }
        state->bp_fill=intval;
        break;
    case BPO_PATTERN:
        /* check parameter */
        if ((optlen==0) && (state->bp_fill==BP_FILL_PATTERN)) {
            errno=EINVAL;
            return -1;
        }

        /* and reallocate the pattern buffer */
        state->pattern_size=optlen;
        state->pattern=realloc(state->pattern,state->pattern_size);
        if (state->pattern==NULL)
                return (optlen==0?0:-1);
        state->packet_data_update=1; /* the old pattern is lost */
        memcpy(state->pattern,(char*)optval,state->pattern_size);
        break;
    case BPO_OM:
        intval=*((int*)optval);
        if (((intval & ~BP_OM_MASK)!=0) || (intval==BP_OM_MASK)){
            errno=EINVAL;
            return -1;
        }
        state->bp_om=intval;
        break;
    case BPO_UDP_PORT:
        handle2state(handle)->udp_port=(int)optval;
        break;
    default:
        errno=EINVAL;
        return -1;
    }
    return 0;
}

int do_probe(bp_handle handle, struct sockaddr* target, int size, int ttl, bp_probedata_t *probe)
{
    bp_state_t* state;
    static int icp_seq=0;
    int res;
    int src_addr_size;
    int ret;

    probe->contents=NULL;
    if ((handle==NULL) || (size<PAD_PKT_SIZE)){
        errno=EINVAL;
        return -1;
    }
    state=handle2state(handle);
    ret=-1;

    /* (!!) hack ??? */
    if (ttl==0)
        ttl=1;

    /* Prepare the packet payload */
    generate_data(state,size);

    /* Probe the remote host according to the chosen method */
    switch (state->bp_om) {
    case BP_OM_UNREACH_PORT:
        /* BP_OM_UNREACH_PORT: send a UDP packet to a non-existing port.
         * We will either receive a TTL Exceeded or a Port Unreachable
         * ICMP message.
         */
        return -1;
        break;
    case BP_OM_ECHO_REPLY:
    case BP_OM_TTL_EXCEEDED:
        probe->size=state->packet_options_size+size;
        probe->contents=malloc(sizeof(struct ip)+probe->size);
        /* BP_OM_TTL_EXCEEDED and BP_OM_ECHO_REPLY: send an ICMP EchoRequest
         * message.
         * In the first case the upper layer should have arranged to receive 
         * TTL Exceeded ICMP messages while in the second case we should receive 
         * Echo Reply ICMP messages.
         */
        state->packet->icmp_type=ICMP_ECHO;
        state->packet->icmp_code=0;
        /* icmp_id is set by icmp_send */
        state->packet->icmp_seq = icp_seq++;
        /* (TODO) target should be in the sockaddr format.
         * Find something else to compute the packet size.
         * The ttl is not handled.
         */
        if (ttl>0) {
            probe_set_option(handle, IPPROTO_IP, IP_TTL, &ttl, 4);
        }
        res=icmp_send(state->icmp_handle,state->packet,
            probe->size,
            target,sizeof(*target));
        if (res<0) {
            return -1;
        }
        /* Now try to get the answer to our packet. Since we receive all ICMP
         * packets for this host we may receive other packets before our own, 
         * we may even receive the packet that we just sent !
         */
        while (1) {
            src_addr_size=sizeof(probe->src_addr);
            res=icmp_recv(state->icmp_handle,
                probe->contents,probe->size+sizeof(struct ip),
                &probe->src_addr,&src_addr_size,
                &probe->rtt);
            if (res>0) {
                struct ip* ip;
                struct icmp* icp;
                /* On linux the returned size does not take into account the ip header ! 
                 * Furthermore the ip->ip_len field does not take into account endianess
                 * issues. That's why I'm using res+(ip->ip_hl<<2).
                 */
        
                /* ip header */
                ip=(struct ip*)(probe->contents);
                /* icmp header */
                icp=(struct icmp*)(probe->contents+(ip->ip_hl<<2));
                probe->dst_addr.sa_family=PF_INET;
                memcpy(&((struct sockaddr_in*)(&probe->dst_addr))->sin_addr,&ip->ip_dst,4);
                ((struct sockaddr_in*)&(probe->dst_addr))->sin_port=0;

                switch (icp->icmp_type) {
                case ICMP_ECHOREPLY:
                    /*DEBUG_MSG((stderr,"icmp_type/code=%d/%d rtt=%g",icp->icmp_type,icp->icmp_code,probe->rtt));*/
                    if ((icp->icmp_seq+1==icp_seq)  && (icp->icmp_id==icmp_get_id(state->icmp_handle))) {
                        probe->size=res+(ip->ip_hl<<2);
                        return BP_RES_HIT;
                    } else {
                        printf("received a stale Echo Reply id=%d seq=%d\n",icp->icmp_id,icp->icmp_seq);
                    }
                    break;
                case ICMP_UNREACH:
                    DEBUG_MSG((stderr,"icmp_type/code=%d/%d rtt=%g",icp->icmp_type,icp->icmp_code,probe->rtt));
                    if (icp->icmp_code==ICMP_UNREACH_PORT) {
                        probe->size=res+(ip->ip_hl<<2);
                        return BP_RES_HIT;
                    }
                    break;
                case ICMP_TIMXCEED:
                    DEBUG_MSG((stderr,"icmp_type/code=%d/%d rtt=%g",icp->icmp_type,icp->icmp_code,probe->rtt));
                    if (icp->icmp_code==ICMP_TIMXCEED_INTRANS) {
                        probe->size=res+(ip->ip_hl<<2);
                        return BP_RES_TTL_EXCEEDED;
                    }
                    break;
                case ICMP_ECHO:
                    /* this is most likely the echo that we sent */
                    break;
                default:
                    DEBUG_MSG((stderr,"icmp_type/code=%d/%d",icp->icmp_type,icp->icmp_code));
                    dump_packet(probe->contents,probe->size);
                }
            } else if (res==0) {
                 DEBUG_MSG((stderr,"probe timeout"));
                 return BP_RES_TIMEOUT;
            } else {
                 DEBUG_MSG((stderr,"res=%d errno=%d",res,errno));
                 return res;
            }
            /*DEBUG_MSG((stderr,"*** retrying"));*/
        }
        break;
    }

    return ret;
}

int probe_close(bp_handle handle)
{
    bp_state_t* state;

    if (handle==NULL) {
    errno=EINVAL;
    return -1;
    }
    state=handle2state(handle);

    if (state->pattern!=NULL)
    free(state->pattern);
    icmp_close(state->icmp_handle);
    if (state->packet!=NULL)
    free(state->packet);
    free(state);
    return 0;
}