]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dhcpmessage.cc
and the final bit of whitespace/tab cleanup
[thirdparty/pdns.git] / pdns / dhcpmessage.cc
1 /*
2 * Copyright 2006, 2007 Stefan Rompf <sux@loplof.de>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * version 2 as published by the Free Software Foundation
7 *
8 */
9
10 #include <string.h>
11 #include <stdlib.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14
15 #include <arpa/inet.h>
16 #include <netpacket/packet.h>
17 #include <net/ethernet.h>
18 #include <netinet/ip.h>
19 #include <netinet/udp.h>
20 #include <netinet/ether.h>
21 #include "iputils.hh"
22 #include "dhcpmessage.hh"
23
24 static const unsigned char vendcookie[] = { 99, 130, 83, 99 };
25 void dm_init(struct dhcp_message *msg) {
26 memset(msg, 0, sizeof(*msg));
27 msg->pos = msg->options+4;
28 // msg->flags = htons(0x8000);
29 memcpy(msg->options, vendcookie, 4);
30 }
31
32 void dm_add_option(struct dhcp_message *msg, u_int8_t option,
33 u_int8_t length, void *opt) {
34 u_int8_t *pos = msg->pos;
35
36 if (&msg->options[MAX_OPT_LEN] - pos < length + 2) abort();
37
38 *pos++ = option;
39 *pos++ = length;
40 memcpy(pos, opt, length);
41 pos += length;
42
43 msg->pos = pos;
44 }
45
46
47 void dm_finish_options(struct dhcp_message *msg) {
48 if (msg->pos == &msg->options[MAX_OPT_LEN]) abort();
49
50 *msg->pos++ = 255;
51 }
52
53
54 u_int8_t *dm_next_option(struct dhcp_message *msg) {
55 u_int8_t *pos = msg->pos;
56 u_int8_t length;
57
58 /* End of packet */
59 if (pos >= msg->last) return NULL;
60
61 /* skip pad packets */
62 while(!*pos) if (++pos >= msg->last) return NULL;
63
64 /* End of option marker */
65 while (*pos == 255) {
66 /* Overload option handling */
67 if (msg->currentblock < msg->overload) { // currentblock: 0,1,3
68 msg->currentblock = (dhcp_overload_opts)(msg->currentblock+1);
69 if (msg->overload & DHCP_OVERLOAD_FILE & msg->currentblock) {
70 pos = &msg->file[0];
71 msg->last = &msg->file[128];
72 } else { // SNAME or BOTH
73 pos = &msg->sname[0];
74 msg->last = &msg->sname[64];
75 msg->currentblock = DHCP_OVERLOAD_BOTH; // definitely last block
76 }
77 /* skip pad packets */
78 while(!*pos) if (++pos >= msg->last) return NULL;
79 } else {
80 return NULL;
81 }
82 }
83
84 /* Actually, this is extra paranoia. Even if pos+1
85 * leaves the dhcp_message structure, the next
86 * check would catch this as long as we don't
87 * try to access an unmapped page ;-)
88 */
89 if (pos+1 >= msg->last) return NULL;
90
91 length = *(pos+1);
92 /* Length overflow */
93 if (pos + length + 2 > msg->last) return NULL;
94
95 msg->pos = pos + length+2;
96
97 return pos;
98 }
99
100
101 DHCPCommunicator::DHCPCommunicator(const std::string& remoteAddr)
102 {
103 ComboAddress remote(remoteAddr, 67); // 195.241.76.195, 82.169.27.254
104 d_socket =socket(AF_INET, SOCK_DGRAM, 0);
105
106 int tmp = 1;
107 if(setsockopt(d_socket, SOL_SOCKET,SO_REUSEADDR,(char*)&tmp,sizeof tmp)<0)
108 unixDie("Setting reuse flag");
109
110 ComboAddress local("0.0.0.0", 67);
111
112 if(bind(d_socket, (struct sockaddr*)&local, local.getSocklen()) < 0)
113 unixDie("binding");
114
115 if(connect(d_socket, (struct sockaddr*)&remote, remote.getSocklen()) < 0)
116 unixDie("connecting");
117
118 }
119
120 string DHCPCommunicator::getMac(const std::string& ip)
121 {
122 struct dhcp_message msg;
123
124 dm_init(&msg);
125 msg.xid = random();
126 msg.op = BOOTP_OPCODE_REQUEST;
127 msg.htype = 0;
128 msg.hlen = 0;
129 memset(&msg.chaddr, 0, sizeof(msg.chaddr));
130
131 ComboAddress about(ip);
132
133 msg.ciaddr = about.sin4.sin_addr.s_addr; // 0x0c01000a; // 0xda837bd4; // ask about 4.3.2.1
134
135 struct sockaddr_in local;
136 socklen_t locallen=sizeof(local);
137 getsockname(d_socket, (struct sockaddr*)&local, &locallen);
138
139 msg.giaddr = local.sin_addr.s_addr; // 0x0c01000a; // 0xda837bd4; // or IP is also 4.3.2.1 (it ain't)
140
141 int dhcptype = 10; // 13; // DHCP_TYPE_LEASEQUERY cisco style
142 dm_add_option(&msg, DHCP_OPTION_TYPE, 1, &dhcptype);
143
144 dhcptype = 51;
145 dm_add_option(&msg, DHCP_OPTION_PARAMREQ, 1, &dhcptype);
146
147 dhcptype = 82;
148 dm_add_option(&msg, DHCP_OPTION_PARAMREQ, 1, &dhcptype);
149
150 send(d_socket, (char*)&msg.op, msg.pos - &msg.op, 0);
151
152 char packet[1500];
153
154 int ret=recv(d_socket, packet, sizeof(packet), 0);
155 if(ret > 0) {
156 memcpy((char*)&msg.op, packet, ret);
157 char mac[19];
158 snprintf(mac, 19, "%02x:%02x:%02x:%02x:%02x:%02x", msg.chaddr[0], msg.chaddr[1], msg.chaddr[2],
159 msg.chaddr[3], msg.chaddr[4], msg.chaddr[5]);
160 return mac;
161 }
162
163 return "unknown";
164 }
165
166
167 #if 0
168
169 int dm_parse_msg_raw(char *dframe, int plen,
170 struct in_addr *from_ip, struct dhcp_message *msg) {
171 struct iphdr *ip;
172 struct udphdr *udp;
173 int iphlen, udplen;
174 u_short checksum;
175
176 if (plen < sizeof(*ip)) return -1;
177
178 /* Verify IP: IP, UDP, ... */
179 ip = (struct iphdr *)dframe;
180 iphlen = 4 * ip->ihl;
181 if (ip->version != 4) return -1; /* no ipv4 packet */
182 if (plen < iphlen || iphlen < 20) return -1; /* ip header too short */
183 if (plen < ntohs(ip->tot_len)) return -1; /* packet too short */
184 if (in_cksum((u_short *)ip, iphlen, 0)) return -1; /* checksum wrong */
185 if (ip->protocol != IPPROTO_UDP) return -1; /* no udp */
186 if (ip->frag_off & htons(IP_OFFMASK)) return -1; /* won't parse fragments */
187
188 from_ip->s_addr = ip->saddr;
189
190 /* UDP src, destination */
191 udp = (struct udphdr *)&dframe[iphlen];
192 if (udp->source != htons(BOOTP_SERVER_PORT)) return -1;
193 if (udp->dest != htons(BOOTP_CLIENT_PORT)) return -1;
194
195 udplen = ntohs(udp->len);
196 if (iphlen + udplen > plen) return -1; /* truncated BOOTPREPLY */
197
198 /* check udp checksum */
199 if (udp->check != 0 && udp->check != 0xffff) {
200 /* FIXME: checksum 0xffff has to be treated as 0. Until I've constructed
201 a testcase, treat 0xffff as no checksum */
202 /* RFC 768: Calculate the checksum including the pseudo header
203 s-ip(4), d-ip(4), 0x00(1), proto(1), udp-length(2) */
204 checksum = htons(IPPROTO_UDP);
205 checksum = in_cksum((u_short *)&udp->len, 2, checksum);
206 checksum = in_cksum((u_short *)&ip->saddr, 8, ~checksum); // ip options might follow
207 checksum = in_cksum((u_short *)udp, udplen, ~checksum); // saddr + daddr + udp
208 if (checksum) return -1; /* udp packet checksum wrong */
209 }
210 udplen -= sizeof(*udp); /* udplen is now dhcplen! */
211 if (udplen < &msg->options[4] - &msg->op) return -1; /* BOOTPREPLY too short */
212
213 memcpy(&msg->op, &dframe[iphlen+sizeof(*udp)], udplen);
214
215 if (memcmp(msg->options, vendcookie, 4)) return -1; /* No DHCP message */
216
217 msg->pos = msg->options+4;
218 msg->last = msg->pos + (udplen + &msg->options[0] - &msg->op);
219 msg->overload = DHCP_OVERLOAD_NONE;
220 msg->currentblock = DHCP_OVERLOAD_NONE;
221
222 return 0;
223 }
224
225 #endif
226