+ if (options && optlen)
+ memcpy(&opt[4], options, optlen);
+
+ if (file && filelen <= 128)
+ memcpy(&message->file, file, filelen);
+
+ if (sname && snamelen <= 64)
+ memcpy(&message->sname, sname, snamelen);
+
+ return message;
+}
+
+static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen)
+{
+ while (*descpos < *desclen) {
+ switch(descoption[*descpos]) {
+ case DHCP_OPTION_PAD:
+ *descpos += 1;
+ break;
+
+ case DHCP_OPTION_MESSAGE_TYPE:
+ case DHCP_OPTION_OVERLOAD:
+ *descpos += 3;
+ break;
+
+ default:
+ return;
+ }
+ }
+}
+
+static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option,
+ void *user_data)
+{
+ struct option_desc *desc = user_data;
+ uint8_t *descoption = NULL;
+ int *desclen = NULL, *descpos = NULL;
+ uint8_t optcode = 0;
+ uint8_t optlen = 0;
+ uint8_t i;
+
+ assert((!desc && !code && !len) || desc);
+
+ if (!desc)
+ return -EINVAL;
+
+ assert(code != DHCP_OPTION_PAD);
+ assert(code != DHCP_OPTION_END);
+ assert(code != DHCP_OPTION_MESSAGE_TYPE);
+ assert(code != DHCP_OPTION_OVERLOAD);
+
+ while (desc->pos >= 0 || desc->filepos >= 0 || desc->snamepos >= 0) {
+
+ if (desc->pos >= 0) {
+ descoption = &desc->options[0];
+ desclen = &desc->len;
+ descpos = &desc->pos;
+ } else if (desc->filepos >= 0) {
+ descoption = &desc->file[0];
+ desclen = &desc->filelen;
+ descpos = &desc->filepos;
+ } else if (desc->snamepos >= 0) {
+ descoption = &desc->sname[0];
+ desclen = &desc->snamelen;
+ descpos = &desc->snamepos;
+ }
+
+ assert(descoption && desclen && descpos);
+
+ if (*desclen)
+ test_ignore_opts(descoption, descpos, desclen);
+
+ if (*descpos < *desclen)
+ break;
+
+ if (*descpos == *desclen)
+ *descpos = -1;
+ }
+
+ assert(descpos);
+ assert(*descpos != -1);
+
+ optcode = descoption[*descpos];
+ optlen = descoption[*descpos + 1];
+
+ if (verbose)
+ printf("DHCP code %2d(%2d) len %2d(%2d) ", code, optcode,
+ len, optlen);
+
+ assert(code == optcode);
+ assert(len == optlen);
+
+ for (i = 0; i < len; i++) {
+
+ if (verbose)
+ printf("0x%02x(0x%02x) ", option[i],
+ descoption[*descpos + 2 + i]);
+
+ assert(option[i] == descoption[*descpos + 2 + i]);
+ }
+
+ if (verbose)
+ printf("\n");
+
+ *descpos += optlen + 2;
+
+ test_ignore_opts(descoption, descpos, desclen);
+
+ if (desc->pos != -1 && desc->pos == desc->len)
+ desc->pos = -1;
+
+ if (desc->filepos != -1 && desc->filepos == desc->filelen)
+ desc->filepos = -1;
+
+ if (desc->snamepos != -1 && desc->snamepos == desc->snamelen)
+ desc->snamepos = -1;
+
+ return 0;
+}
+
+static void test_options(struct option_desc *desc)
+{
+ uint8_t *options = NULL;
+ uint8_t *file = NULL;
+ uint8_t *sname = NULL;
+ int optlen = 0;
+ int filelen = 0;
+ int snamelen = 0;
+ int buflen = 0;
+ _cleanup_free_ DHCPMessage *message;
+ int res;
+
+ if (desc) {
+ file = &desc->file[0];
+ filelen = desc->filelen;
+ if (!filelen)
+ desc->filepos = -1;
+
+ sname = &desc->sname[0];
+ snamelen = desc->snamelen;
+ if (!snamelen)
+ desc->snamepos = -1;
+
+ options = &desc->options[0];
+ optlen = desc->len;
+ desc->pos = 0;
+ }
+ message = create_message(options, optlen, file, filelen,
+ sname, snamelen);
+
+ buflen = sizeof(DHCPMessage) + 4 + optlen;
+
+ if (!desc) {
+ assert((res = dhcp_option_parse(message, buflen,
+ test_options_cb,
+ NULL)) == -ENOMSG);
+ } else if (desc->success) {
+ assert((res = dhcp_option_parse(message, buflen,
+ test_options_cb,
+ desc)) >= 0);
+ assert(desc->pos == -1 && desc->filepos == -1 &&
+ desc->snamepos == -1);
+ } else
+ assert((res = dhcp_option_parse(message, buflen,
+ test_options_cb,
+ desc)) < 0);
+
+ if (verbose)
+ printf("DHCP type %s\n", dhcp_type(res));
+}
+
+static uint8_t result[64] = {
+ 'A', 'B', 'C', 'D',
+};
+
+static uint8_t options[64] = {
+ 'A', 'B', 'C', 'D',
+ 160, 2, 0x11, 0x12,
+ 0,
+ 31, 8, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0,
+ 55, 3, 0x51, 0x52, 0x53,
+ 17, 7, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 255
+};
+
+static void test_option_set(void)
+{
+ size_t len, oldlen;
+ int pos, i;
+ uint8_t *opt;
+
+ assert(dhcp_option_append(NULL, NULL, 0, 0, NULL) == -EINVAL);
+
+ len = 0;
+ opt = &result[0];
+ assert(dhcp_option_append(&opt, NULL, 0, 0, NULL) == -EINVAL);
+ assert(opt == &result[0] && len == 0);
+
+ assert(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
+ 0, NULL) == -ENOBUFS);
+ assert(opt == &result[0] && len == 0);
+
+ opt = &result[4];
+ len = 1;
+ assert(dhcp_option_append(&opt, &len, DHCP_OPTION_PAD,
+ 0, NULL) >= 0);
+ assert(opt == &result[5] && len == 0);
+
+ pos = 4;
+ len = 60;
+ while (pos < 64 && options[pos] != DHCP_OPTION_END) {
+ opt = &result[pos];
+ oldlen = len;
+
+ assert(dhcp_option_append(&opt, &len, options[pos],
+ options[pos + 1],
+ &options[pos + 2]) >= 0);
+
+ if (options[pos] == DHCP_OPTION_PAD) {
+ assert(opt == &result[pos + 1]);
+ assert(len == oldlen - 1);
+ pos++;
+ } else {
+ assert(opt == &result[pos + 2 + options[pos + 1]]);
+ assert(len == oldlen - 2 - options[pos + 1]);
+ pos += 2 + options[pos + 1];
+ }
+ }
+
+ for (i = 0; i < pos; i++) {
+ if (verbose)
+ printf("%2d: 0x%02x(0x%02x)\n", i, result[i],
+ options[i]);
+ assert(result[i] == options[i]);
+ }
+
+ if (verbose)
+ printf ("\n");