C言語入門(57) 実際にプログラムを読んで理解を深めよう ping編(1)

Posted: 2013年09月19日

前回までで2つめのコマンドのソースを解説してきました。

そのシリーズとして今回からpingコマンドのソースを解説していきます。

pingコマンドは、

  • ネットワーク上の端末に対してICMPを送信

します。

ネットワークの疎通確認などで使用します。

最新のtailコマンドのソースは
http://svnweb.freebsd.org/base/head/sbin/ping/
です。

今回解説するソース
/wp-content/uploads/2013/09/ping.zip

です。

前回と同様に初回はエントリーポイントから
ping.c main関数です。

ping.cは、main関数が長いため複数回を使用して
中身を見ていきます。

プログラムプログラム:

int
main(int argc, char *const *argv)
{
    struct sockaddr_in from, sock_in;
    struct in_addr ifaddr;
    struct timeval last, intvl;
    struct iovec iov;
    struct ip *ip;
    struct msghdr msg;
    struct sigaction si_sa;
    size_t sz;
    u_char *datap, packet[IP_MAXPACKET] __aligned(4);
    char *ep, *source, *target, *payload;
    struct hostent *hp;
#ifdef IPSEC_POLICY_IPSEC
    char *policy_in, *policy_out;
#endif
    struct sockaddr_in *to;
    double t;
    u_long alarmtimeout, ultmp;
    int almost_done, ch, df, hold, i, icmp_len, mib[4], preload, sockerrno,
        tos, ttl;
    char ctrl[CMSG_SPACE(sizeof(struct timeval))];
    char hnamebuf[MAXHOSTNAMELEN], snamebuf[MAXHOSTNAMELEN];
#ifdef IP_OPTIONS
    char rspace[MAX_IPOPTLEN];  /* record route space */
#endif
    unsigned char loop, mttl;

    payload = source = NULL;
#ifdef IPSEC_POLICY_IPSEC
    policy_in = policy_out = NULL;
#endif

    /*
     * Do the stuff that we need root priv's for *first*, and
     * then drop our setuid bit.  Save error reporting for
     * after arg parsing.
     */
    s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    sockerrno = errno;

    if (setuid(getuid()) != 0)
        err(EX_NOPERM, "setuid() failed");
    uid = getuid();

    alarmtimeout = df = preload = tos = 0;

    outpack = outpackhdr + sizeof(struct ip);
    while ((ch = getopt(argc, argv,
        "Aac:DdfG:g:h:I:i:Ll:M:m:nop:QqRrS:s:T:t:vW:z:"
#ifdef IPSEC
#ifdef IPSEC_POLICY_IPSEC
        "P:"
#endif /*IPSEC_POLICY_IPSEC*/
#endif /*IPSEC*/
        )) != -1)
    {
        switch(ch) {
        case 'A':
            options |= F_MISSED;
            break;
        case 'a':
            options |= F_AUDIBLE;
            break;
        case 'c':
            ultmp = strtoul(optarg, &ep, 0);
            if (*ep || ep == optarg || ultmp > LONG_MAX || !ultmp)
                errx(EX_USAGE,
                    "invalid count of packets to transmit: `%s'",
                    optarg);
            npackets = ultmp;
            break;
        case 'D':
            options |= F_HDRINCL;
            df = 1;
            break;
        case 'd':
            options |= F_SO_DEBUG;
            break;
        case 'f':
            if (uid) {
                errno = EPERM;
                err(EX_NOPERM, "-f flag");
            }
            options |= F_FLOOD;
            setbuf(stdout, (char *)NULL);
            break;
        case 'G': /* Maximum packet size for ping sweep */
            ultmp = strtoul(optarg, &ep, 0);
            if (*ep || ep == optarg)
                errx(EX_USAGE, "invalid packet size: `%s'",
                    optarg);
            if (uid != 0 && ultmp > DEFDATALEN) {
                errno = EPERM;
                err(EX_NOPERM,
                    "packet size too large: %lu > %u",
                    ultmp, DEFDATALEN);
            }
            options |= F_SWEEP;
            sweepmax = ultmp;
            break;
        case 'g': /* Minimum packet size for ping sweep */
            ultmp = strtoul(optarg, &ep, 0);
            if (*ep || ep == optarg)
                errx(EX_USAGE, "invalid packet size: `%s'",
                    optarg);
            if (uid != 0 && ultmp > DEFDATALEN) {
                errno = EPERM;
                err(EX_NOPERM,
                    "packet size too large: %lu > %u",
                    ultmp, DEFDATALEN);
            }
            options |= F_SWEEP;
            sweepmin = ultmp;
            break;
        case 'h': /* Packet size increment for ping sweep */
            ultmp = strtoul(optarg, &ep, 0);
            if (*ep || ep == optarg || ultmp < 1)
                errx(EX_USAGE, "invalid increment size: `%s'",
                    optarg);
            if (uid != 0 && ultmp > DEFDATALEN) {
                errno = EPERM;
                err(EX_NOPERM,
                    "packet size too large: %lu > %u",
                    ultmp, DEFDATALEN);
            }
            options |= F_SWEEP;
            sweepincr = ultmp;
            break;
        case 'I':       /* multicast interface */
            if (inet_aton(optarg, &ifaddr) == 0)
                errx(EX_USAGE,
                    "invalid multicast interface: `%s'",
                    optarg);
            options |= F_MIF;
            break;
        case 'i':       /* wait between sending packets */
            t = strtod(optarg, &ep) * 1000.0;
            if (*ep || ep == optarg || t > (double)INT_MAX)
                errx(EX_USAGE, "invalid timing interval: `%s'",
                    optarg);
            options |= F_INTERVAL;
            interval = (int)t;
            if (uid && interval < 1000) {
                errno = EPERM;
                err(EX_NOPERM, "-i interval too short");
            }
            break;
        case 'L':
            options |= F_NOLOOP;
            loop = 0;
            break;
        case 'l':
            ultmp = strtoul(optarg, &ep, 0);
            if (*ep || ep == optarg || ultmp > INT_MAX)
                errx(EX_USAGE,
                    "invalid preload value: `%s'", optarg);
            if (uid) {
                errno = EPERM;
                err(EX_NOPERM, "-l flag");
            }
            preload = ultmp;
            break;
        case 'M':
            switch(optarg[0]) {
            case 'M':
            case 'm':
                options |= F_MASK;
                break;
            case 'T':
            case 't':
                options |= F_TIME;
                break;
            default:
                errx(EX_USAGE, "invalid message: `%c'", optarg[0]);
                break;
            }
            break;
        case 'm':       /* TTL */
            ultmp = strtoul(optarg, &ep, 0);
            if (*ep || ep == optarg || ultmp > MAXTTL)
                errx(EX_USAGE, "invalid TTL: `%s'", optarg);
            ttl = ultmp;
            options |= F_TTL;
            break;
        case 'n':
            options |= F_NUMERIC;
            break;
        case 'o':
            options |= F_ONCE;
            break;
#ifdef IPSEC
#ifdef IPSEC_POLICY_IPSEC
        case 'P':
            options |= F_POLICY;
            if (!strncmp("in", optarg, 2))
                policy_in = strdup(optarg);
            else if (!strncmp("out", optarg, 3))
                policy_out = strdup(optarg);
            else
                errx(1, "invalid security policy");
            break;
#endif /*IPSEC_POLICY_IPSEC*/
#endif /*IPSEC*/
        case 'p':       /* fill buffer with user pattern */
            options |= F_PINGFILLED;
            payload = optarg;
            break;
        case 'Q':
            options |= F_QUIET2;
            break;
        case 'q':
            options |= F_QUIET;
            break;
        case 'R':
            options |= F_RROUTE;
            break;
        case 'r':
            options |= F_SO_DONTROUTE;
            break;
        case 'S':
            source = optarg;
            break;
        case 's':       /* size of packet to send */
            ultmp = strtoul(optarg, &ep, 0);
            if (*ep || ep == optarg)
                errx(EX_USAGE, "invalid packet size: `%s'",
                    optarg);
            if (uid != 0 && ultmp > DEFDATALEN) {
                errno = EPERM;
                err(EX_NOPERM,
                    "packet size too large: %lu > %u",
                    ultmp, DEFDATALEN);
            }
            datalen = ultmp;
            break;
        case 'T':       /* multicast TTL */
            ultmp = strtoul(optarg, &ep, 0);
            if (*ep || ep == optarg || ultmp > MAXTTL)
                errx(EX_USAGE, "invalid multicast TTL: `%s'",
                    optarg);
            mttl = ultmp;
            options |= F_MTTL;
            break;
        case 't':
            alarmtimeout = strtoul(optarg, &ep, 0);
            if ((alarmtimeout < 1) || (alarmtimeout == ULONG_MAX))
                errx(EX_USAGE, "invalid timeout: `%s'",
                    optarg);
            if (alarmtimeout > MAXALARM)
                errx(EX_USAGE, "invalid timeout: `%s' > %d",
                    optarg, MAXALARM);
            alarm((int)alarmtimeout);
            break;
        case 'v':
            options |= F_VERBOSE;
            break;
        case 'W':       /* wait ms for answer */
            t = strtod(optarg, &ep);
            if (*ep || ep == optarg || t > (double)INT_MAX)
                errx(EX_USAGE, "invalid timing interval: `%s'",
                    optarg);
            options |= F_WAITTIME;
            waittime = (int)t;
            break;
        case 'z':
            options |= F_HDRINCL;
            ultmp = strtoul(optarg, &ep, 0);
            if (*ep || ep == optarg || ultmp > MAXTOS)
                errx(EX_USAGE, "invalid TOS: `%s'", optarg);
            tos = ultmp;
            break;
        default:
            usage();
        }
    }

    if (argc - optind != 1)
        usage();

解説解説:

socketをICMPでオープンした後

setuidとgetuidを用いて
アクセス権の確認を行なっています。
(この解釈があっているかは不明)

引数の確認を行なっています。

引数は以下のような意味です。 (usageから引用)

ping [-AaDdfnoQqRrv] [-c count] [-G sweepmaxsize] [-g sweepminsize]
[-h sweepincrsize] [-i wait] [-l preload] [-M mask | time] [-m ttl]
” SECOPT ” [-p pattern] [-S src_addr] [-s packetsize] [-t timeout]
[-W waittime] [-z tos] host
ping [-AaDdfLnoQqRrv] [-c count] [-I iface] [-i wait] [-l preload]
[-M mask | time] [-m ttl]” SECOPT ” [-p pattern] [-S src_addr]
[-s packetsize] [-T ttl] [-t timeout] [-W waittime]
[-z tos] mcast-group

カテゴリー: プログラム, 入門 | タグ: , , | コメント無し »

コメント