char * get_option_string(const struct dhcp_message *dhcp, uint8_t option) { int type = 0; int len; const uint8_t *p; char *s; p = get_option(dhcp, option, &len, &type); if (!p || *p == '\0') return NULL; if (type & RFC3397) { type = decode_rfc3397(NULL, 0, len, p); if (!type) { errno = EINVAL; return NULL; } s = xmalloc(sizeof(char) * type); decode_rfc3397(s, type, len, p); return s; } if (type & RFC3361) return decode_rfc3361(len, p); s = xmalloc(sizeof(char) * (len + 1)); memcpy(s, p, len); s[len] = '\0'; return s; }
static char * decode_rfc3361(int dl, const uint8_t *data) { uint8_t enc; unsigned int l; char *sip = NULL; struct in_addr addr; char *p; if (dl < 2) { errno = EINVAL; return 0; } enc = *data++; dl--; switch (enc) { case 0: if ((l = decode_rfc3397(NULL, 0, dl, data)) > 0) { sip = xmalloc(l); decode_rfc3397(sip, l, dl, data); } break; case 1: if (dl == 0 || dl % 4 != 0) { errno = EINVAL; break; } addr.s_addr = INADDR_BROADCAST; l = ((dl / sizeof(addr.s_addr)) * ((4 * 4) + 1)) + 1; sip = p = xmalloc(l); while (l != 0) { memcpy(&addr.s_addr, data, sizeof(addr.s_addr)); data += sizeof(addr.s_addr); p += snprintf(p, l - (p - sip), "%s ", inet_ntoa(addr)); l -= sizeof(addr.s_addr); } *--p = '\0'; break; default: errno = EINVAL; return 0; } return sip; }
ssize_t print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data, const char *ifname) { const uint8_t *e, *t; uint16_t u16; int16_t s16; uint32_t u32; int32_t s32; struct in_addr addr; ssize_t bytes = 0; ssize_t l; char *tmp; if (type & RFC3397) { l = decode_rfc3397(NULL, 0, dl, data); if (l < 1) return l; tmp = malloc(l); if (tmp == NULL) return -1; decode_rfc3397(tmp, l, dl, data); l = print_string(s, len, l - 1, (uint8_t *)tmp); free(tmp); return l; } #ifdef INET if (type & RFC3361) { if ((tmp = decode_rfc3361(dl, data)) == NULL) return -1; l = strlen(tmp); l = print_string(s, len, l - 1, (uint8_t *)tmp); free(tmp); return l; } if (type & RFC3442) return decode_rfc3442(s, len, dl, data); if (type & RFC5969) return decode_rfc5969(s, len, dl, data); #endif if (type & STRING) { /* Some DHCP servers return NULL strings */ if (*data == '\0') return 0; return print_string(s, len, dl, data); } if (type & FLAG) { if (s) { *s++ = '1'; *s = '\0'; } return 2; } /* DHCPv6 status code */ if (type & SCODE && dl >= (int)sizeof(u16)) { if (s) { memcpy(&u16, data, sizeof(u16)); u16 = ntohs(u16); l = snprintf(s, len, "%d ", u16); len -= l; } else l = 7; data += sizeof(u16); dl -= sizeof(u16); if (dl) l += print_option(s, len, STRING, dl, data, ifname); return l; } if (!s) { if (type & UINT8) l = 3; else if (type & UINT16) { l = 5; dl /= 2; } else if (type & SINT16) { l = 6; dl /= 2; } else if (type & UINT32) { l = 10; dl /= 4; } else if (type & SINT32) { l = 11; dl /= 4; } else if (type & ADDRIPV4) { l = 16; dl /= 4; } #ifdef INET6 else if (type & ADDRIPV6) { e = data + dl; l = 0; while (data < e) { if (l) l++; /* space */ dl = ipv6_printaddr(NULL, 0, data, ifname); if (dl != -1) l += dl; data += 16; } return l + 1; } #endif else if (type & BINHEX) { l = 2; } else { errno = EINVAL; return -1; } return (l + 1) * dl; } t = data; e = data + dl; while (data < e) { if (data != t && type != BINHEX) { *s++ = ' '; bytes++; len--; } if (type & UINT8) { l = snprintf(s, len, "%d", *data); data++; } else if (type & UINT16) { memcpy(&u16, data, sizeof(u16)); u16 = ntohs(u16); l = snprintf(s, len, "%d", u16); data += sizeof(u16); } else if (type & SINT16) { memcpy(&s16, data, sizeof(s16)); s16 = ntohs(s16); l = snprintf(s, len, "%d", s16); data += sizeof(s16); } else if (type & UINT32) { memcpy(&u32, data, sizeof(u32)); u32 = ntohl(u32); l = snprintf(s, len, "%d", u32); data += sizeof(u32); } else if (type & SINT32) { memcpy(&s32, data, sizeof(s32)); s32 = ntohl(s32); l = snprintf(s, len, "%d", s32); data += sizeof(s32); } else if (type & ADDRIPV4) { memcpy(&addr.s_addr, data, sizeof(addr.s_addr)); l = snprintf(s, len, "%s", inet_ntoa(addr)); data += sizeof(addr.s_addr); } #ifdef INET6 else if (type & ADDRIPV6) { dl = ipv6_printaddr(s, len, data, ifname); if (dl != -1) l = dl; else l = 0; data += 16; } #endif else if (type & BINHEX) { l = snprintf(s, len, "%.2x", data[0]); data++; } else l = 0; len -= l; bytes += l; s += l; } return bytes; }
static ssize_t print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data) { const uint8_t *e, *t; uint16_t u16; int16_t s16; uint32_t u32; int32_t s32; struct in_addr addr; ssize_t bytes = 0; ssize_t l; char *tmp; if (type & RFC3397) { l = decode_rfc3397(NULL, 0, dl, data); if (l < 1) return l; tmp = xmalloc(l); decode_rfc3397(tmp, l, dl, data); l = print_string(s, len, l - 1, (uint8_t *)tmp); free(tmp); return l; } if (type & RFC3442) return decode_rfc3442(s, len, dl, data); if (type & STRING) { /* Some DHCP servers return NULL strings */ if (*data == '\0') return 0; return print_string(s, len, dl, data); } if (!s) { if (type & UINT8) l = 3; else if (type & UINT16) l = 5; else if (type & SINT16) l = 6; else if (type & UINT32) l = 10; else if (type & SINT32) l = 11; else if (type & IPV4) l = 16; else { errno = EINVAL; return -1; } return (l + 1) * dl; } t = data; e = data + dl; while (data < e) { if (data != t) { *s++ = ' '; bytes++; len--; } if (type & UINT8) { l = snprintf(s, len, "%d", *data); data++; } else if (type & UINT16) { memcpy(&u16, data, sizeof(u16)); u16 = ntohs(u16); l = snprintf(s, len, "%d", u16); data += sizeof(u16); } else if (type & SINT16) { memcpy(&s16, data, sizeof(s16)); s16 = ntohs(s16); l = snprintf(s, len, "%d", s16); data += sizeof(s16); } else if (type & UINT32) { memcpy(&u32, data, sizeof(u32)); u32 = ntohl(u32); l = snprintf(s, len, "%d", u32); data += sizeof(u32); } else if (type & SINT32) { memcpy(&s32, data, sizeof(s32)); s32 = ntohl(s32); l = snprintf(s, len, "%d", s32); data += sizeof(s32); } else if (type & IPV4) { memcpy(&addr.s_addr, data, sizeof(addr.s_addr)); l = snprintf(s, len, "%s", inet_ntoa(addr)); data += sizeof(addr.s_addr); } else l = 0; len -= l; bytes += l; s += l; } return bytes; }