Annotation of embedaddon/hping2/scan.c, revision 1.1

1.1     ! misho       1: /* Scanner mode for hping2
        !             2:  * Copyright(C) 2003 Salvatore Sanfilippo
        !             3:  * All rights reserved */
        !             4: 
        !             5: /* TODO:
        !             6:  * an application-level aware UDP scanner.
        !             7:  * add ICMP handling in replies.
        !             8:  * The algorithm is far from be optimal, also there isn't a clear
        !             9:  * way to delay smaller amounts of time then usleep(1) without
        !            10:  * to use a dummy loop.
        !            11:  * */
        !            12: 
        !            13: #include <stdio.h>
        !            14: #include <stdlib.h>
        !            15: #include <string.h>
        !            16: #include <sys/types.h>
        !            17: #if 0
        !            18: #include <sys/ipc.h>
        !            19: #endif
        !            20: #include <sys/shm.h>
        !            21: #include <sys/sem.h>
        !            22: #include <unistd.h>
        !            23: #include <netdb.h>
        !            24: #include <sys/time.h>
        !            25: #include <signal.h>
        !            26: 
        !            27: #include <sys/socket.h>
        !            28: #include <netinet/in.h>
        !            29: #include <arpa/inet.h>
        !            30: 
        !            31: #include <errno.h>
        !            32: #include <fcntl.h>
        !            33: 
        !            34: #if 0
        !            35: #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
        !            36: /* union semun is defined by including <sys/sem.h> */
        !            37: #else
        !            38: /* according to X/OPEN we have to define it ourselves */
        !            39: union semun {
        !            40:        int val;                    /* value for SETVAL */
        !            41:        struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
        !            42:        unsigned short int *array;  /* array for GETALL, SETALL */
        !            43:        struct seminfo *__buf;      /* buffer for IPC_INFO */
        !            44: };
        !            45: #endif
        !            46: #endif
        !            47: 
        !            48: #include "hping2.h"
        !            49: #include "globals.h"
        !            50: #include "hstring.h"
        !            51: 
        !            52: #define SEM_MODE 0777
        !            53: #define MAXPORT 65535
        !            54: 
        !            55: int opt_scan_probes = 8;
        !            56: float avrgms = 0;
        !            57: int avrgcount = 0;
        !            58: 
        !            59: /* ---------------------------- data structures ----------------------------- */
        !            60: 
        !            61: /* Note that while we don't use any kind of locking, to access
        !            62:  * this fields is safe. the 'retry' field is only accessed by the
        !            63:  * sendinf half, while the 'active' field is set by the receiver
        !            64:  * and tested by the sender so atomicity isn't an issue. */
        !            65: struct portinfo {
        !            66:        int active;
        !            67:        int retry;
        !            68:        time_t sentms; /* Upss... added this that requires locking, FIXME */
        !            69: };
        !            70: 
        !            71: /* ------------------------- shared memory related -------------------------- */
        !            72: 
        !            73: static int id; /* shared memory id */
        !            74: 
        !            75: static int shm_creat(int size)
        !            76: {
        !            77:        id = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777);
        !            78:        if (id == -1)
        !            79:        {
        !            80:                perror("[shm_creat] shmget");
        !            81:                return -1; /* on error -1 */
        !            82:        }
        !            83:        return id; /* on success > 0 */
        !            84: }
        !            85: 
        !            86: static void *shm_attach(void)
        !            87: {
        !            88:        void *shared;
        !            89: 
        !            90:        shared = shmat(id, 0, 0);
        !            91:        if (shared == (void*) -1)
        !            92:        {
        !            93:                perror("[shm_attach] shmat");
        !            94:                return NULL; /* on error NULL */
        !            95:        }
        !            96:        return shared; /* on success the address */
        !            97: }
        !            98: 
        !            99: static int shm_rm(void)
        !           100: {
        !           101:        struct shmid_ds shmemds;
        !           102: 
        !           103:        return shmctl(id, IPC_RMID, &shmemds);
        !           104: }
        !           105: 
        !           106: static int shm_detach(void *addr)
        !           107: {
        !           108:        return shmdt(addr);
        !           109: }
        !           110: 
        !           111: static void *shm_init(int size)
        !           112: {
        !           113:        if (shm_creat(size) == -1)
        !           114:                return NULL;
        !           115:        return shm_attach();
        !           116: }
        !           117: 
        !           118: static void shm_close(void *addr)
        !           119: {
        !           120:        shm_detach(addr);
        !           121:        shm_rm();
        !           122: }
        !           123: 
        !           124: /* ------------------------------ locking ---------------------------------- */
        !           125: 
        !           126: /* Note that a mutex can't be used with shared memory (on Linux), the only left
        !           127:  * option is a semaphore, but I tried to protect the critical code
        !           128:  * using the functions above: the scanner becomes too slow. For now
        !           129:  * it's better to have nothing at all, for the future we need something
        !           130:  * like a spinlock. (btw, note that the code should be safe on x86) */
        !           131: 
        !           132: /* I left this code here, just in the case it will be useful for testing */
        !           133: #if 0
        !           134: static int sem_init(void)
        !           135: {
        !           136:        int semid, sem_key;
        !           137: 
        !           138:        if ((sem_key = ftok("/tmp/hpingscansem", 1)) == -1) {
        !           139:                perror("ftok");
        !           140:                exit(1);
        !           141:        }
        !           142: 
        !           143:        /* Semi-safe semaphore initialization from R.Stevens */
        !           144: 
        !           145:        /* Try to create the semaphore with EXCL */
        !           146:        if ((semid = semget(sem_key, 1, IPC_CREAT|IPC_EXCL|SEM_MODE)) != -1) {
        !           147:                /* success, we need to initialize it */
        !           148:                union semun arg;
        !           149: 
        !           150:                arg.val = 1;
        !           151:                if (semctl(semid, 0, SETVAL, arg) == -1) {
        !           152:                        perror("semctl");
        !           153:                        exit(1);
        !           154:                }
        !           155:        } else if (errno == EEXIST) {
        !           156:                if ((semid = semget(sem_key, 1, SEM_MODE)) == -1) {
        !           157:                        perror("semget");
        !           158:                        exit(1);
        !           159:                }
        !           160:        } else {
        !           161:                perror("semget");
        !           162:                exit(1);
        !           163:        }
        !           164:        return semid;
        !           165: }
        !           166: 
        !           167: static int ports_lock(int semid)
        !           168: {
        !           169:        struct sembuf op[1];
        !           170: 
        !           171:        op[0].sem_num = 0;
        !           172:        op[0].sem_op = -1;
        !           173:        op[0].sem_flg = SEM_UNDO;
        !           174:        return semop(semid, op, 1);
        !           175: }
        !           176: 
        !           177: static int ports_unlock(int semid)
        !           178: {
        !           179:        struct sembuf op[1];
        !           180: 
        !           181:        op[0].sem_num = 0;
        !           182:        op[0].sem_op = +1;
        !           183:        op[0].sem_flg = SEM_UNDO;
        !           184:        return semop(semid, op, 1);
        !           185: }
        !           186: #endif
        !           187: 
        !           188: /* -------------------------------- misc ----------------------------------- */
        !           189: static char *tcp_strflags(char *s, unsigned int flags)
        !           190: {
        !           191:        char *ftab = "FSRPAYXY", *p = s;
        !           192:        int bit = 0;
        !           193: 
        !           194:        memset(s, '.', 8);
        !           195:        s[8] = '\0';
        !           196:        while(bit < 8) {
        !           197:                if (flags & (1 << bit))
        !           198:                        p[bit] = ftab[bit];
        !           199:                bit++;
        !           200:        }
        !           201:        return s;
        !           202: }
        !           203: 
        !           204: static char *port_to_name(int port)
        !           205: {
        !           206:        struct servent *se;
        !           207: 
        !           208:        se = getservbyport(htons(port), NULL);
        !           209:        if (!se)
        !           210:                return "";
        !           211:        else
        !           212:                return se->s_name;
        !           213: }
        !           214: 
        !           215: /* ----------------------------- ports parsing ------------------------------ */
        !           216: static int parse_ports(struct portinfo *pi, char *ports)
        !           217: {
        !           218:        char *args[32], *p = strdup(ports);
        !           219:        int argc, j, i;
        !           220: 
        !           221:        if (!p) {
        !           222:                fprintf(stderr, "Out of memory");
        !           223:                return 1;
        !           224:        }
        !           225:        argc = strftok(",", ports, args, 32);
        !           226:        for (j = 0; j < argc; j++) {
        !           227:                int neg = 0;
        !           228:                char *a = args[j];
        !           229: 
        !           230:                /* ports negation */
        !           231:                if (a[0] == '!') {
        !           232:                        neg = 1;
        !           233:                        a++;
        !           234:                }
        !           235:                /* range */
        !           236:                if (strchr(a, '-')) {
        !           237:                        char *range[2];
        !           238:                        int low, high;
        !           239: 
        !           240:                        strftok("-", a, range, 2);
        !           241:                        if (!strisnum(range[0]) || !strisnum(range[1]))
        !           242:                                goto err; /* syntax error */
        !           243:                        low = strtol(range[0], NULL, 0);
        !           244:                        high = strtol(range[1], NULL, 0);
        !           245:                        if (low > high) {
        !           246:                                int t;
        !           247:                                t = high;
        !           248:                                high = low;
        !           249:                                low = t;
        !           250:                        }
        !           251:                        for (i = low; i <= high; i++)
        !           252:                                pi[i].active = !neg;
        !           253:                /* all the ports */
        !           254:                } else if (!strcmp(a, "all")) {
        !           255:                        for (i = 0; i <= MAXPORT; i++)
        !           256:                                pi[i].active = !neg;
        !           257:                /* /etc/services ports */
        !           258:                } else if (!strcmp(a, "known")) {
        !           259:                        struct servent *se;
        !           260:                        setservent(0);
        !           261:                        while((se = getservent()) != NULL) {
        !           262:                                int port = ntohs(se->s_port);
        !           263:                                if (port < 0 || port > MAXPORT)
        !           264:                                        continue;
        !           265:                                pi[port].active = !neg;
        !           266:                        }
        !           267:                /* a single port */
        !           268:                } else {
        !           269:                        int port;
        !           270:                        if (!strisnum(a))
        !           271:                                goto err; /* syntax error */
        !           272:                        port = strtol(a, NULL, 0);
        !           273:                        if (port < 0 || port > MAXPORT)
        !           274:                                goto err; /* syntax error */
        !           275:                        pi[port].active = !neg;
        !           276:                }
        !           277:        }
        !           278:        free(p);
        !           279:        return 0;
        !           280: err:
        !           281:        free(p);
        !           282:        return 1;
        !           283: }
        !           284: 
        !           285: /* -------------------------------- output ---------------------------------- */
        !           286: static void sender(struct portinfo *pi)
        !           287: {
        !           288:        int i, retry = 0;
        !           289:        time_t start_time;
        !           290: 
        !           291:        start_time = get_midnight_ut_ms();
        !           292: 
        !           293:        while(1) {
        !           294:                int active = 0;
        !           295:                int recvd = 0;
        !           296:                retry ++;
        !           297:                for (i = 0; i < MAXPORT; i++) {
        !           298:                        if (pi[i].active && pi[i].retry) {
        !           299:                                active++;
        !           300:                                pi[i].retry--;
        !           301:                                sequence = -1;
        !           302:                                dst_port = i;
        !           303:                                pi[i].sentms = get_midnight_ut_ms();
        !           304:                                send_tcp();
        !           305:                                if (opt_waitinusec) {
        !           306:                                        if (usec_delay.it_interval.tv_usec)
        !           307:                                                usleep(usec_delay.it_interval.tv_usec);
        !           308:                                } else {
        !           309:                                        sleep(sending_wait);
        !           310:                                }
        !           311:                        }
        !           312:                }
        !           313:                avrgms = (float) pi[MAXPORT+1].active;
        !           314:                if (retry >= 3) {
        !           315:                        if (opt_debug)
        !           316:                                printf("AVRGMS %f\n", avrgms);
        !           317:                        if (avrgms)
        !           318:                                usleep((int) (avrgms*1000));
        !           319:                        else
        !           320:                                sleep(1);
        !           321:                }
        !           322:                for (i = 0; i < MAXPORT; i++) {
        !           323:                        if (!pi[i].active && pi[i].retry)
        !           324:                                recvd++;
        !           325:                }
        !           326:                /* More to scan? */
        !           327:                if (!active) {
        !           328:                        if (!recvd)
        !           329:                                sleep(1);
        !           330:                        fprintf(stderr, "All replies received. Done.\n");
        !           331:                        printf("Not responding ports: ");
        !           332:                        for (i = 0; i < MAXPORT; i++) {
        !           333:                                if (pi[i].active && !pi[i].retry)
        !           334:                                        printf("(%d %.11s) ", i, port_to_name(i));
        !           335:                        }
        !           336:                        printf("\n");
        !           337:                        exit(0);
        !           338:                }
        !           339:                /* Are we sending too fast? */
        !           340:                if ((!recvd && opt_waitinusec &&
        !           341:                    usec_delay.it_interval.tv_usec == 0 &&
        !           342:                    (get_midnight_ut_ms() - start_time) > 500) ||
        !           343:                        (opt_scan_probes-retry) <= 2)
        !           344:                {
        !           345:                        if (opt_debug)
        !           346:                                printf("SLOWING DONW\n");
        !           347:                        usec_delay.it_interval.tv_usec *= 10;
        !           348:                        usec_delay.it_interval.tv_usec ++;
        !           349:                }
        !           350:        }
        !           351: }
        !           352: 
        !           353: /* -------------------------------- input  ---------------------------------- */
        !           354: static void receiver(struct portinfo *pi, int childpid)
        !           355: {
        !           356:        struct myiphdr ip;
        !           357:        char packet[IP_MAX_SIZE+linkhdr_size];
        !           358: 
        !           359:        while(1)
        !           360:        {
        !           361:                int len, iplen;
        !           362: 
        !           363:                len = read_packet(packet, IP_MAX_SIZE+linkhdr_size);
        !           364:                if (len == -1) {
        !           365:                        perror("read_packet");
        !           366:                        continue;
        !           367:                }
        !           368:                /* minimal sanity checks */
        !           369:                if (len < linkhdr_size)
        !           370:                        continue;
        !           371:                iplen = len - linkhdr_size;
        !           372:                if (iplen < sizeof(struct myiphdr))
        !           373:                        continue;
        !           374:                /* copy the ip header in an access-safe place */
        !           375:                memcpy(&ip, packet+linkhdr_size, sizeof(ip));
        !           376:                /* check if the dest IP matches */
        !           377:                if (memcmp(&ip.daddr, &local.sin_addr, sizeof(ip.daddr)))
        !           378:                        continue;
        !           379:                /* check if the source IP matches */
        !           380:                if (ip.protocol != IPPROTO_ICMP &&
        !           381:                    memcmp(&ip.saddr, &remote.sin_addr, sizeof(ip.saddr)))
        !           382:                        continue;
        !           383:                if (ip.protocol == IPPROTO_TCP) {
        !           384:                        struct mytcphdr tcp;
        !           385:                        int iphdrlen = ip.ihl << 2;
        !           386:                        char flags[16];
        !           387:                        time_t rttms;
        !           388:                        int sport;
        !           389: 
        !           390:                        /* more sanity checks */
        !           391:                        if ((iplen - iphdrlen) < sizeof(tcp))
        !           392:                                continue;
        !           393:                        /* time to copy the TCP header in a safe place */
        !           394:                        memcpy(&tcp, packet+linkhdr_size+iphdrlen, sizeof(tcp));
        !           395: 
        !           396:                        /* check if the TCP dest port matches */
        !           397: #if 0
        !           398:                        printf("SRC: %d DST: %d\n",
        !           399:                                        ntohs(tcp.th_sport),
        !           400:                                        ntohs(tcp.th_dport));
        !           401: #endif
        !           402:                        if (ntohs(tcp.th_dport) != initsport)
        !           403:                                continue;
        !           404:                        sport = htons(tcp.th_sport);
        !           405:                        if (pi[sport].active == 0)
        !           406:                                continue;
        !           407: 
        !           408: 
        !           409:                        /* Note that we don't care about a wrote RTT
        !           410:                         * result due to resend on the same port. */
        !           411:                        rttms = get_midnight_ut_ms() - pi[sport].sentms;
        !           412: 
        !           413:                        avrgcount++;
        !           414:                        avrgms = (avrgms*(avrgcount-1)/avrgcount)+(rttms/avrgcount);
        !           415:                        /* The avrg RTT is shared using shared memory,
        !           416:                         * no locking... */
        !           417:                        pi[MAXPORT+1].active = (int) avrgms;
        !           418: 
        !           419:                        tcp_strflags(flags, tcp.th_flags);
        !           420: #if 0
        !           421:                        printf("%5d: %s %3d %5d %5d %10ld (%2d)\n",
        !           422:                                        sport,
        !           423:                                        flags,
        !           424:                                        ip.ttl,
        !           425:                                        ip.id,
        !           426:                                        ntohs(tcp.th_win),
        !           427:                                        (long) rttms,
        !           428:                                        opt_scan_probes-(pi[sport].retry));
        !           429: #endif
        !           430:                        if ((tcp.th_flags & TH_SYN) || opt_verbose) {
        !           431:                        printf("%5d %-11.11s: %s %3d %5d %5d\n",
        !           432:                                        sport,
        !           433:                                        port_to_name(sport),
        !           434:                                        flags,
        !           435:                                        ip.ttl,
        !           436:                                        ip.id,
        !           437:                                        ntohs(tcp.th_win));
        !           438:                        fflush(stdout);
        !           439:                        }
        !           440:                        pi[sport].active = 0;
        !           441:                } else if (ip.protocol == IPPROTO_ICMP) {
        !           442:                        struct myicmphdr icmp;
        !           443:                        struct myiphdr subip;
        !           444:                        struct mytcphdr subtcp;
        !           445:                        int iphdrlen = ip.ihl << 2;
        !           446:                        unsigned char *p;
        !           447:                        int port;
        !           448:                        struct in_addr gwaddr;
        !           449: 
        !           450:                        /* more sanity checks, we are only interested
        !           451:                         * in ICMP quoting the original packet. */
        !           452:                        if ((iplen - iphdrlen) < sizeof(icmp)+sizeof(subip)+sizeof(subtcp))
        !           453:                                continue;
        !           454:                        /* time to copy headers in a safe place */
        !           455:                        p = packet+linkhdr_size+iphdrlen;
        !           456:                        memcpy(&icmp, p, sizeof(subtcp));
        !           457:                        p += sizeof(icmp);
        !           458:                        memcpy(&subip, p, sizeof(ip));
        !           459:                        p += sizeof(ip);
        !           460:                        memcpy(&subtcp, p, sizeof(subtcp));
        !           461: 
        !           462:                        /* Check if the ICMP quoted packet matches */
        !           463:                        /* check if the source IP matches */
        !           464:                        if (memcmp(&subip.saddr, &local.sin_addr, sizeof(subip.saddr)))
        !           465:                                continue;
        !           466:                        /* check if the destination IP matches */
        !           467:                        if (memcmp(&subip.daddr, &remote.sin_addr, sizeof(subip.daddr)))
        !           468:                                continue;
        !           469:                        /* check if the quoted TCP packet port matches */
        !           470:                        if (ntohs(subtcp.th_sport) != initsport)
        !           471:                                continue;
        !           472:                        port = htons(subtcp.th_dport);
        !           473:                        if (pi[port].active == 0)
        !           474:                                continue;
        !           475:                        pi[port].active = 0;
        !           476:                        memcpy(&gwaddr.s_addr, &ip.saddr, 4);
        !           477:                        printf("%5d:                      %3d %5d       (ICMP %3d %3d from %s)\n",
        !           478:                                        port,
        !           479:                                        ip.ttl,
        !           480:                                        ntohs(ip.id),
        !           481:                                        icmp.type,
        !           482:                                        icmp.code,
        !           483:                                        inet_ntoa(gwaddr));
        !           484:                }
        !           485:        }
        !           486: }
        !           487: 
        !           488: /* ---------------------------------- main ---------------------------------- */
        !           489: static void do_exit(int sid)
        !           490: {
        !           491:        exit(0);
        !           492: }
        !           493: 
        !           494: void scanmain(void)
        !           495: {
        !           496:        struct portinfo *pi;
        !           497:        int ports = 0, i;
        !           498:        int childpid;
        !           499: 
        !           500:        pi = shm_init(sizeof(*pi)*(MAXPORT+2));
        !           501:        pi[MAXPORT+1].active = 0; /* hold the average RTT */
        !           502:        if (pi == NULL) {
        !           503:                fprintf(stderr, "Unable to create the shared memory");
        !           504:                shm_close(pi);
        !           505:                exit(1);
        !           506:        }
        !           507:        for (i = 0; i <= MAXPORT; i++) {
        !           508:                pi[i].active = 0;
        !           509:                pi[i].retry = opt_scan_probes;
        !           510:        }
        !           511:        if (parse_ports(pi, opt_scanports)) {
        !           512:                fprintf(stderr, "Ports syntax error for scan mode\n");
        !           513:                shm_close(pi);
        !           514:                exit(1);
        !           515:        }
        !           516:        for (i = 0; i <= MAXPORT; i++) {
        !           517:                if (!pi[i].active)
        !           518:                        pi[i].retry = 0;
        !           519:        }
        !           520:        for (i = 0; i <= MAXPORT; i++)
        !           521:                ports += pi[i].active;
        !           522:        fprintf(stderr, "%d ports to scan, use -V to see all the replies\n", ports);
        !           523:        fprintf(stderr, "+----+-----------+---------+---+-----+-----+\n");
        !           524:        fprintf(stderr, "|port| serv name |  flags  |ttl| id  | win |\n");
        !           525:        fprintf(stderr, "+----+-----------+---------+---+-----+-----+\n");
        !           526: 
        !           527:        /* We are ready to fork, the input and output parts
        !           528:         * are separated processes */
        !           529:        if ((childpid = fork()) == -1) {
        !           530:                perror("fork");
        !           531:                shm_close(pi);
        !           532:                exit(1);
        !           533:        }
        !           534:        /* The parent is the receiver, the child the sender.
        !           535:         * it's almost the same but this way is simpler
        !           536:         * to make it working in pipe with other commands like grep. */
        !           537:        if (childpid) { /* parent */
        !           538:                Signal(SIGCHLD, do_exit);
        !           539:                Signal(SIGINT, do_exit);
        !           540:                Signal(SIGTERM, do_exit);
        !           541:                receiver(pi, childpid);
        !           542:        } else {        /* child */
        !           543:                Signal(SIGINT, do_exit);
        !           544:                Signal(SIGTERM, do_exit);
        !           545:                sender(pi);
        !           546:        }
        !           547:        /* UNREACHED */
        !           548: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>