/*=========================================================================*/ int DHCPGetOptionInfo(unsigned char *dhcpOptCodes, int dhcpOptCodeCnt, DHCPInfoCallBack *dhcpInfoCB, void *context) /* Calls dhcpInfoCB once for each requested option in dhcpOptCodes. Returns - zero on success, non-zero on failure errno ENOTCONN error during read ETIME read timed out ENOMEM out of memory EINVAL parse error BOOTP/DHCP packet header format: Offs Len Name Description 0 1 opcode Message opcode: 1 = BOOTREQUEST, 2 = BOOTREPLY 1 1 htype Hardware address type (eg., 1 = 10mb ethernet) 2 1 hlen Hardware address length (eg., 6 = 10mb ethernet) 3 1 hops Client sets to zero, optionally used by relay agents 4 4 xid Transaction ID, random number chosen by client 8 2 secs Client sets to seconds since start of boot process 10 2 flags Bit 0: broadcast response bit 12 4 ciaddr Client IP address - only filled if client is bound 16 4 yiaddr 'your' (Client) IP address 20 4 siaddr IP address of next server to use in bootstrap 24 4 giaddr Relay agent IP address, used in booting via RA 28 16 chaddr Client hardware address 44 64 sname Optional server host name, null-terminated string 108 128 file Boot file name, null-terminated string 236 var options Optional parameters field The options field has the following format: Offs Len Name Description 0 4 cookie 4-byte cookie field: 99.130.83.99 (0x63825363) Followed by 1-byte option codes and 1-byte option lengths, except for the two special fixed length options, pad (0) and end (255). Options are defined in slp_dhcp.h as TAG_XXX values. The two we really care about here are options TAG_SLP_DA and TAG_SLP_SCOPE, 78 and 79, respectively. The format for TAG_SLP_DA (starting with the tag) is: Offs Len Name Description 0 1 tag TAG_SLP_DA - directory agent ip addresses 1 1 length length of remaining data in the option 2 1 mand flag: the use of these DA's is mandatory 3 4 a(0) 4-byte ip address ... 3+n*4 4 a(n) 4-byte ip address The format for TAG_SLP_SCOPE (starting with the tag) is: Offs Len Name Description 0 1 tag TAG_SLP_SCOPE - directory scopes to use 1 1 length length of remaining data in the option 2 1 mand flag: the use of these scopes is mandatory 3 var scopes a null-terminated, comma-separated string of scopes The "DHCP Message Type" option must be included in every DHCP message. All tags except for TAG_PAD(0) and TAG_END(255) begin with a tag value followed by a length of remaining data value. =========================================================================*/ { UINT32 xid; time_t timer; struct timeval tv; int sockfd, retries; struct sockaddr_in sendaddr; unsigned char chaddr[MAX_MACADDR_SIZE]; unsigned char hlen, htype; unsigned char sndbuf[512]; unsigned char rcvbuf[512]; struct hostent *hep; unsigned char *p; size_t rcvbufsz = 0; char host[256]; /* Get our IP and MAC addresses */ if(gethostname(host, (int)sizeof(host)) || !(hep = gethostbyname(host)) || dhcpGetAddressInfo((unsigned char *)hep->h_addr, chaddr, &hlen, &htype)) return -1; /* get a reasonably random transaction id value */ xid = (UINT32)time(&timer); /* BOOTP request header */ memset(sndbuf, 0, 236); /* clear bootp header */ p = sndbuf; *p++ = BOOTREQUEST; /* opcode */ *p++ = htype; *p++ = hlen; p++; /* hops */ ToUINT32(p, xid); p += 2 * sizeof(UINT32); /* xid, secs, flags */ memcpy(p, hep->h_addr, 4); p += 4 * sizeof(UINT32); /* ciaddr, yiaddr, siaddr, giaddr */ memcpy(p, chaddr, hlen); p += 16 + 64 + 128; /* chaddr, sname and file */ *p++ = DHCP_COOKIE1; /* options, cookies 1-4 */ *p++ = DHCP_COOKIE2; *p++ = DHCP_COOKIE3; *p++ = DHCP_COOKIE4; /* DHCP Message Type option */ *p++ = TAG_DHCP_MSG_TYPE; *p++ = 1; /* option length */ *p++ = DHCP_MSG_INFORM; /* message type is DHCPINFORM */ /* DHCP Parameter Request option */ *p++ = TAG_DHCP_PARAM_REQ; /* request for DHCP parms */ *p++ = (unsigned char)dhcpOptCodeCnt; memcpy(p, dhcpOptCodes, dhcpOptCodeCnt); p += dhcpOptCodeCnt; /* DHCP Client Identifier option */ *p++ = TAG_CLIENT_IDENTIFIER; *p++ = hlen + 1; /* option length */ *p++ = htype; /* client id is htype/haddr */ memcpy(p, chaddr, hlen); p += hlen; /* End option */ *p++ = TAG_END; /* get a broadcast send/recv socket and address */ if((sockfd = dhcpCreateBCSkt(&sendaddr)) < 0) return -1; /* setup select timeout */ tv.tv_sec = 0; tv.tv_usec = INIT_TMOUT_USECS; retries = 0; srand((unsigned)time(&timer)); while (retries++ < MAX_DHCP_RETRIES) { if(dhcpSendRequest(sockfd, sndbuf, p - sndbuf, (struct sockaddr *)&sendaddr, &tv) < 0) { if (errno != ETIMEDOUT) { closesocket(sockfd); return -1; } } else if((rcvbufsz = dhcpRecvResponse(sockfd, rcvbuf, sizeof(rcvbuf), &tv)) < 0) { if (errno != ETIMEDOUT) { closesocket(sockfd); return -1; } } else if(rcvbufsz >= 236 && AsUINT32(&rcvbuf[4]) == xid) break; /* exponential backoff randomized by a uniform number between -1 and 1 */ tv.tv_usec = tv.tv_usec * 2 + (rand() % 3) - 1; tv.tv_sec = tv.tv_usec / USECS_PER_SEC; tv.tv_usec %= USECS_PER_SEC; } closesocket(sockfd); return rcvbufsz? dhcpProcessOptions(rcvbuf + 236, rcvbufsz - 236, dhcpInfoCB, context): -1; }
/** Queries a DHCP server and calls a callback once for each option. * * Queries the sub-net DHCP server for a specified set of DHCP options * and then calls a user-supplied callback function once for each of * the desired options returned by the server. * * @param[in] dhcpOptCodes - An array of option codes to query for. * @param[in] dhcpOptCodeCnt - The number of elements in @p dhcpOptCodes. * @param[in] dhcpInfoCB - The callback function to call for each option. * @param[in] context - callback context. * * @return Zero on success; non-zero on failure, with errno set to * ENOTCONN (read error), ETIMEDOUT (read timeout), ENOMEM (out of * memory), or EINVAL (on parse error). */ int DHCPGetOptionInfo(unsigned char * dhcpOptCodes, int dhcpOptCodeCnt, DHCPInfoCallBack * dhcpInfoCB, void * context) { uint32_t xid; time_t timer; struct timeval tv; sockfd_t sockfd; int retries; struct sockaddr_storage sendaddr; unsigned char chaddr[MAX_MACADDR_SIZE]; unsigned char hlen, htype; uint8_t sndbuf[512]; uint8_t rcvbuf[512]; struct hostent * hep; uint8_t * p; int rcvbufsz = 0; char host[256]; /* Get our IP and MAC addresses */ if (gethostname(host, (int)sizeof(host)) || (hep = gethostbyname(host)) == 0 || dhcpGetAddressInfo((unsigned char *)hep->h_addr, chaddr, &hlen, &htype)) return -1; /* get a reasonably random transaction id value */ xid = (uint32_t)time(&timer); /* BOOTP request header */ p = sndbuf; *p++ = BOOTREQUEST; /* opcode */ *p++ = htype; *p++ = hlen; *p++ = 0; /* hops */ PutUINT32(&p, xid); PutUINT16(&p, 0); /* seconds */ PutUINT16(&p, 0); /* flags */ memcpy(p, hep->h_addr, 4); /* ciaddr */ p += 4; PutUINT32(&p, 0); /* yiaddr */ PutUINT32(&p, 0); /* siaddr */ PutUINT32(&p, 0); /* giaddr */ memcpy(p, chaddr, hlen); /* chaddr */ p += hlen; memset(p, 0, 16-hlen); /* remaining chaddr space */ p += 16-hlen; memset(p, 0, 64 + 128); /* server host name, boot file */ p += 64 + 128; /* BOOTP options field */ *p++ = DHCP_COOKIE1; /* options, cookies 1-4 */ *p++ = DHCP_COOKIE2; *p++ = DHCP_COOKIE3; *p++ = DHCP_COOKIE4; /* DHCP Message Type option */ *p++ = TAG_DHCP_MSG_TYPE; *p++ = 1; /* option length */ *p++ = DHCP_MSG_INFORM; /* message type is DHCPINFORM */ /* DHCP Parameter Request option */ *p++ = TAG_DHCP_PARAM_REQ; /* request for DHCP parms */ *p++ = (unsigned char)dhcpOptCodeCnt; memcpy(p, dhcpOptCodes, dhcpOptCodeCnt); p += dhcpOptCodeCnt; /* DHCP Client Identifier option */ *p++ = TAG_CLIENT_IDENTIFIER; *p++ = hlen + 1; /* option length */ *p++ = htype; /* client id is htype/haddr */ memcpy(p, chaddr, hlen); p += hlen; /* End option */ *p++ = TAG_END; /* get a broadcast send/recv socket and address */ if ((sockfd = dhcpCreateBCSkt(&sendaddr)) == SLP_INVALID_SOCKET) return -1; /* setup select timeout */ tv.tv_sec = 0; tv.tv_usec = INIT_TMOUT_USECS; retries = 0; srand((unsigned)time(&timer)); while (retries++ < MAX_DHCP_RETRIES) { if (dhcpSendRequest(sockfd, sndbuf, p - sndbuf, &sendaddr, sizeof(sendaddr), &tv) < 0) { if (errno != ETIMEDOUT) { closesocket(sockfd); return -1; } } else if ((rcvbufsz = dhcpRecvResponse(sockfd, rcvbuf, sizeof(rcvbuf), &tv)) < 0) { if (errno != ETIMEDOUT) { closesocket(sockfd); return -1; } } else if (rcvbufsz >= 236 && AS_UINT32(&rcvbuf[4]) == xid) break; /* exponential backoff randomized by a * uniform number between -1 and 1 */ tv.tv_usec = tv.tv_usec * 2 + (rand() % 3) - 1; tv.tv_sec = tv.tv_usec / USECS_PER_SEC; tv.tv_usec %= USECS_PER_SEC; } closesocket(sockfd); return rcvbufsz? dhcpProcessOptions(rcvbuf + 236, rcvbufsz - 236, dhcpInfoCB, context): -1; }