pgm_print(netdissect_options *ndo,
          register const u_char *bp, register u_int length,
          register const u_char *bp2)
{
	register const struct pgm_header *pgm;
	register const struct ip *ip;
	register char ch;
	uint16_t sport, dport;
	u_int nla_afnum;
	char nla_buf[INET6_ADDRSTRLEN];
	register const struct ip6_hdr *ip6;
	uint8_t opt_type, opt_len;
	uint32_t seq, opts_len, len, offset;

	pgm = (const struct pgm_header *)bp;
	ip = (const struct ip *)bp2;
	if (IP_V(ip) == 6)
		ip6 = (const struct ip6_hdr *)bp2;
	else
		ip6 = NULL;
	ch = '\0';
	if (!ND_TTEST(pgm->pgm_dport)) {
		if (ip6) {
 			ND_PRINT((ndo, "%s > %s: [|pgm]",
 				ip6addr_string(ndo, &ip6->ip6_src),
 				ip6addr_string(ndo, &ip6->ip6_dst)));
 		} else {
 			ND_PRINT((ndo, "%s > %s: [|pgm]",
 				ipaddr_string(ndo, &ip->ip_src),
 				ipaddr_string(ndo, &ip->ip_dst)));
 		}
		return;
 	}

 	sport = EXTRACT_16BITS(&pgm->pgm_sport);
	dport = EXTRACT_16BITS(&pgm->pgm_dport);

	if (ip6) {
		if (ip6->ip6_nxt == IPPROTO_PGM) {
			ND_PRINT((ndo, "%s.%s > %s.%s: ",
				ip6addr_string(ndo, &ip6->ip6_src),
				tcpport_string(ndo, sport),
				ip6addr_string(ndo, &ip6->ip6_dst),
				tcpport_string(ndo, dport)));
		} else {
			ND_PRINT((ndo, "%s > %s: ",
				tcpport_string(ndo, sport), tcpport_string(ndo, dport)));
		}
	} else {
		if (ip->ip_p == IPPROTO_PGM) {
			ND_PRINT((ndo, "%s.%s > %s.%s: ",
				ipaddr_string(ndo, &ip->ip_src),
				tcpport_string(ndo, sport),
				ipaddr_string(ndo, &ip->ip_dst),
				tcpport_string(ndo, dport)));
		} else {
			ND_PRINT((ndo, "%s > %s: ",
				tcpport_string(ndo, sport), tcpport_string(ndo, dport)));
		}
	}

	ND_TCHECK(*pgm);

        ND_PRINT((ndo, "PGM, length %u", EXTRACT_16BITS(&pgm->pgm_length)));

        if (!ndo->ndo_vflag)
            return;

	ND_PRINT((ndo, " 0x%02x%02x%02x%02x%02x%02x ",
		     pgm->pgm_gsid[0],
                     pgm->pgm_gsid[1],
                     pgm->pgm_gsid[2],
		     pgm->pgm_gsid[3],
                     pgm->pgm_gsid[4],
                     pgm->pgm_gsid[5]));
	switch (pgm->pgm_type) {
	case PGM_SPM: {
	    const struct pgm_spm *spm;

	    spm = (const struct pgm_spm *)(pgm + 1);
	    ND_TCHECK(*spm);
	    bp = (const u_char *) (spm + 1);

	    switch (EXTRACT_16BITS(&spm->pgms_nla_afi)) {
	    case AFNUM_INET:
		ND_TCHECK2(*bp, sizeof(struct in_addr));
		addrtostr(bp, nla_buf, sizeof(nla_buf));
		bp += sizeof(struct in_addr);
		break;
	    case AFNUM_INET6:
		ND_TCHECK2(*bp, sizeof(struct in6_addr));
		addrtostr6(bp, nla_buf, sizeof(nla_buf));
		bp += sizeof(struct in6_addr);
		break;
	    default:
		goto trunc;
		break;
	    }

	    ND_PRINT((ndo, "SPM seq %u trail %u lead %u nla %s",
			 EXTRACT_32BITS(&spm->pgms_seq),
                         EXTRACT_32BITS(&spm->pgms_trailseq),
			 EXTRACT_32BITS(&spm->pgms_leadseq),
                         nla_buf));
	    break;
	}

	case PGM_POLL: {
	    const struct pgm_poll *poll_msg;

	    poll_msg = (const struct pgm_poll *)(pgm + 1);
	    ND_TCHECK(*poll_msg);
	    ND_PRINT((ndo, "POLL seq %u round %u",
			 EXTRACT_32BITS(&poll_msg->pgmp_seq),
                         EXTRACT_16BITS(&poll_msg->pgmp_round)));
	    bp = (const u_char *) (poll_msg + 1);
	    break;
	}
	case PGM_POLR: {
	    const struct pgm_polr *polr;
	    uint32_t ivl, rnd, mask;

	    polr = (const struct pgm_polr *)(pgm + 1);
	    ND_TCHECK(*polr);
	    bp = (const u_char *) (polr + 1);

	    switch (EXTRACT_16BITS(&polr->pgmp_nla_afi)) {
	    case AFNUM_INET:
		ND_TCHECK2(*bp, sizeof(struct in_addr));
		addrtostr(bp, nla_buf, sizeof(nla_buf));
		bp += sizeof(struct in_addr);
		break;
	    case AFNUM_INET6:
		ND_TCHECK2(*bp, sizeof(struct in6_addr));
		addrtostr6(bp, nla_buf, sizeof(nla_buf));
		bp += sizeof(struct in6_addr);
		break;
	    default:
		goto trunc;
		break;
	    }

	    ND_TCHECK2(*bp, sizeof(uint32_t));
	    ivl = EXTRACT_32BITS(bp);
	    bp += sizeof(uint32_t);

	    ND_TCHECK2(*bp, sizeof(uint32_t));
	    rnd = EXTRACT_32BITS(bp);
	    bp += sizeof(uint32_t);

	    ND_TCHECK2(*bp, sizeof(uint32_t));
	    mask = EXTRACT_32BITS(bp);
	    bp += sizeof(uint32_t);

	    ND_PRINT((ndo, "POLR seq %u round %u nla %s ivl %u rnd 0x%08x "
			 "mask 0x%08x", EXTRACT_32BITS(&polr->pgmp_seq),
			 EXTRACT_16BITS(&polr->pgmp_round), nla_buf, ivl, rnd, mask));
	    break;
	}
	case PGM_ODATA: {
	    const struct pgm_data *odata;

	    odata = (const struct pgm_data *)(pgm + 1);
	    ND_TCHECK(*odata);
	    ND_PRINT((ndo, "ODATA trail %u seq %u",
			 EXTRACT_32BITS(&odata->pgmd_trailseq),
			 EXTRACT_32BITS(&odata->pgmd_seq)));
	    bp = (const u_char *) (odata + 1);
	    break;
	}

	case PGM_RDATA: {
	    const struct pgm_data *rdata;

	    rdata = (const struct pgm_data *)(pgm + 1);
	    ND_TCHECK(*rdata);
	    ND_PRINT((ndo, "RDATA trail %u seq %u",
			 EXTRACT_32BITS(&rdata->pgmd_trailseq),
			 EXTRACT_32BITS(&rdata->pgmd_seq)));
	    bp = (const u_char *) (rdata + 1);
	    break;
	}

	case PGM_NAK:
	case PGM_NULLNAK:
	case PGM_NCF: {
	    const struct pgm_nak *nak;
	    char source_buf[INET6_ADDRSTRLEN], group_buf[INET6_ADDRSTRLEN];

	    nak = (const struct pgm_nak *)(pgm + 1);
	    ND_TCHECK(*nak);
	    bp = (const u_char *) (nak + 1);

	    /*
	     * Skip past the source, saving info along the way
	     * and stopping if we don't have enough.
	     */
	    switch (EXTRACT_16BITS(&nak->pgmn_source_afi)) {
	    case AFNUM_INET:
		ND_TCHECK2(*bp, sizeof(struct in_addr));
		addrtostr(bp, source_buf, sizeof(source_buf));
		bp += sizeof(struct in_addr);
		break;
	    case AFNUM_INET6:
		ND_TCHECK2(*bp, sizeof(struct in6_addr));
		addrtostr6(bp, source_buf, sizeof(source_buf));
		bp += sizeof(struct in6_addr);
		break;
	    default:
		goto trunc;
		break;
	    }

	    /*
	     * Skip past the group, saving info along the way
 	     * and stopping if we don't have enough.
 	     */
 	    bp += (2 * sizeof(uint16_t));
	    ND_TCHECK_16BITS(bp);
 	    switch (EXTRACT_16BITS(bp)) {
 	    case AFNUM_INET:
 		ND_TCHECK2(*bp, sizeof(struct in_addr));
		addrtostr(bp, group_buf, sizeof(group_buf));
		bp += sizeof(struct in_addr);
		break;
	    case AFNUM_INET6:
		ND_TCHECK2(*bp, sizeof(struct in6_addr));
		addrtostr6(bp, group_buf, sizeof(group_buf));
		bp += sizeof(struct in6_addr);
		break;
	    default:
		goto trunc;
		break;
	    }

	    /*
	     * Options decoding can go here.
	     */
	    switch (pgm->pgm_type) {
		case PGM_NAK:
		    ND_PRINT((ndo, "NAK "));
		    break;
		case PGM_NULLNAK:
		    ND_PRINT((ndo, "NNAK "));
		    break;
		case PGM_NCF:
		    ND_PRINT((ndo, "NCF "));
		    break;
		default:
                    break;
	    }
	    ND_PRINT((ndo, "(%s -> %s), seq %u",
			 source_buf, group_buf, EXTRACT_32BITS(&nak->pgmn_seq)));
	    break;
	}

	case PGM_ACK: {
	    const struct pgm_ack *ack;

	    ack = (const struct pgm_ack *)(pgm + 1);
	    ND_TCHECK(*ack);
	    ND_PRINT((ndo, "ACK seq %u",
			 EXTRACT_32BITS(&ack->pgma_rx_max_seq)));
	    bp = (const u_char *) (ack + 1);
	    break;
	}

	case PGM_SPMR:
	    ND_PRINT((ndo, "SPMR"));
	    break;

	default:
	    ND_PRINT((ndo, "UNKNOWN type 0x%02x", pgm->pgm_type));
	    break;

	}
	if (pgm->pgm_options & PGM_OPT_BIT_PRESENT) {

	    /*
	     * make sure there's enough for the first option header
	     */
	    if (!ND_TTEST2(*bp, PGM_MIN_OPT_LEN)) {
		ND_PRINT((ndo, "[|OPT]"));
		return;
	    }

	    /*
	     * That option header MUST be an OPT_LENGTH option
	     * (see the first paragraph of section 9.1 in RFC 3208).
	     */
	    opt_type = *bp++;
	    if ((opt_type & PGM_OPT_MASK) != PGM_OPT_LENGTH) {
		ND_PRINT((ndo, "[First option bad, should be PGM_OPT_LENGTH, is %u]", opt_type & PGM_OPT_MASK));
		return;
	    }
	    opt_len = *bp++;
	    if (opt_len != 4) {
		ND_PRINT((ndo, "[Bad OPT_LENGTH option, length %u != 4]", opt_len));
		return;
	    }
	    opts_len = EXTRACT_16BITS(bp);
	    if (opts_len < 4) {
		ND_PRINT((ndo, "[Bad total option length %u < 4]", opts_len));
		return;
	    }
	    bp += sizeof(uint16_t);
	    ND_PRINT((ndo, " OPTS LEN %d", opts_len));
	    opts_len -= 4;

	    while (opts_len) {
		if (opts_len < PGM_MIN_OPT_LEN) {
		    ND_PRINT((ndo, "[Total option length leaves no room for final option]"));
		    return;
		}
		if (!ND_TTEST2(*bp, 2)) {
		    ND_PRINT((ndo, " [|OPT]"));
		    return;
		}
		opt_type = *bp++;
		opt_len = *bp++;
		if (opt_len < PGM_MIN_OPT_LEN) {
		    ND_PRINT((ndo, "[Bad option, length %u < %u]", opt_len,
		        PGM_MIN_OPT_LEN));
		    break;
		}
		if (opts_len < opt_len) {
		    ND_PRINT((ndo, "[Total option length leaves no room for final option]"));
		    return;
		}
		if (!ND_TTEST2(*bp, opt_len - 2)) {
		    ND_PRINT((ndo, " [|OPT]"));
		    return;
		}

		switch (opt_type & PGM_OPT_MASK) {
		case PGM_OPT_LENGTH:
#define PGM_OPT_LENGTH_LEN	(2+2)
		    if (opt_len != PGM_OPT_LENGTH_LEN) {
			ND_PRINT((ndo, "[Bad OPT_LENGTH option, length %u != %u]",
			    opt_len, PGM_OPT_LENGTH_LEN));
			return;
		    }
		    ND_PRINT((ndo, " OPTS LEN (extra?) %d", EXTRACT_16BITS(bp)));
		    bp += 2;
		    opts_len -= PGM_OPT_LENGTH_LEN;
		    break;

		case PGM_OPT_FRAGMENT:
#define PGM_OPT_FRAGMENT_LEN	(2+2+4+4+4)
		    if (opt_len != PGM_OPT_FRAGMENT_LEN) {
			ND_PRINT((ndo, "[Bad OPT_FRAGMENT option, length %u != %u]",
			    opt_len, PGM_OPT_FRAGMENT_LEN));
			return;
		    }
		    bp += 2;
		    seq = EXTRACT_32BITS(bp);
		    bp += 4;
		    offset = EXTRACT_32BITS(bp);
		    bp += 4;
		    len = EXTRACT_32BITS(bp);
		    bp += 4;
		    ND_PRINT((ndo, " FRAG seq %u off %u len %u", seq, offset, len));
		    opts_len -= PGM_OPT_FRAGMENT_LEN;
		    break;

		case PGM_OPT_NAK_LIST:
		    bp += 2;
		    opt_len -= 4;	/* option header */
		    ND_PRINT((ndo, " NAK LIST"));
		    while (opt_len) {
			if (opt_len < 4) {
			    ND_PRINT((ndo, "[Option length not a multiple of 4]"));
			    return;
			}
			ND_TCHECK2(*bp, 4);
			ND_PRINT((ndo, " %u", EXTRACT_32BITS(bp)));
			bp += 4;
			opt_len -= 4;
			opts_len -= 4;
		    }
		    break;

		case PGM_OPT_JOIN:
#define PGM_OPT_JOIN_LEN	(2+2+4)
		    if (opt_len != PGM_OPT_JOIN_LEN) {
			ND_PRINT((ndo, "[Bad OPT_JOIN option, length %u != %u]",
			    opt_len, PGM_OPT_JOIN_LEN));
			return;
		    }
		    bp += 2;
		    seq = EXTRACT_32BITS(bp);
		    bp += 4;
		    ND_PRINT((ndo, " JOIN %u", seq));
		    opts_len -= PGM_OPT_JOIN_LEN;
		    break;

		case PGM_OPT_NAK_BO_IVL:
#define PGM_OPT_NAK_BO_IVL_LEN	(2+2+4+4)
		    if (opt_len != PGM_OPT_NAK_BO_IVL_LEN) {
			ND_PRINT((ndo, "[Bad OPT_NAK_BO_IVL option, length %u != %u]",
			    opt_len, PGM_OPT_NAK_BO_IVL_LEN));
			return;
		    }
		    bp += 2;
		    offset = EXTRACT_32BITS(bp);
		    bp += 4;
		    seq = EXTRACT_32BITS(bp);
		    bp += 4;
		    ND_PRINT((ndo, " BACKOFF ivl %u ivlseq %u", offset, seq));
		    opts_len -= PGM_OPT_NAK_BO_IVL_LEN;
		    break;

		case PGM_OPT_NAK_BO_RNG:
#define PGM_OPT_NAK_BO_RNG_LEN	(2+2+4+4)
		    if (opt_len != PGM_OPT_NAK_BO_RNG_LEN) {
			ND_PRINT((ndo, "[Bad OPT_NAK_BO_RNG option, length %u != %u]",
			    opt_len, PGM_OPT_NAK_BO_RNG_LEN));
			return;
		    }
		    bp += 2;
		    offset = EXTRACT_32BITS(bp);
		    bp += 4;
		    seq = EXTRACT_32BITS(bp);
		    bp += 4;
		    ND_PRINT((ndo, " BACKOFF max %u min %u", offset, seq));
		    opts_len -= PGM_OPT_NAK_BO_RNG_LEN;
		    break;

		case PGM_OPT_REDIRECT:
#define PGM_OPT_REDIRECT_FIXED_LEN	(2+2+2+2)
		    if (opt_len < PGM_OPT_REDIRECT_FIXED_LEN) {
			ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u < %u]",
			    opt_len, PGM_OPT_REDIRECT_FIXED_LEN));
			return;
		    }
		    bp += 2;
		    nla_afnum = EXTRACT_16BITS(bp);
		    bp += 2+2;
		    switch (nla_afnum) {
		    case AFNUM_INET:
			if (opt_len != PGM_OPT_REDIRECT_FIXED_LEN + sizeof(struct in_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u != %u + address size]",
			        opt_len, PGM_OPT_REDIRECT_FIXED_LEN));
			    return;
			}
			ND_TCHECK2(*bp, sizeof(struct in_addr));
			addrtostr(bp, nla_buf, sizeof(nla_buf));
			bp += sizeof(struct in_addr);
			opts_len -= PGM_OPT_REDIRECT_FIXED_LEN + sizeof(struct in_addr);
			break;
		    case AFNUM_INET6:
			if (opt_len != PGM_OPT_REDIRECT_FIXED_LEN + sizeof(struct in6_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u != %u + address size]",
			        PGM_OPT_REDIRECT_FIXED_LEN, opt_len));
			    return;
			}
			ND_TCHECK2(*bp, sizeof(struct in6_addr));
			addrtostr6(bp, nla_buf, sizeof(nla_buf));
			bp += sizeof(struct in6_addr);
			opts_len -= PGM_OPT_REDIRECT_FIXED_LEN + sizeof(struct in6_addr);
			break;
		    default:
			goto trunc;
			break;
		    }

		    ND_PRINT((ndo, " REDIRECT %s",  nla_buf));
		    break;

		case PGM_OPT_PARITY_PRM:
#define PGM_OPT_PARITY_PRM_LEN	(2+2+4)
		    if (opt_len != PGM_OPT_PARITY_PRM_LEN) {
			ND_PRINT((ndo, "[Bad OPT_PARITY_PRM option, length %u != %u]",
			    opt_len, PGM_OPT_PARITY_PRM_LEN));
			return;
		    }
		    bp += 2;
		    len = EXTRACT_32BITS(bp);
		    bp += 4;
		    ND_PRINT((ndo, " PARITY MAXTGS %u", len));
		    opts_len -= PGM_OPT_PARITY_PRM_LEN;
		    break;

		case PGM_OPT_PARITY_GRP:
#define PGM_OPT_PARITY_GRP_LEN	(2+2+4)
		    if (opt_len != PGM_OPT_PARITY_GRP_LEN) {
			ND_PRINT((ndo, "[Bad OPT_PARITY_GRP option, length %u != %u]",
			    opt_len, PGM_OPT_PARITY_GRP_LEN));
			return;
		    }
		    bp += 2;
		    seq = EXTRACT_32BITS(bp);
		    bp += 4;
		    ND_PRINT((ndo, " PARITY GROUP %u", seq));
		    opts_len -= PGM_OPT_PARITY_GRP_LEN;
		    break;

		case PGM_OPT_CURR_TGSIZE:
#define PGM_OPT_CURR_TGSIZE_LEN	(2+2+4)
		    if (opt_len != PGM_OPT_CURR_TGSIZE_LEN) {
			ND_PRINT((ndo, "[Bad OPT_CURR_TGSIZE option, length %u != %u]",
			    opt_len, PGM_OPT_CURR_TGSIZE_LEN));
			return;
		    }
		    bp += 2;
		    len = EXTRACT_32BITS(bp);
		    bp += 4;
		    ND_PRINT((ndo, " PARITY ATGS %u", len));
		    opts_len -= PGM_OPT_CURR_TGSIZE_LEN;
		    break;

		case PGM_OPT_NBR_UNREACH:
#define PGM_OPT_NBR_UNREACH_LEN	(2+2)
		    if (opt_len != PGM_OPT_NBR_UNREACH_LEN) {
			ND_PRINT((ndo, "[Bad OPT_NBR_UNREACH option, length %u != %u]",
			    opt_len, PGM_OPT_NBR_UNREACH_LEN));
			return;
		    }
		    bp += 2;
		    ND_PRINT((ndo, " NBR_UNREACH"));
		    opts_len -= PGM_OPT_NBR_UNREACH_LEN;
		    break;

		case PGM_OPT_PATH_NLA:
		    ND_PRINT((ndo, " PATH_NLA [%d]", opt_len));
		    bp += opt_len;
		    opts_len -= opt_len;
		    break;

		case PGM_OPT_SYN:
#define PGM_OPT_SYN_LEN	(2+2)
		    if (opt_len != PGM_OPT_SYN_LEN) {
			ND_PRINT((ndo, "[Bad OPT_SYN option, length %u != %u]",
			    opt_len, PGM_OPT_SYN_LEN));
			return;
		    }
		    bp += 2;
		    ND_PRINT((ndo, " SYN"));
		    opts_len -= PGM_OPT_SYN_LEN;
		    break;

		case PGM_OPT_FIN:
#define PGM_OPT_FIN_LEN	(2+2)
		    if (opt_len != PGM_OPT_FIN_LEN) {
			ND_PRINT((ndo, "[Bad OPT_FIN option, length %u != %u]",
			    opt_len, PGM_OPT_FIN_LEN));
			return;
		    }
		    bp += 2;
		    ND_PRINT((ndo, " FIN"));
		    opts_len -= PGM_OPT_FIN_LEN;
		    break;

		case PGM_OPT_RST:
#define PGM_OPT_RST_LEN	(2+2)
		    if (opt_len != PGM_OPT_RST_LEN) {
			ND_PRINT((ndo, "[Bad OPT_RST option, length %u != %u]",
			    opt_len, PGM_OPT_RST_LEN));
			return;
		    }
		    bp += 2;
		    ND_PRINT((ndo, " RST"));
		    opts_len -= PGM_OPT_RST_LEN;
		    break;

		case PGM_OPT_CR:
		    ND_PRINT((ndo, " CR"));
		    bp += opt_len;
		    opts_len -= opt_len;
		    break;

		case PGM_OPT_CRQST:
#define PGM_OPT_CRQST_LEN	(2+2)
		    if (opt_len != PGM_OPT_CRQST_LEN) {
			ND_PRINT((ndo, "[Bad OPT_CRQST option, length %u != %u]",
			    opt_len, PGM_OPT_CRQST_LEN));
			return;
		    }
		    bp += 2;
		    ND_PRINT((ndo, " CRQST"));
		    opts_len -= PGM_OPT_CRQST_LEN;
		    break;

		case PGM_OPT_PGMCC_DATA:
#define PGM_OPT_PGMCC_DATA_FIXED_LEN	(2+2+4+2+2)
		    if (opt_len < PGM_OPT_PGMCC_DATA_FIXED_LEN) {
			ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u < %u]",
			    opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN));
			return;
		    }
		    bp += 2;
		    offset = EXTRACT_32BITS(bp);
		    bp += 4;
		    nla_afnum = EXTRACT_16BITS(bp);
		    bp += 2+2;
		    switch (nla_afnum) {
		    case AFNUM_INET:
			if (opt_len != PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(struct in_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != %u + address size]",
			        opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN));
			    return;
			}
			ND_TCHECK2(*bp, sizeof(struct in_addr));
			addrtostr(bp, nla_buf, sizeof(nla_buf));
			bp += sizeof(struct in_addr);
			opts_len -= PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(struct in_addr);
			break;
		    case AFNUM_INET6:
			if (opt_len != PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(struct in6_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != %u + address size]",
			        opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN));
			    return;
			}
			ND_TCHECK2(*bp, sizeof(struct in6_addr));
			addrtostr6(bp, nla_buf, sizeof(nla_buf));
			bp += sizeof(struct in6_addr);
			opts_len -= PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(struct in6_addr);
			break;
		    default:
			goto trunc;
			break;
		    }

		    ND_PRINT((ndo, " PGMCC DATA %u %s", offset, nla_buf));
		    break;

		case PGM_OPT_PGMCC_FEEDBACK:
#define PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN	(2+2+4+2+2)
		    if (opt_len < PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN) {
			ND_PRINT((ndo, "[Bad PGM_OPT_PGMCC_FEEDBACK option, length %u < %u]",
			    opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN));
			return;
		    }
		    bp += 2;
		    offset = EXTRACT_32BITS(bp);
		    bp += 4;
		    nla_afnum = EXTRACT_16BITS(bp);
		    bp += 2+2;
		    switch (nla_afnum) {
		    case AFNUM_INET:
			if (opt_len != PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(struct in_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_PGMCC_FEEDBACK option, length %u != %u + address size]",
			        opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN));
			    return;
			}
			ND_TCHECK2(*bp, sizeof(struct in_addr));
			addrtostr(bp, nla_buf, sizeof(nla_buf));
			bp += sizeof(struct in_addr);
			opts_len -= PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(struct in_addr);
			break;
		    case AFNUM_INET6:
			if (opt_len != PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(struct in6_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_PGMCC_FEEDBACK option, length %u != %u + address size]",
			        opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN));
			    return;
			}
			ND_TCHECK2(*bp, sizeof(struct in6_addr));
			addrtostr6(bp, nla_buf, sizeof(nla_buf));
			bp += sizeof(struct in6_addr);
			opts_len -= PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(struct in6_addr);
			break;
		    default:
			goto trunc;
			break;
		    }

		    ND_PRINT((ndo, " PGMCC FEEDBACK %u %s", offset, nla_buf));
		    break;

		default:
		    ND_PRINT((ndo, " OPT_%02X [%d] ", opt_type, opt_len));
		    bp += opt_len;
		    opts_len -= opt_len;
		    break;
		}

		if (opt_type & PGM_OPT_END)
		    break;
	     }
	}

	ND_PRINT((ndo, " [%u]", length));
	if (ndo->ndo_packettype == PT_PGM_ZMTP1 &&
	    (pgm->pgm_type == PGM_ODATA || pgm->pgm_type == PGM_RDATA))
		zmtp1_print_datagram(ndo, bp, EXTRACT_16BITS(&pgm->pgm_length));

	return;

trunc:
	ND_PRINT((ndo, "[|pgm]"));
	if (ch != '\0')
		ND_PRINT((ndo, ">"));
}


CVE-2017-13032/RADIUS: Check whether a byte exists before testing its value.

Reverse the test in a for loop to test the length before testing whether
we have a null byte.

This fixes a buffer over-read discovered by Bhargava Shastry.

Add a test using the capture file supplied by the reporter(s), modified
so the capture file won't be rejected as an invalid capture.

Clean up other length tests while we're at it.

print_attr_string(netdissect_options *ndo,
                  register const u_char *data, u_int length, u_short attr_code)
{
   register u_int i;

   ND_TCHECK2(data[0],length);

   switch(attr_code)
    {
       case TUNNEL_PASS:
            if (length < 3)
           {
              ND_PRINT((ndo, "%s", tstr));
              return;
           }
            if (*data && (*data <=0x1F) )
               ND_PRINT((ndo, "Tag[%u] ", *data));
            else
              ND_PRINT((ndo, "Tag[Unused] "));
           data++;
           length--;
           ND_PRINT((ndo, "Salt %u ", EXTRACT_16BITS(data)));
           data+=2;
           length-=2;
        break;
      case TUNNEL_CLIENT_END:
      case TUNNEL_SERVER_END:
      case TUNNEL_PRIV_GROUP:
      case TUNNEL_ASSIGN_ID:
      case TUNNEL_CLIENT_AUTH:
      case TUNNEL_SERVER_AUTH:
            if (*data <= 0x1F)
            {
               if (length < 1)
              {
                 ND_PRINT((ndo, "%s", tstr));
                 return;
              }
               if (*data)
                 ND_PRINT((ndo, "Tag[%u] ", *data));
               else
                ND_PRINT((ndo, "Tag[Unused] "));
              data++;
              length--;
            }
         break;
       case EGRESS_VLAN_NAME:
            ND_PRINT((ndo, "%s (0x%02x) ",
                   tok2str(rfc4675_tagged,"Unknown tag",*data),
                   *data));
           data++;
           length--;
         break;
    }

   for (i=0; *data && i < length ; i++, data++)
        ND_PRINT((ndo, "%c", (*data < 32 || *data > 126) ? '.' : *data));

    return;

   trunc:
      ND_PRINT((ndo, "%s", tstr));
}

The RADIUS parser in tcpdump before 4.9.2 has a buffer over-read in print-radius.c:print_attr_string().


print_attr_string(netdissect_options *ndo,
                  register const u_char *data, u_int length, u_short attr_code)
{
   register u_int i;

   ND_TCHECK2(data[0],length);

   switch(attr_code)
    {
       case TUNNEL_PASS:
            if (length < 3)
              goto trunc;
            if (*data && (*data <=0x1F) )
               ND_PRINT((ndo, "Tag[%u] ", *data));
            else
              ND_PRINT((ndo, "Tag[Unused] "));
           data++;
           length--;
           ND_PRINT((ndo, "Salt %u ", EXTRACT_16BITS(data)));
           data+=2;
           length-=2;
        break;
      case TUNNEL_CLIENT_END:
      case TUNNEL_SERVER_END:
      case TUNNEL_PRIV_GROUP:
      case TUNNEL_ASSIGN_ID:
      case TUNNEL_CLIENT_AUTH:
      case TUNNEL_SERVER_AUTH:
            if (*data <= 0x1F)
            {
               if (length < 1)
                 goto trunc;
               if (*data)
                 ND_PRINT((ndo, "Tag[%u] ", *data));
               else
                ND_PRINT((ndo, "Tag[Unused] "));
              data++;
              length--;
            }
         break;
       case EGRESS_VLAN_NAME:
           if (length < 1)
              goto trunc;
            ND_PRINT((ndo, "%s (0x%02x) ",
                   tok2str(rfc4675_tagged,"Unknown tag",*data),
                   *data));
           data++;
           length--;
         break;
    }

   for (i=0; i < length && *data; i++, data++)
        ND_PRINT((ndo, "%c", (*data < 32 || *data > 126) ? '.' : *data));

    return;

   trunc:
      ND_PRINT((ndo, "%s", tstr));
}


CVE-2017-12985/IPv6: Check for print routines returning -1 when running past the end.

rt6_print(), ah_print(), and esp_print() return -1 if they run up
against the end of the packet while dissecting; if that happens, stop
dissecting, don't try to fetch the next header value, because 1) *it*
might be past the end of the packet and 2) we won't be using it in any
case, as we'll be exiting the loop.

Also, change mobility_print() to return -1 if it runs up against the
end of the packet, and stop dissecting if it does so.

This fixes a buffer over-read discovered by Brian 'geeknik' Carpenter.

Add tests using the capture files supplied by the reporter(s).

ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
{
	register const struct ip6_hdr *ip6;
	register int advance;
	u_int len;
	const u_char *ipend;
	register const u_char *cp;
	register u_int payload_len;
	int nh;
	int fragmented = 0;
	u_int flow;

	ip6 = (const struct ip6_hdr *)bp;

	ND_TCHECK(*ip6);
	if (length < sizeof (struct ip6_hdr)) {
		ND_PRINT((ndo, "truncated-ip6 %u", length));
		return;
	}

        if (!ndo->ndo_eflag)
            ND_PRINT((ndo, "IP6 "));

	if (IP6_VERSION(ip6) != 6) {
          ND_PRINT((ndo,"version error: %u != 6", IP6_VERSION(ip6)));
          return;
	}

	payload_len = EXTRACT_16BITS(&ip6->ip6_plen);
	len = payload_len + sizeof(struct ip6_hdr);
	if (length < len)
		ND_PRINT((ndo, "truncated-ip6 - %u bytes missing!",
			len - length));

        if (ndo->ndo_vflag) {
            flow = EXTRACT_32BITS(&ip6->ip6_flow);
            ND_PRINT((ndo, "("));
#if 0
            /* rfc1883 */
            if (flow & 0x0f000000)
		ND_PRINT((ndo, "pri 0x%02x, ", (flow & 0x0f000000) >> 24));
            if (flow & 0x00ffffff)
		ND_PRINT((ndo, "flowlabel 0x%06x, ", flow & 0x00ffffff));
#else
            /* RFC 2460 */
            if (flow & 0x0ff00000)
		ND_PRINT((ndo, "class 0x%02x, ", (flow & 0x0ff00000) >> 20));
            if (flow & 0x000fffff)
		ND_PRINT((ndo, "flowlabel 0x%05x, ", flow & 0x000fffff));
#endif

            ND_PRINT((ndo, "hlim %u, next-header %s (%u) payload length: %u) ",
                         ip6->ip6_hlim,
                         tok2str(ipproto_values,"unknown",ip6->ip6_nxt),
                         ip6->ip6_nxt,
                         payload_len));
        }

	/*
	 * Cut off the snapshot length to the end of the IP payload.
	 */
	ipend = bp + len;
	if (ipend < ndo->ndo_snapend)
		ndo->ndo_snapend = ipend;

	cp = (const u_char *)ip6;
 	advance = sizeof(struct ip6_hdr);
 	nh = ip6->ip6_nxt;
 	while (cp < ndo->ndo_snapend && advance > 0) {
 		cp += advance;
 		len -= advance;

		if (cp == (const u_char *)(ip6 + 1) &&
		    nh != IPPROTO_TCP && nh != IPPROTO_UDP &&
		    nh != IPPROTO_DCCP && nh != IPPROTO_SCTP) {
			ND_PRINT((ndo, "%s > %s: ", ip6addr_string(ndo, &ip6->ip6_src),
				     ip6addr_string(ndo, &ip6->ip6_dst)));
		}

		switch (nh) {
		case IPPROTO_HOPOPTS:
			advance = hbhopt_print(ndo, cp);
			if (advance < 0)
				return;
			nh = *cp;
			break;
		case IPPROTO_DSTOPTS:
			advance = dstopt_print(ndo, cp);
			if (advance < 0)
				return;
			nh = *cp;
			break;
		case IPPROTO_FRAGMENT:
			advance = frag6_print(ndo, cp, (const u_char *)ip6);
			if (advance < 0 || ndo->ndo_snapend <= cp + advance)
				return;
			nh = *cp;
			fragmented = 1;
			break;

		case IPPROTO_MOBILITY_OLD:
		case IPPROTO_MOBILITY:
			/*
			 * XXX - we don't use "advance"; RFC 3775 says that
			 * the next header field in a mobility header
			 * should be IPPROTO_NONE, but speaks of
			 * the possiblity of a future extension in
			 * which payload can be piggybacked atop a
 			 * mobility header.
 			 */
 			advance = mobility_print(ndo, cp, (const u_char *)ip6);
 			nh = *cp;
 			return;
 		case IPPROTO_ROUTING:
 			advance = rt6_print(ndo, cp, (const u_char *)ip6);
 			nh = *cp;
 			break;
 		case IPPROTO_SCTP:
			sctp_print(ndo, cp, (const u_char *)ip6, len);
			return;
		case IPPROTO_DCCP:
			dccp_print(ndo, cp, (const u_char *)ip6, len);
			return;
		case IPPROTO_TCP:
			tcp_print(ndo, cp, len, (const u_char *)ip6, fragmented);
			return;
		case IPPROTO_UDP:
			udp_print(ndo, cp, len, (const u_char *)ip6, fragmented);
			return;
		case IPPROTO_ICMPV6:
			icmp6_print(ndo, cp, len, (const u_char *)ip6, fragmented);
 			return;
 		case IPPROTO_AH:
 			advance = ah_print(ndo, cp);
 			nh = *cp;
 			break;
 		case IPPROTO_ESP:
 		    {
 			int enh, padlen;
 			advance = esp_print(ndo, cp, len, (const u_char *)ip6, &enh, &padlen);
 			nh = enh & 0xff;
 			len -= padlen;
 			break;
		    }
		case IPPROTO_IPCOMP:
		    {
			ipcomp_print(ndo, cp);
			/*
			 * Either this has decompressed the payload and
			 * printed it, in which case there's nothing more
			 * to do, or it hasn't, in which case there's
			 * nothing more to do.
			 */
			advance = -1;
			break;
		    }

		case IPPROTO_PIM:
			pim_print(ndo, cp, len, (const u_char *)ip6);
			return;

		case IPPROTO_OSPF:
			ospf6_print(ndo, cp, len);
			return;

		case IPPROTO_IPV6:
			ip6_print(ndo, cp, len);
			return;

		case IPPROTO_IPV4:
		        ip_print(ndo, cp, len);
			return;

                case IPPROTO_PGM:
                        pgm_print(ndo, cp, len, (const u_char *)ip6);
                        return;

		case IPPROTO_GRE:
			gre_print(ndo, cp, len);
			return;

		case IPPROTO_RSVP:
			rsvp_print(ndo, cp, len);
			return;

		case IPPROTO_NONE:
			ND_PRINT((ndo, "no next header"));
			return;

		default:
			ND_PRINT((ndo, "ip-proto-%d %d", nh, len));
			return;
		}
	}

	return;
trunc:
	ND_PRINT((ndo, "[|ip6]"));
}

The IPv6 parser in tcpdump before 4.9.2 has a buffer over-read in print-ip6.c:ip6_print().


ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
{
	register const struct ip6_hdr *ip6;
	register int advance;
	u_int len;
	const u_char *ipend;
	register const u_char *cp;
	register u_int payload_len;
	int nh;
	int fragmented = 0;
	u_int flow;

	ip6 = (const struct ip6_hdr *)bp;

	ND_TCHECK(*ip6);
	if (length < sizeof (struct ip6_hdr)) {
		ND_PRINT((ndo, "truncated-ip6 %u", length));
		return;
	}

        if (!ndo->ndo_eflag)
            ND_PRINT((ndo, "IP6 "));

	if (IP6_VERSION(ip6) != 6) {
          ND_PRINT((ndo,"version error: %u != 6", IP6_VERSION(ip6)));
          return;
	}

	payload_len = EXTRACT_16BITS(&ip6->ip6_plen);
	len = payload_len + sizeof(struct ip6_hdr);
	if (length < len)
		ND_PRINT((ndo, "truncated-ip6 - %u bytes missing!",
			len - length));

        if (ndo->ndo_vflag) {
            flow = EXTRACT_32BITS(&ip6->ip6_flow);
            ND_PRINT((ndo, "("));
#if 0
            /* rfc1883 */
            if (flow & 0x0f000000)
		ND_PRINT((ndo, "pri 0x%02x, ", (flow & 0x0f000000) >> 24));
            if (flow & 0x00ffffff)
		ND_PRINT((ndo, "flowlabel 0x%06x, ", flow & 0x00ffffff));
#else
            /* RFC 2460 */
            if (flow & 0x0ff00000)
		ND_PRINT((ndo, "class 0x%02x, ", (flow & 0x0ff00000) >> 20));
            if (flow & 0x000fffff)
		ND_PRINT((ndo, "flowlabel 0x%05x, ", flow & 0x000fffff));
#endif

            ND_PRINT((ndo, "hlim %u, next-header %s (%u) payload length: %u) ",
                         ip6->ip6_hlim,
                         tok2str(ipproto_values,"unknown",ip6->ip6_nxt),
                         ip6->ip6_nxt,
                         payload_len));
        }

	/*
	 * Cut off the snapshot length to the end of the IP payload.
	 */
	ipend = bp + len;
	if (ipend < ndo->ndo_snapend)
		ndo->ndo_snapend = ipend;

	cp = (const u_char *)ip6;
 	advance = sizeof(struct ip6_hdr);
 	nh = ip6->ip6_nxt;
 	while (cp < ndo->ndo_snapend && advance > 0) {
		if (len < (u_int)advance)
			goto trunc;
 		cp += advance;
 		len -= advance;

		if (cp == (const u_char *)(ip6 + 1) &&
		    nh != IPPROTO_TCP && nh != IPPROTO_UDP &&
		    nh != IPPROTO_DCCP && nh != IPPROTO_SCTP) {
			ND_PRINT((ndo, "%s > %s: ", ip6addr_string(ndo, &ip6->ip6_src),
				     ip6addr_string(ndo, &ip6->ip6_dst)));
		}

		switch (nh) {
		case IPPROTO_HOPOPTS:
			advance = hbhopt_print(ndo, cp);
			if (advance < 0)
				return;
			nh = *cp;
			break;
		case IPPROTO_DSTOPTS:
			advance = dstopt_print(ndo, cp);
			if (advance < 0)
				return;
			nh = *cp;
			break;
		case IPPROTO_FRAGMENT:
			advance = frag6_print(ndo, cp, (const u_char *)ip6);
			if (advance < 0 || ndo->ndo_snapend <= cp + advance)
				return;
			nh = *cp;
			fragmented = 1;
			break;

		case IPPROTO_MOBILITY_OLD:
		case IPPROTO_MOBILITY:
			/*
			 * XXX - we don't use "advance"; RFC 3775 says that
			 * the next header field in a mobility header
			 * should be IPPROTO_NONE, but speaks of
			 * the possiblity of a future extension in
			 * which payload can be piggybacked atop a
 			 * mobility header.
 			 */
 			advance = mobility_print(ndo, cp, (const u_char *)ip6);
			if (advance < 0)
				return;
 			nh = *cp;
 			return;
 		case IPPROTO_ROUTING:
			ND_TCHECK(*cp);
 			advance = rt6_print(ndo, cp, (const u_char *)ip6);
			if (advance < 0)
				return;
 			nh = *cp;
 			break;
 		case IPPROTO_SCTP:
			sctp_print(ndo, cp, (const u_char *)ip6, len);
			return;
		case IPPROTO_DCCP:
			dccp_print(ndo, cp, (const u_char *)ip6, len);
			return;
		case IPPROTO_TCP:
			tcp_print(ndo, cp, len, (const u_char *)ip6, fragmented);
			return;
		case IPPROTO_UDP:
			udp_print(ndo, cp, len, (const u_char *)ip6, fragmented);
			return;
		case IPPROTO_ICMPV6:
			icmp6_print(ndo, cp, len, (const u_char *)ip6, fragmented);
 			return;
 		case IPPROTO_AH:
 			advance = ah_print(ndo, cp);
			if (advance < 0)
				return;
 			nh = *cp;
 			break;
 		case IPPROTO_ESP:
 		    {
 			int enh, padlen;
 			advance = esp_print(ndo, cp, len, (const u_char *)ip6, &enh, &padlen);
			if (advance < 0)
				return;
 			nh = enh & 0xff;
 			len -= padlen;
 			break;
		    }
		case IPPROTO_IPCOMP:
		    {
			ipcomp_print(ndo, cp);
			/*
			 * Either this has decompressed the payload and
			 * printed it, in which case there's nothing more
			 * to do, or it hasn't, in which case there's
			 * nothing more to do.
			 */
			advance = -1;
			break;
		    }

		case IPPROTO_PIM:
			pim_print(ndo, cp, len, (const u_char *)ip6);
			return;

		case IPPROTO_OSPF:
			ospf6_print(ndo, cp, len);
			return;

		case IPPROTO_IPV6:
			ip6_print(ndo, cp, len);
			return;

		case IPPROTO_IPV4:
		        ip_print(ndo, cp, len);
			return;

                case IPPROTO_PGM:
                        pgm_print(ndo, cp, len, (const u_char *)ip6);
                        return;

		case IPPROTO_GRE:
			gre_print(ndo, cp, len);
			return;

		case IPPROTO_RSVP:
			rsvp_print(ndo, cp, len);
			return;

		case IPPROTO_NONE:
			ND_PRINT((ndo, "no next header"));
			return;

		default:
			ND_PRINT((ndo, "ip-proto-%d %d", nh, len));
			return;
		}
	}

	return;
trunc:
	ND_PRINT((ndo, "[|ip6]"));
}


static MagickBooleanType WriteINLINEImage(const ImageInfo *image_info,
  Image *image)
{
  char
    *base64,
    message[MaxTextExtent];

  const MagickInfo
    *magick_info;

  ExceptionInfo
    *exception;

  Image
    *write_image;

  ImageInfo
    *write_info;

  MagickBooleanType
    status;

  size_t
    blob_length,
    encode_length;

  unsigned char
    *blob;

  /*
    Convert image to base64-encoding.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickSignature);
  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  exception=(&image->exception);
  write_info=CloneImageInfo(image_info);
  (void) SetImageInfo(write_info,1,exception);
  if (LocaleCompare(write_info->magick,"INLINE") == 0)
    (void) CopyMagickString(write_info->magick,image->magick,MaxTextExtent);
   magick_info=GetMagickInfo(write_info->magick,exception);
   if ((magick_info == (const MagickInfo *) NULL) ||
       (GetMagickMimeType(magick_info) == (const char *) NULL))
    ThrowWriterException(CorruptImageError,"ImageTypeNotSupported");
   (void) CopyMagickString(image->filename,write_info->filename,MaxTextExtent);
   blob_length=2048;
   write_image=CloneImage(image,0,0,MagickTrue,exception);
  if (write_image == (Image *) NULL)
    {
      write_info=DestroyImageInfo(write_info);
      return(MagickTrue);
    }
  blob=(unsigned char *) ImageToBlob(write_info,write_image,&blob_length,
    exception);
  write_image=DestroyImage(write_image);
  write_info=DestroyImageInfo(write_info);
  if (blob == (unsigned char *) NULL)
    return(MagickFalse);
  encode_length=0;
  base64=Base64Encode(blob,blob_length,&encode_length);
  blob=(unsigned char *) RelinquishMagickMemory(blob);
  if (base64 == (char *) NULL)
    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
  /*
    Write base64-encoded image.
  */
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      base64=DestroyString(base64);
      return(status);
    }
  (void) FormatLocaleString(message,MaxTextExtent,"data:%s;base64,",
    GetMagickMimeType(magick_info));
  (void) WriteBlobString(image,message);
  (void) WriteBlobString(image,base64);
  base64=DestroyString(base64);
  return(MagickTrue);
}

ImageMagick 7.0.6-2 has a memory leak vulnerability in WriteINLINEImage in coders/inline.c.


static MagickBooleanType WriteINLINEImage(const ImageInfo *image_info,
  Image *image)
{
  char
    *base64,
    message[MaxTextExtent];

  const MagickInfo
    *magick_info;

  ExceptionInfo
    *exception;

  Image
    *write_image;

  ImageInfo
    *write_info;

  MagickBooleanType
    status;

  size_t
    blob_length,
    encode_length;

  unsigned char
    *blob;

  /*
    Convert image to base64-encoding.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickSignature);
  assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  exception=(&image->exception);
  write_info=CloneImageInfo(image_info);
  (void) SetImageInfo(write_info,1,exception);
  if (LocaleCompare(write_info->magick,"INLINE") == 0)
    (void) CopyMagickString(write_info->magick,image->magick,MaxTextExtent);
   magick_info=GetMagickInfo(write_info->magick,exception);
   if ((magick_info == (const MagickInfo *) NULL) ||
       (GetMagickMimeType(magick_info) == (const char *) NULL))
    {
      write_info=DestroyImageInfo(write_info);
      ThrowWriterException(CorruptImageError,"ImageTypeNotSupported");
    }
   (void) CopyMagickString(image->filename,write_info->filename,MaxTextExtent);
   blob_length=2048;
   write_image=CloneImage(image,0,0,MagickTrue,exception);
  if (write_image == (Image *) NULL)
    {
      write_info=DestroyImageInfo(write_info);
      return(MagickTrue);
    }
  blob=(unsigned char *) ImageToBlob(write_info,write_image,&blob_length,
    exception);
  write_image=DestroyImage(write_image);
  write_info=DestroyImageInfo(write_info);
  if (blob == (unsigned char *) NULL)
    return(MagickFalse);
  encode_length=0;
  base64=Base64Encode(blob,blob_length,&encode_length);
  blob=(unsigned char *) RelinquishMagickMemory(blob);
  if (base64 == (char *) NULL)
    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
  /*
    Write base64-encoded image.
  */
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      base64=DestroyString(base64);
      return(status);
    }
  (void) FormatLocaleString(message,MaxTextExtent,"data:%s;base64,",
    GetMagickMimeType(magick_info));
  (void) WriteBlobString(image,message);
  (void) WriteBlobString(image,base64);
  base64=DestroyString(base64);
  return(MagickTrue);
}


static Image *ReadDCMImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
#define ThrowDCMException(exception,message) \
{ \
  if (data != (unsigned char *) NULL) \
    data=(unsigned char *) RelinquishMagickMemory(data); \
  if (stream_info != (DCMStreamInfo *) NULL) \
    stream_info=(DCMStreamInfo *) RelinquishMagickMemory(stream_info); \
  ThrowReaderException((exception),(message)); \
}

  char
    explicit_vr[MaxTextExtent],
    implicit_vr[MaxTextExtent],
    magick[MaxTextExtent],
    photometric[MaxTextExtent];

  DCMInfo
    info;

  DCMStreamInfo
    *stream_info;

  Image
    *image;

  int
    *bluemap,
    datum,
    *greenmap,
    *graymap,
    *redmap;

  MagickBooleanType
    explicit_file,
    explicit_retry,
    sequence,
    use_explicit;

  MagickOffsetType
    offset;

  register unsigned char
    *p;

  register ssize_t
    i;

  size_t
    colors,
    height,
    length,
    number_scenes,
    quantum,
    status,
    width;

  ssize_t
    count,
    scene;

  unsigned char
    *data;

  unsigned short
    group,
    element;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
  assert(image_info->signature == MagickSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  assert(exception->signature == MagickSignature);
  image=AcquireImage(image_info);
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  image->depth=8UL;
  image->endian=LSBEndian;
  /*
    Read DCM preamble.
  */
  data=(unsigned char *) NULL;
  stream_info=(DCMStreamInfo *) AcquireMagickMemory(sizeof(*stream_info));
  if (stream_info == (DCMStreamInfo *) NULL)
    ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed");
  (void) ResetMagickMemory(stream_info,0,sizeof(*stream_info));
  count=ReadBlob(image,128,(unsigned char *) magick);
  if (count != 128)
    ThrowDCMException(CorruptImageError,"ImproperImageHeader");
  count=ReadBlob(image,4,(unsigned char *) magick);
  if ((count != 4) || (LocaleNCompare(magick,"DICM",4) != 0))
    {
      offset=SeekBlob(image,0L,SEEK_SET);
      if (offset < 0)
        ThrowDCMException(CorruptImageError,"ImproperImageHeader");
    }
  /*
    Read DCM Medical image.
  */
  (void) CopyMagickString(photometric,"MONOCHROME1 ",MaxTextExtent);
  info.polarity=MagickFalse;
  info.scale=(Quantum *) NULL;
  info.bits_allocated=8;
  info.bytes_per_pixel=1;
  info.depth=8;
  info.mask=0xffff;
  info.max_value=255UL;
  info.samples_per_pixel=1;
  info.signed_data=(~0UL);
  info.significant_bits=0;
  info.rescale=MagickFalse;
  info.rescale_intercept=0.0;
  info.rescale_slope=1.0;
  info.window_center=0.0;
  info.window_width=0.0;
  data=(unsigned char *) NULL;
  element=0;
  explicit_vr[2]='\0';
  explicit_file=MagickFalse;
  colors=0;
  redmap=(int *) NULL;
  greenmap=(int *) NULL;
  bluemap=(int *) NULL;
  graymap=(int *) NULL;
  height=0;
  number_scenes=1;
  sequence=MagickFalse;
  use_explicit=MagickFalse;
  explicit_retry = MagickFalse;
  width=0;
  for (group=0; (group != 0x7FE0) || (element != 0x0010) ||
                (sequence != MagickFalse); )
  {
    /*
      Read a group.
    */
    image->offset=(ssize_t) TellBlob(image);
    group=ReadBlobLSBShort(image);
    element=ReadBlobLSBShort(image);
    if ((group != 0x0002) && (image->endian == MSBEndian))
      {
        group=(unsigned short) ((group << 8) | ((group >> 8) & 0xFF));
        element=(unsigned short) ((element << 8) | ((element >> 8) & 0xFF));
      }
    quantum=0;
    /*
      Find corresponding VR for this group and element.
    */
    for (i=0; dicom_info[i].group < 0xffff; i++)
      if ((group == dicom_info[i].group) && (element == dicom_info[i].element))
        break;
    (void) CopyMagickString(implicit_vr,dicom_info[i].vr,MaxTextExtent);
    count=ReadBlob(image,2,(unsigned char *) explicit_vr);
    if (count != 2)
      ThrowDCMException(CorruptImageError,"ImproperImageHeader");
    /*
      Check for "explicitness", but meta-file headers always explicit.
    */
    if ((explicit_file == MagickFalse) && (group != 0x0002))
      explicit_file=(isupper((unsigned char) *explicit_vr) != MagickFalse) &&
        (isupper((unsigned char) *(explicit_vr+1)) != MagickFalse) ?
        MagickTrue : MagickFalse;
    use_explicit=((group == 0x0002) && (explicit_retry == MagickFalse)) ||
      (explicit_file != MagickFalse) ? MagickTrue : MagickFalse;
    if ((use_explicit != MagickFalse) && (strncmp(implicit_vr,"xs",2) == 0))
      (void) CopyMagickString(implicit_vr,explicit_vr,MaxTextExtent);
    if ((use_explicit == MagickFalse) || (strncmp(implicit_vr,"!!",2) == 0))
      {
        offset=SeekBlob(image,(MagickOffsetType) -2,SEEK_CUR);
        if (offset < 0)
          ThrowDCMException(CorruptImageError,"ImproperImageHeader");
        quantum=4;
      }
    else
      {
        /*
          Assume explicit type.
        */
        quantum=2;
        if ((strncmp(explicit_vr,"OB",2) == 0) ||
            (strncmp(explicit_vr,"UN",2) == 0) ||
            (strncmp(explicit_vr,"OW",2) == 0) ||
            (strncmp(explicit_vr,"SQ",2) == 0))
          {
            (void) ReadBlobLSBShort(image);
            quantum=4;
          }
      }
    datum=0;
    if (quantum == 4)
      {
        if (group == 0x0002)
          datum=ReadBlobLSBSignedLong(image);
        else
          datum=ReadBlobSignedLong(image);
      }
    else
      if (quantum == 2)
        {
          if (group == 0x0002)
            datum=ReadBlobLSBSignedShort(image);
          else
            datum=ReadBlobSignedShort(image);
        }
    quantum=0;
    length=1;
    if (datum != 0)
      {
        if ((strncmp(implicit_vr,"SS",2) == 0) ||
            (strncmp(implicit_vr,"US",2) == 0))
          quantum=2;
        else
          if ((strncmp(implicit_vr,"UL",2) == 0) ||
              (strncmp(implicit_vr,"SL",2) == 0) ||
              (strncmp(implicit_vr,"FL",2) == 0))
            quantum=4;
          else
            if (strncmp(implicit_vr,"FD",2) != 0)
              quantum=1;
            else
              quantum=8;
        if (datum != ~0)
          length=(size_t) datum/quantum;
        else
          {
            /*
              Sequence and item of undefined length.
            */
            quantum=0;
            length=0;
          }
      }
    if (image_info->verbose != MagickFalse)
      {
        /*
          Display Dicom info.
        */
        if (use_explicit == MagickFalse)
          explicit_vr[0]='\0';
        for (i=0; dicom_info[i].description != (char *) NULL; i++)
          if ((group == dicom_info[i].group) &&
              (element == dicom_info[i].element))
            break;
        (void) FormatLocaleFile(stdout,"0x%04lX %4ld %s-%s (0x%04lx,0x%04lx)",
          (unsigned long) image->offset,(long) length,implicit_vr,explicit_vr,
          (unsigned long) group,(unsigned long) element);
        if (dicom_info[i].description != (char *) NULL)
          (void) FormatLocaleFile(stdout," %s",dicom_info[i].description);
        (void) FormatLocaleFile(stdout,": ");
      }
    if ((sequence == MagickFalse) && (group == 0x7FE0) && (element == 0x0010))
      {
        if (image_info->verbose != MagickFalse)
          (void) FormatLocaleFile(stdout,"\n");
        break;
      }
    /*
      Allocate space and read an array.
    */
    data=(unsigned char *) NULL;
    if ((length == 1) && (quantum == 1))
      datum=ReadBlobByte(image);
    else
      if ((length == 1) && (quantum == 2))
        {
          if (group == 0x0002)
            datum=ReadBlobLSBSignedShort(image);
          else
            datum=ReadBlobSignedShort(image);
        }
      else
        if ((length == 1) && (quantum == 4))
          {
            if (group == 0x0002)
              datum=ReadBlobLSBSignedLong(image);
            else
              datum=ReadBlobSignedLong(image);
          }
        else
           if ((quantum != 0) && (length != 0))
             {
               if (length > GetBlobSize(image))
                ThrowReaderException(CorruptImageError,
                   "InsufficientImageDataInFile");
               if (~length >= 1)
                 data=(unsigned char *) AcquireQuantumMemory(length+1,quantum*
                  sizeof(*data));
              if (data == (unsigned char *) NULL)
                ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed");
              count=ReadBlob(image,(size_t) quantum*length,data);
              if (count != (ssize_t) (quantum*length))
                {
                  if (image_info->verbose != MagickFalse)
                    (void) FormatLocaleFile(stdout,"count=%d quantum=%d "
                      "length=%d group=%d\n",(int) count,(int) quantum,(int)
                      length,(int) group);
                   ThrowDCMException(CorruptImageError,
                     "InsufficientImageDataInFile");
                }
              data[length*quantum]='\0';
            }
          else
            if ((unsigned int) datum == 0xFFFFFFFFU)
              {
                sequence=MagickTrue;
                continue;
              }
    if ((unsigned int) ((group << 16) | element) == 0xFFFEE0DD)
      {
        if (data != (unsigned char *) NULL)
          data=(unsigned char *) RelinquishMagickMemory(data);
        sequence=MagickFalse;
        continue;
      }
    if (sequence != MagickFalse)
      {
        if (data != (unsigned char *) NULL)
          data=(unsigned char *) RelinquishMagickMemory(data);
        continue;
      }
    switch (group)
    {
      case 0x0002:
      {
        switch (element)
        {
          case 0x0010:
          {
            char
              transfer_syntax[MaxTextExtent];

            /*
              Transfer Syntax.
            */
            if ((datum == 0) && (explicit_retry == MagickFalse))
              {
                explicit_retry=MagickTrue;
                (void) SeekBlob(image,(MagickOffsetType) 0,SEEK_SET);
                group=0;
                element=0;
                if (image_info->verbose != MagickFalse)
                  (void) FormatLocaleFile(stdout,
                    "Corrupted image - trying explicit format\n");
                break;
              }
            *transfer_syntax='\0';
            if (data != (unsigned char *) NULL)
              (void) CopyMagickString(transfer_syntax,(char *) data,
                MaxTextExtent);
            if (image_info->verbose != MagickFalse)
              (void) FormatLocaleFile(stdout,"transfer_syntax=%s\n",
                (const char *) transfer_syntax);
            if (strncmp(transfer_syntax,"1.2.840.10008.1.2",17) == 0)
              {
                int
                  count,
                  subtype,
                  type;

                type=1;
                subtype=0;
                if (strlen(transfer_syntax) > 17)
                  {
                    count=sscanf(transfer_syntax+17,".%d.%d",&type,&subtype);
                    if (count < 1)
                      ThrowDCMException(CorruptImageError,
                        "ImproperImageHeader");
                  }
                switch (type)
                {
                  case 1:
                  {
                    image->endian=LSBEndian;
                    break;
                  }
                  case 2:
                  {
                    image->endian=MSBEndian;
                    break;
                  }
                  case 4:
                  {
                    if ((subtype >= 80) && (subtype <= 81))
                      image->compression=JPEGCompression;
                    else
                      if ((subtype >= 90) && (subtype <= 93))
                        image->compression=JPEG2000Compression;
                      else
                        image->compression=JPEGCompression;
                    break;
                  }
                  case 5:
                  {
                    image->compression=RLECompression;
                    break;
                  }
                }
              }
            break;
          }
          default:
            break;
        }
        break;
      }
      case 0x0028:
      {
        switch (element)
        {
          case 0x0002:
          {
            /*
              Samples per pixel.
            */
            info.samples_per_pixel=(size_t) datum;
            break;
          }
          case 0x0004:
          {
            /*
              Photometric interpretation.
            */
            if (data == (unsigned char *) NULL)
              break;
            for (i=0; i < (ssize_t) MagickMin(length,MaxTextExtent-1); i++)
              photometric[i]=(char) data[i];
            photometric[i]='\0';
            info.polarity=LocaleCompare(photometric,"MONOCHROME1 ") == 0 ?
              MagickTrue : MagickFalse;
            break;
          }
          case 0x0006:
          {
            /*
              Planar configuration.
            */
            if (datum == 1)
              image->interlace=PlaneInterlace;
            break;
          }
          case 0x0008:
          {
            /*
              Number of frames.
            */
            if (data == (unsigned char *) NULL)
              break;
            number_scenes=StringToUnsignedLong((char *) data);
            break;
          }
          case 0x0010:
          {
            /*
              Image rows.
            */
            height=(size_t) datum;
            break;
          }
          case 0x0011:
          {
            /*
              Image columns.
            */
            width=(size_t) datum;
            break;
          }
          case 0x0100:
          {
            /*
              Bits allocated.
            */
            info.bits_allocated=(size_t) datum;
            info.bytes_per_pixel=1;
            if (datum > 8)
              info.bytes_per_pixel=2;
            info.depth=info.bits_allocated;
            if (info.depth > 32)
              ThrowDCMException(CorruptImageError,"ImproperImageHeader");
            info.max_value=(1UL << info.bits_allocated)-1;
            image->depth=info.depth;
            break;
          }
          case 0x0101:
          {
            /*
              Bits stored.
            */
            info.significant_bits=(size_t) datum;
            info.bytes_per_pixel=1;
            if (info.significant_bits > 8)
              info.bytes_per_pixel=2;
            info.depth=info.significant_bits;
            if (info.depth > 32)
              ThrowDCMException(CorruptImageError,"ImproperImageHeader");
            info.max_value=(1UL << info.significant_bits)-1;
            info.mask=(size_t) GetQuantumRange(info.significant_bits);
            image->depth=info.depth;
            break;
          }
          case 0x0102:
          {
            /*
              High bit.
            */
            break;
          }
          case 0x0103:
          {
            /*
              Pixel representation.
            */
            info.signed_data=(size_t) datum;
            break;
          }
          case 0x1050:
          {
            /*
              Visible pixel range: center.
            */
            if (data != (unsigned char *) NULL)
              info.window_center=StringToDouble((char *) data,
                (char **) NULL);
            break;
          }
          case 0x1051:
          {
            /*
              Visible pixel range: width.
            */
            if (data != (unsigned char *) NULL)
              info.window_width=StringToDouble((char *) data,
                (char **) NULL);
            break;
          }
          case 0x1052:
          {
            /*
              Rescale intercept
            */
            if (data != (unsigned char *) NULL)
              info.rescale_intercept=StringToDouble((char *) data,
                (char **) NULL);
            break;
          }
          case 0x1053:
          {
            /*
              Rescale slope
            */
            if (data != (unsigned char *) NULL)
              info.rescale_slope=StringToDouble((char *) data,
                (char **) NULL);
            break;
          }
          case 0x1200:
          case 0x3006:
          {
            /*
              Populate graymap.
            */
            if (data == (unsigned char *) NULL)
              break;
            colors=(size_t) (length/info.bytes_per_pixel);
            datum=(int) colors;
            graymap=(int *) AcquireQuantumMemory((size_t) colors,
              sizeof(*graymap));
            if (graymap == (int *) NULL)
              ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed");
            for (i=0; i < (ssize_t) colors; i++)
              if (info.bytes_per_pixel == 1)
                graymap[i]=(int) data[i];
              else
                graymap[i]=(int) ((short *) data)[i];
            break;
          }
          case 0x1201:
          {
            unsigned short
              index;

            /*
              Populate redmap.
            */
            if (data == (unsigned char *) NULL)
              break;
            colors=(size_t) (length/2);
            datum=(int) colors;
            redmap=(int *) AcquireQuantumMemory((size_t) colors,
              sizeof(*redmap));
            if (redmap == (int *) NULL)
              ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed");
            p=data;
            for (i=0; i < (ssize_t) colors; i++)
            {
              if (image->endian == MSBEndian)
                index=(unsigned short) ((*p << 8) | *(p+1));
              else
                index=(unsigned short) (*p | (*(p+1) << 8));
              redmap[i]=(int) index;
              p+=2;
            }
            break;
          }
          case 0x1202:
          {
            unsigned short
              index;

            /*
              Populate greenmap.
            */
            if (data == (unsigned char *) NULL)
              break;
            colors=(size_t) (length/2);
            datum=(int) colors;
            greenmap=(int *) AcquireQuantumMemory((size_t) colors,
              sizeof(*greenmap));
            if (greenmap == (int *) NULL)
              ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed");
            p=data;
            for (i=0; i < (ssize_t) colors; i++)
            {
              if (image->endian == MSBEndian)
                index=(unsigned short) ((*p << 8) | *(p+1));
              else
                index=(unsigned short) (*p | (*(p+1) << 8));
              greenmap[i]=(int) index;
              p+=2;
            }
            break;
          }
          case 0x1203:
          {
            unsigned short
              index;

            /*
              Populate bluemap.
            */
            if (data == (unsigned char *) NULL)
              break;
            colors=(size_t) (length/2);
            datum=(int) colors;
            bluemap=(int *) AcquireQuantumMemory((size_t) colors,
              sizeof(*bluemap));
            if (bluemap == (int *) NULL)
              ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed");
            p=data;
            for (i=0; i < (ssize_t) colors; i++)
            {
              if (image->endian == MSBEndian)
                index=(unsigned short) ((*p << 8) | *(p+1));
              else
                index=(unsigned short) (*p | (*(p+1) << 8));
              bluemap[i]=(int) index;
              p+=2;
            }
            break;
          }
          default:
            break;
        }
        break;
      }
      case 0x2050:
      {
        switch (element)
        {
          case 0x0020:
          {
            if ((data != (unsigned char *) NULL) &&
                (strncmp((char *) data,"INVERSE",7) == 0))
              info.polarity=MagickTrue;
            break;
          }
          default:
            break;
        }
        break;
      }
      default:
        break;
    }
    if (data != (unsigned char *) NULL)
      {
        char
          *attribute;

        for (i=0; dicom_info[i].description != (char *) NULL; i++)
          if ((group == dicom_info[i].group) &&
              (element == dicom_info[i].element))
            break;
        if (dicom_info[i].description != (char *) NULL)
          {
            attribute=AcquireString("dcm:");
            (void) ConcatenateString(&attribute,dicom_info[i].description);
            for (i=0; i < (ssize_t) MagickMax(length,4); i++)
              if (isprint((int) data[i]) == MagickFalse)
                break;
            if ((i == (ssize_t) length) || (length > 4))
              {
                (void) SubstituteString(&attribute," ","");
                (void) SetImageProperty(image,attribute,(char *) data);
              }
            attribute=DestroyString(attribute);
          }
      }
    if (image_info->verbose != MagickFalse)
      {
        if (data == (unsigned char *) NULL)
          (void) FormatLocaleFile(stdout,"%d\n",datum);
        else
          {
            /*
              Display group data.
            */
            for (i=0; i < (ssize_t) MagickMax(length,4); i++)
              if (isprint((int) data[i]) == MagickFalse)
                break;
            if ((i != (ssize_t) length) && (length <= 4))
              {
                ssize_t
                  j;

                datum=0;
                for (j=(ssize_t) length-1; j >= 0; j--)
                  datum=(256*datum+data[j]);
                (void) FormatLocaleFile(stdout,"%d",datum);
              }
            else
              for (i=0; i < (ssize_t) length; i++)
                if (isprint((int) data[i]) != MagickFalse)
                  (void) FormatLocaleFile(stdout,"%c",data[i]);
                else
                  (void) FormatLocaleFile(stdout,"%c",'.');
            (void) FormatLocaleFile(stdout,"\n");
          }
      }
    if (data != (unsigned char *) NULL)
      data=(unsigned char *) RelinquishMagickMemory(data);
    if (EOFBlob(image) != MagickFalse)
      {
        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
          image->filename);
        break;
      }
  }
  if ((width == 0) || (height == 0))
    ThrowDCMException(CorruptImageError,"ImproperImageHeader");
  image->columns=(size_t) width;
  image->rows=(size_t) height;
  if (info.signed_data == 0xffff)
    info.signed_data=(size_t) (info.significant_bits == 16 ? 1 : 0);
  if ((image->compression == JPEGCompression) ||
      (image->compression == JPEG2000Compression))
    {
      Image
        *images;

      ImageInfo
        *read_info;

      int
        c;

      size_t
        length;

      unsigned int
        tag;

      /*
        Read offset table.
      */
      for (i=0; i < (ssize_t) stream_info->remaining; i++)
        (void) ReadBlobByte(image);
      tag=(ReadBlobLSBShort(image) << 16) | ReadBlobLSBShort(image);
      (void) tag;
      length=(size_t) ReadBlobLSBLong(image);
      stream_info->offset_count=length >> 2;
      if (stream_info->offset_count != 0)
        {
          MagickOffsetType
            offset;

          stream_info->offsets=(ssize_t *) AcquireQuantumMemory(
            stream_info->offset_count,sizeof(*stream_info->offsets));
          if (stream_info->offsets == (ssize_t *) NULL)
            ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed");
          for (i=0; i < (ssize_t) stream_info->offset_count; i++)
            stream_info->offsets[i]=(ssize_t) ReadBlobLSBSignedLong(image);
          offset=TellBlob(image);
          for (i=0; i < (ssize_t) stream_info->offset_count; i++)
            stream_info->offsets[i]+=offset;
        }
      /*
        Handle non-native image formats.
      */
      read_info=CloneImageInfo(image_info);
      SetImageInfoBlob(read_info,(void *) NULL,0);
      images=NewImageList();
      for (scene=0; scene < (ssize_t) number_scenes; scene++)
      {
        char
          filename[MaxTextExtent];

        const char
          *property;

        FILE
          *file;

        Image
          *jpeg_image;

        int
          unique_file;

        unsigned int
          tag;

        tag=(ReadBlobLSBShort(image) << 16) | ReadBlobLSBShort(image);
        length=(size_t) ReadBlobLSBLong(image);
        if (tag == 0xFFFEE0DD)
          break; /* sequence delimiter tag */
        if (tag != 0xFFFEE000)
          ThrowDCMException(CorruptImageError,"ImproperImageHeader");
        file=(FILE *) NULL;
        unique_file=AcquireUniqueFileResource(filename);
        if (unique_file != -1)
          file=fdopen(unique_file,"wb");
        if (file == (FILE *) NULL)
          {
            (void) RelinquishUniqueFileResource(filename);
            ThrowFileException(exception,FileOpenError,
              "UnableToCreateTemporaryFile",filename);
            break;
          }
        for ( ; length != 0; length--)
        {
          c=ReadBlobByte(image);
          if (c == EOF)
            {
              ThrowFileException(exception,CorruptImageError,
                "UnexpectedEndOfFile",image->filename);
              break;
            }
          (void) fputc(c,file);
        }
        (void) fclose(file);
        (void) FormatLocaleString(read_info->filename,MaxTextExtent,"jpeg:%s",
          filename);
        if (image->compression == JPEG2000Compression)
          (void) FormatLocaleString(read_info->filename,MaxTextExtent,"j2k:%s",
            filename);
        jpeg_image=ReadImage(read_info,exception);
        if (jpeg_image != (Image *) NULL)
          {
            ResetImagePropertyIterator(image);
            property=GetNextImageProperty(image);
            while (property != (const char *) NULL)
            {
              (void) SetImageProperty(jpeg_image,property,
                GetImageProperty(image,property));
              property=GetNextImageProperty(image);
            }
            AppendImageToList(&images,jpeg_image);
          }
        (void) RelinquishUniqueFileResource(filename);
      }
      read_info=DestroyImageInfo(read_info);
      image=DestroyImage(image);
      return(GetFirstImageInList(images));
    }
  if (info.depth != (1UL*MAGICKCORE_QUANTUM_DEPTH))
    {
      QuantumAny
        range;

      size_t
        length;

      /*
        Compute pixel scaling table.
      */
      length=(size_t) (GetQuantumRange(info.depth)+1);
      info.scale=(Quantum *) AcquireQuantumMemory(length,sizeof(*info.scale));
      if (info.scale == (Quantum *) NULL)
        ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed");
      range=GetQuantumRange(info.depth);
      for (i=0; i <= (ssize_t) GetQuantumRange(info.depth); i++)
        info.scale[i]=ScaleAnyToQuantum((size_t) i,range);
    }
  if (image->compression == RLECompression)
    {
      size_t
        length;

      unsigned int
        tag;

      /*
        Read RLE offset table.
      */
      for (i=0; i < (ssize_t) stream_info->remaining; i++)
        (void) ReadBlobByte(image);
      tag=(ReadBlobLSBShort(image) << 16) | ReadBlobLSBShort(image);
      (void) tag;
      length=(size_t) ReadBlobLSBLong(image);
      stream_info->offset_count=length >> 2;
      if (stream_info->offset_count != 0)
        {
          MagickOffsetType
            offset;

          stream_info->offsets=(ssize_t *) AcquireQuantumMemory(
            stream_info->offset_count,sizeof(*stream_info->offsets));
          if (stream_info->offsets == (ssize_t *) NULL)
            ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed");
          for (i=0; i < (ssize_t) stream_info->offset_count; i++)
            stream_info->offsets[i]=(ssize_t) ReadBlobLSBSignedLong(image);
          offset=TellBlob(image)+8;
          for (i=0; i < (ssize_t) stream_info->offset_count; i++)
            stream_info->offsets[i]+=offset;
        }
    }
  for (scene=0; scene < (ssize_t) number_scenes; scene++)
  {
    if (image_info->ping != MagickFalse)
      break;
    image->columns=(size_t) width;
    image->rows=(size_t) height;
    image->depth=info.depth;
    status=SetImageExtent(image,image->columns,image->rows);
    if (status == MagickFalse)
      {
        InheritException(exception,&image->exception);
        break;
      }
    image->colorspace=RGBColorspace;
    if ((image->colormap == (PixelPacket *) NULL) &&
        (info.samples_per_pixel == 1))
      {
        int
          index;

        size_t
          one;

        one=1;
        if (colors == 0)
          colors=one << info.depth;
        if (AcquireImageColormap(image,colors) == MagickFalse)
          ThrowDCMException(ResourceLimitError,"MemoryAllocationFailed");
        if (redmap != (int *) NULL)
          for (i=0; i < (ssize_t) colors; i++)
          {
            index=redmap[i];
            if ((info.scale != (Quantum *) NULL) &&
                (index <= (int) info.max_value))
              index=(int) info.scale[index];
            image->colormap[i].red=(Quantum) index;
          }
        if (greenmap != (int *) NULL)
          for (i=0; i < (ssize_t) colors; i++)
          {
            index=greenmap[i];
            if ((info.scale != (Quantum *) NULL) &&
                (index <= (int) info.max_value))
              index=(int) info.scale[index];
            image->colormap[i].green=(Quantum) index;
          }
        if (bluemap != (int *) NULL)
          for (i=0; i < (ssize_t) colors; i++)
          {
            index=bluemap[i];
            if ((info.scale != (Quantum *) NULL) &&
                (index <= (int) info.max_value))
              index=(int) info.scale[index];
            image->colormap[i].blue=(Quantum) index;
          }
        if (graymap != (int *) NULL)
          for (i=0; i < (ssize_t) colors; i++)
          {
            index=graymap[i];
            if ((info.scale != (Quantum *) NULL) &&
                (index <= (int) info.max_value))
              index=(int) info.scale[index];
            image->colormap[i].red=(Quantum) index;
            image->colormap[i].green=(Quantum) index;
            image->colormap[i].blue=(Quantum) index;
          }
      }
    if (image->compression == RLECompression)
      {
        unsigned int
          tag;

        /*
          Read RLE segment table.
        */
        for (i=0; i < (ssize_t) stream_info->remaining; i++)
          (void) ReadBlobByte(image);
        tag=(ReadBlobLSBShort(image) << 16) | ReadBlobLSBShort(image);
        stream_info->remaining=(size_t) ReadBlobLSBLong(image);
        if ((tag != 0xFFFEE000) || (stream_info->remaining <= 64) ||
            (EOFBlob(image) != MagickFalse))
          ThrowDCMException(CorruptImageError,"ImproperImageHeader");
        stream_info->count=0;
        stream_info->segment_count=ReadBlobLSBLong(image);
        for (i=0; i < 15; i++)
          stream_info->segments[i]=(ssize_t) ReadBlobLSBSignedLong(image);
        stream_info->remaining-=64;
        if (stream_info->segment_count > 1)
          {
            info.bytes_per_pixel=1;
            info.depth=8;
            if (stream_info->offset_count > 0)
              (void) SeekBlob(image,stream_info->offsets[0]+
                stream_info->segments[0],SEEK_SET);
          }
      }
    if ((info.samples_per_pixel > 1) && (image->interlace == PlaneInterlace))
      {
        register ssize_t
          x;

        register PixelPacket
          *q;

        ssize_t
          y;

        /*
          Convert Planar RGB DCM Medical image to pixel packets.
        */
        for (i=0; i < (ssize_t) info.samples_per_pixel; i++)
        {
          for (y=0; y < (ssize_t) image->rows; y++)
          {
            q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
            if (q == (PixelPacket *) NULL)
              break;
            for (x=0; x < (ssize_t) image->columns; x++)
            {
              switch ((int) i)
              {
                case 0:
                {
                  SetPixelRed(q,ScaleCharToQuantum((unsigned char)
                    ReadDCMByte(stream_info,image)));
                  break;
                }
                case 1:
                {
                  SetPixelGreen(q,ScaleCharToQuantum((unsigned char)
                    ReadDCMByte(stream_info,image)));
                  break;
                }
                case 2:
                {
                  SetPixelBlue(q,ScaleCharToQuantum((unsigned char)
                    ReadDCMByte(stream_info,image)));
                  break;
                }
                case 3:
                {
                  SetPixelAlpha(q,ScaleCharToQuantum((unsigned char)
                    ReadDCMByte(stream_info,image)));
                  break;
                }
                default:
                  break;
              }
              q++;
            }
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
            if (image->previous == (Image *) NULL)
              {
                status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
                  image->rows);
                if (status == MagickFalse)
                  break;
              }
          }
        }
      }
    else
      {
        const char
          *option;

        /*
          Convert DCM Medical image to pixel packets.
        */
        option=GetImageOption(image_info,"dcm:display-range");
        if (option != (const char *) NULL)
          {
            if (LocaleCompare(option,"reset") == 0)
              info.window_width=0;
          }
        option=GetImageOption(image_info,"dcm:window");
        if (option != (char *) NULL)
          {
            GeometryInfo
              geometry_info;

            MagickStatusType
              flags;

            flags=ParseGeometry(option,&geometry_info);
            if (flags & RhoValue)
              info.window_center=geometry_info.rho;
            if (flags & SigmaValue)
              info.window_width=geometry_info.sigma;
            info.rescale=MagickTrue;
          }
        option=GetImageOption(image_info,"dcm:rescale");
        if (option != (char *) NULL)
          info.rescale=IsStringTrue(option);
        if ((info.window_center != 0) && (info.window_width == 0))
          info.window_width=info.window_center;
        status=ReadDCMPixels(image,&info,stream_info,MagickTrue,exception);
        if ((status != MagickFalse) && (stream_info->segment_count > 1))
          {
            if (stream_info->offset_count > 0)
              (void) SeekBlob(image,stream_info->offsets[0]+
                stream_info->segments[1],SEEK_SET);
            (void) ReadDCMPixels(image,&info,stream_info,MagickFalse,exception);
          }
      }
    if (SetImageGray(image,exception) != MagickFalse)
      (void) SetImageColorspace(image,GRAYColorspace);
    if (EOFBlob(image) != MagickFalse)
      {
        ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
          image->filename);
        break;
      }
    /*
      Proceed to next image.
    */
    if (image_info->number_scenes != 0)
      if (image->scene >= (image_info->scene+image_info->number_scenes-1))
        break;
    if (scene < (ssize_t) (number_scenes-1))
      {
        /*
          Allocate next image structure.
        */
        AcquireNextImage(image_info,image);
        if (GetNextImageInList(image) == (Image *) NULL)
          {
            image=DestroyImageList(image);
            return((Image *) NULL);
          }
        image=SyncNextImageInList(image);
        status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
          GetBlobSize(image));
        if (status == MagickFalse)
          break;
      }
  }
  /*
    Free resources.
  */
  if (stream_info->offsets != (ssize_t *) NULL)
    stream_info->offsets=(ssize_t *)
      RelinquishMagickMemory(stream_info->offsets);
  stream_info=(DCMStreamInfo *) RelinquishMagickMemory(stream_info);
  if (info.scale != (Quantum *) NULL)
    info.scale=(Quantum *) RelinquishMagickMemory(info.scale);
  if (graymap != (int *) NULL)
    graymap=(int *) RelinquishMagickMemory(graymap);
  if (bluemap != (int *) NULL)
    bluemap=(int *) RelinquishMagickMemory(bluemap);
  if (greenmap != (int *) NULL)
    greenmap=(int *) RelinquishMagickMemory(greenmap);
  if (redmap != (int *) NULL)
    redmap=(int *) RelinquishMagickMemory(redmap);
  (void) CloseBlob(image);
  return(GetFirstImageInList(image));
}

ImageMagick 7.0.6-1 has a memory leak vulnerability in ReadDCMImage in coders\dcm.c.