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>