1 //
2 // $Id: dhcping.c,v 1.3 2002/01/27 01:57:15 mavetju Exp $
3 //
4
5 /*
6 * Copyright 2000, 2001, 2002 by Edwin Groothuis, edwin@mavetju.org
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 */
30
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <sys/time.h>
34 #include <sys/uio.h>
35 #include <netinet/in.h>
36 #include <unistd.h>
37 #include <netdb.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <time.h>
42
43 #include "dhcp_options.h"
44
45 #define BUF_SIZ 256*256
46
47 int offset=0;
addpacket(char * pktbuf,char * msgbuf,int size)48 void addpacket(char *pktbuf,char *msgbuf,int size) {
49 memcpy(pktbuf+offset,msgbuf,size);
50 offset+=size;
51 }
52
53 void dhcp_setup(char *);
54 int dhcp_read(void);
55 void dhcp_close(void);
56 void dhcp_dump(unsigned char *buffer,int size);
57 void dhcp_inform(char *ipaddr,char *gwaddr,char *hardware);
58 void dhcp_request(char *ipaddr,char *gwaddr,char *hardware);
59 void dhcp_release(char *ipaddr,char *gwaddr,char *hardware);
60 void dhcp_packet(int type,char *ciaddr,char *opt50,char *gwaddr,char *hardware);
61
62 int dhcp_socket;
63 struct sockaddr_in dhcp_to;
64
65 int _serveripaddress;
66
67 int inform,request,verbose,VERBOSE,quiet;
68 char *ci,*gi,*server,*hw;
69 unsigned char serveridentifier[4];
70 int maxwait=3;
71
doargs(int argc,char ** argv)72 void doargs(int argc,char **argv) {
73 char ch;
74
75 inform=request=verbose=VERBOSE=quiet=0;
76 ci=gi=server="0.0.0.0";
77 hw="00:00:00:00:00:00";
78
79 if (argc==1) {
80 printf("dhcping -c ciaddr -g giaddr -h chaddr -r -s server -t maxwait -i -v -q\n");
81 exit(1);
82 }
83
84 while ((ch = getopt(argc,argv,"c:g:h:iqrs:t:vV"))>0) {
85 switch (ch) {
86 case 'c': ci=optarg;break;
87 case 'g': gi=optarg;break;
88 case 'h': hw=optarg;break;
89 case 'i': inform=1;break;
90 case 'q': quiet=1;break;
91 case 'r': request=1;break;
92 case 's': server=optarg;break;
93 case 't': maxwait=atoi(optarg);break;
94 case 'v': verbose=1;break;
95 case 'V': VERBOSE=1;break;
96 }
97 }
98
99 if (request && inform) {
100 fprintf(stderr,"Error: -r and -i are mutally exclusive.\n");
101 exit(1);
102 }
103
104 // DHCPREQUEST is by default.
105 if (!inform) request=1;
106 }
107
main(int argc,char ** argv)108 int main(int argc,char **argv) {
109 fd_set read;
110 struct timeval timeout;
111 int foundpacket=0;
112 int returnvalue=0;
113
114 if (geteuid()!=0) {
115 printf("This program should only be ran by root or be installed as setuid root.\n");
116 exit(1);
117 }
118
119 doargs(argc,argv);
120
121 if (VERBOSE) puts("setup");
122 dhcp_setup(server);
123
124 if (setuid(getuid())!=0) {
125 perror("setuid");
126 printf("Can't drop privileges back to normal user, program aborted.\n");
127 exit(1);
128 }
129
130 if (inform) {
131 if (VERBOSE) puts("inform");
132 dhcp_inform(ci,gi,hw);
133 }
134 if (request) {
135 if (VERBOSE) puts("request");
136 dhcp_request(ci,gi,hw);
137 }
138
139 while (!foundpacket) {
140 FD_ZERO(&read);
141 FD_SET(dhcp_socket,&read);
142 timeout.tv_sec=maxwait;
143 timeout.tv_usec=0;
144 if(select(dhcp_socket+1,&read,NULL,NULL,&timeout)<0) {
145 perror("select");
146 exit(0);
147 }
148 if (FD_ISSET(dhcp_socket,&read)) {
149 if (VERBOSE) puts("read");
150 /* If a expected packet was found, then also release it. */
151 if ((foundpacket=dhcp_read())!=0) {
152 if (request) {
153 if (VERBOSE) puts("release");
154 dhcp_release(ci,gi,hw);
155 }
156 }
157 } else {
158 if (!quiet)
159 fprintf(stderr,"no answer\n");
160 returnvalue=1;
161 foundpacket=1;
162 }
163 }
164 if (VERBOSE) puts("close");
165 dhcp_close();
166 return returnvalue;
167 }
168
169
dhcp_setup(char * serveripaddress)170 void dhcp_setup(char *serveripaddress) {
171 struct servent *servent,*clientent;
172 struct hostent *hostent;
173 int flag;
174 struct sockaddr_in name;
175
176 /*
177 // setup sending socket
178 */
179 if ((servent=getservbyname("bootps",0))==NULL) {
180 perror("getservbyname: bootps");
181 exit(1);
182 }
183 if ((hostent=gethostbyname(serveripaddress))==NULL) {
184 perror("gethostbyname");
185 exit(1);
186 }
187
188 dhcp_to.sin_family=AF_INET;
189 bcopy(hostent->h_addr,&dhcp_to.sin_addr.s_addr,hostent->h_length);
190 _serveripaddress=ntohl(dhcp_to.sin_addr.s_addr);
191 /* dhcp_to.sin_addr.s_addr=INADDR_BROADCAST; */
192 dhcp_to.sin_port=servent->s_port;
193
194 if ((dhcp_socket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))==-1) {
195 perror("dhcp_socket/socket");
196 exit(1);
197 }
198
199 flag=1;
200 if (setsockopt (dhcp_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof flag) < 0) {
201 perror("dhcp_socket/setsockopt: SO_REUSEADDR");
202 exit(1);
203 }
204
205 if (setsockopt(dhcp_socket,SOL_SOCKET,SO_BROADCAST,(char *)&flag, sizeof flag) < 0) {
206 perror ("dhcp_socket/setsockopt: SO_BROADCAST");
207 exit(1);
208 }
209
210 if ((clientent=getservbyname("bootpc",0))==NULL) {
211 perror("getservbyname: bootpc");
212 exit(1);
213 }
214 name.sin_family = AF_INET;
215 name.sin_port = clientent->s_port;
216 name.sin_addr.s_addr = INADDR_ANY;
217 /* name.sin_addr.s_addr = INADDR_NONE; */
218 memset (name.sin_zero, 0, sizeof (name.sin_zero));
219
220 if (bind (dhcp_socket, (struct sockaddr *)&name, sizeof name) < 0) {
221 perror("bind");
222 exit(1);
223 }
224 }
225
dhcp_request(char * ipaddr,char * gwaddr,char * hardware)226 void dhcp_request(char *ipaddr,char *gwaddr,char *hardware) {
227 dhcp_packet(3,ipaddr,ipaddr,gwaddr,hardware);
228 }
dhcp_release(char * ipaddr,char * gwaddr,char * hardware)229 void dhcp_release(char *ipaddr,char *gwaddr,char *hardware) {
230 dhcp_packet(7,ipaddr,NULL,gwaddr,hardware);
231 }
dhcp_inform(char * ipaddr,char * gwaddr,char * hardware)232 void dhcp_inform(char *ipaddr,char *gwaddr,char *hardware) {
233 dhcp_packet(8,ipaddr,NULL,gwaddr,hardware);
234 }
235
236
dhcp_packet(int type,char * ipaddr,char * opt50,char * gwaddr,char * hardware)237 void dhcp_packet(int type,char *ipaddr,char *opt50,char *gwaddr,char *hardware) {
238 static time_t l=0;
239 unsigned char msgbuf[BUF_SIZ];
240 unsigned char pktbuf[BUF_SIZ];
241 int ip[4],gw[4],hw[16],ip50[4];
242 int hwcount;
243
244 sscanf(ipaddr,"%d.%d.%d.%d",&ip[0],&ip[1],&ip[2],&ip[3]);
245 sscanf(gwaddr,"%d.%d.%d.%d",&gw[0],&gw[1],&gw[2],&gw[3]);
246 if (opt50)
247 sscanf(opt50,"%d.%d.%d.%d",&ip50[0],&ip50[1],&ip50[2],&ip50[3]);
248 memset(&hw,0,sizeof(hw));
249 hwcount=sscanf(hardware,"%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x:%x",
250 &hw[0],&hw[1],&hw[2],&hw[3],
251 &hw[4],&hw[5],&hw[6],&hw[7],
252 &hw[8],&hw[9],&hw[10],&hw[11],
253 &hw[12],&hw[13],&hw[14],&hw[15]);
254
255 memset(msgbuf,0,sizeof(msgbuf));
256 sprintf(msgbuf,"\1\1%c%c",hwcount,0);
257 addpacket(pktbuf,msgbuf,4);
258
259 /* xid */
260 if (l>time(NULL))
261 l++;
262 else
263 l=time(NULL);
264 memcpy(msgbuf,&l,4);
265 addpacket(pktbuf,msgbuf,4);
266
267 /* secs and flags */
268 memset(msgbuf,0,4);
269 addpacket(pktbuf,msgbuf,4);
270 /* sprintf(msgbuf,"%c%c",0x80,0x00); */
271 /* sprintf(msgbuf,"%c%c",0x00,0x00); */
272 /* addpacket(pktbuf,msgbuf,2); */
273
274 /* ciaddr */
275 memset(msgbuf,0,4);
276 sprintf(msgbuf,"%c%c%c%c",ip[0],ip[1],ip[2],ip[3]);
277 addpacket(pktbuf,msgbuf,4);
278
279 /* yiaddr */
280 memset(msgbuf,0,4);
281 addpacket(pktbuf,msgbuf,4);
282
283 /* siaddr */
284 memset(msgbuf,0,4);
285 addpacket(pktbuf,msgbuf,4);
286
287 /* giaddr */
288 sprintf(msgbuf,"%c%c%c%c",gw[0],gw[1],gw[2],gw[3]);
289 addpacket(pktbuf,msgbuf,4);
290
291 /* chaddr */
292 sprintf(msgbuf,"%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
293 hw[0],hw[1],hw[2],hw[3],hw[4],hw[5],hw[6],hw[7],
294 hw[8],hw[9],hw[10],hw[11],hw[12],hw[13],hw[14],hw[15]);
295 addpacket(pktbuf,msgbuf,16);
296
297 /* sname */
298 memset(msgbuf,0,64);
299 addpacket(pktbuf,msgbuf,64);
300
301 /* file */
302 memset(msgbuf,0,128);
303 addpacket(pktbuf,msgbuf,128);
304
305 /* options */
306 {
307 /* cookie */
308 sprintf(msgbuf,"%c%c%c%c",99,130,83,99);
309 addpacket(pktbuf,msgbuf,4);
310
311 /* dhcp-type */
312 sprintf(msgbuf,"%c%c%c",53,1,type);
313 addpacket(pktbuf,msgbuf,3);
314
315 /* Not for inform */
316 if (type!=8) {
317 /* requested IP address */
318 if (opt50) {
319 sprintf(msgbuf,"%c%c%c%c%c%c",50,4,ip50[0],ip50[1],ip50[2],ip50[3]);
320 addpacket(pktbuf,msgbuf,6);
321 }
322
323 /* server-identifier */
324 if (serveridentifier[0]) {
325 sprintf(msgbuf,"%c%c%c%c%c%c",54,4,
326 serveridentifier[0],serveridentifier[1],
327 serveridentifier[2],serveridentifier[3]);
328 addpacket(pktbuf,msgbuf,6);
329 }
330 }
331
332 /* client-identifier */
333 // sprintf(msgbuf,"%c%c%c%c%c%c%c%c",61,6,
334 // hw[0],hw[1],hw[2],hw[3],hw[4],hw[5]);
335 // addpacket(pktbuf,msgbuf,8);
336
337 /* parameter request list */
338 if (type==8) {
339 sprintf(msgbuf,"%c%c%c",55,1,1);
340 addpacket(pktbuf,msgbuf,3);
341 }
342
343 /* end of options */
344 sprintf(msgbuf,"%c",255);
345 addpacket(pktbuf,msgbuf,1);
346 }
347
348 dhcp_dump(pktbuf,offset);
349
350 sendto(dhcp_socket,pktbuf,offset,0,(struct sockaddr *)&dhcp_to,sizeof(dhcp_to));
351
352 offset=0;
353 }
354
355
dhcp_read(void)356 int dhcp_read(void) {
357 unsigned char msgbuf[BUF_SIZ];
358 struct sockaddr_in fromsock;
359 socklen_t fromlen=sizeof(fromsock);
360 int addr;
361 int i;
362
363 i=recvfrom(dhcp_socket,msgbuf,BUF_SIZ,0,(struct sockaddr *)&fromsock,&fromlen);
364 addr=ntohl(fromsock.sin_addr.s_addr);
365
366 if (!quiet) {
367 printf( "Got answer from: %d.%d.%d.%d\n",
368 ( addr >> 24 ) & 0xFF, ( addr >> 16 ) & 0xFF,
369 ( addr >> 8 ) & 0xFF, ( addr ) & 0xFF
370 );
371 }
372
373 if (_serveripaddress!=addr) {
374 if (!quiet)
375 fprintf(stderr,"received from %d.%d.%d.%d, expected from %d.%d.%d.%d\n",
376 ( addr >> 24 ) & 0xFF, ( addr >> 16 ) & 0xFF,
377 ( addr >> 8 ) & 0xFF, ( addr ) & 0xFF,
378 ( _serveripaddress >> 24 )&0xFF,(_serveripaddress >> 16 )&0xFF,
379 ( _serveripaddress >> 8 )&0xFF,(_serveripaddress )&0xFF
380 );
381 return 0;
382
383 }
384
385
386 dhcp_dump(msgbuf,i);
387 return 1;
388 }
389
390
391
dhcp_dump(unsigned char * buffer,int size)392 void dhcp_dump(unsigned char *buffer,int size) {
393 int j;
394
395 if (VERBOSE)
396 printf("packet %d bytes\n",size);
397
398 if (!VERBOSE)
399 return;
400
401 //
402 // Are you sure you want to see this? Try dhcpdump, which is better
403 // suited for this kind of work... See http://www.mavetju.org
404 //
405 j=0;
406 while (j<size) {
407 printf("%02x ",buffer[j]);
408 if (j%16==15) printf("\n");
409 j++;
410 }
411 printf("\n");
412
413 printf("op: %d\n",buffer[0]);
414 printf("htype: %d\n",buffer[1]);
415 printf("hlen: %d\n",buffer[2]);
416 printf("hops: %d\n",buffer[3]);
417
418 printf("xid: %02x%02x%02x%02x\n",
419 buffer[4],buffer[5],buffer[6],buffer[7]);
420 printf("secs: %d\n",255*buffer[8]+buffer[9]);
421 printf("flags: %x\n",255*buffer[10]+buffer[11]);
422
423 printf("ciaddr: %d.%d.%d.%d\n",
424 buffer[12],buffer[13],buffer[14],buffer[15]);
425 printf("yiaddr: %d.%d.%d.%d\n",
426 buffer[16],buffer[17],buffer[18],buffer[19]);
427 printf("siaddr: %d.%d.%d.%d\n",
428 buffer[20],buffer[21],buffer[22],buffer[23]);
429 printf("giaddr: %d.%d.%d.%d\n",
430 buffer[24],buffer[25],buffer[26],buffer[27]);
431 printf("chaddr: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
432 buffer[28],buffer[29],buffer[30],buffer[31],
433 buffer[32],buffer[33],buffer[34],buffer[35],
434 buffer[36],buffer[37],buffer[38],buffer[39],
435 buffer[40],buffer[41],buffer[42],buffer[43]);
436 printf("sname : %s.\n",buffer+44);
437 printf("fname : %s.\n",buffer+108);
438
439 j=236;
440 j+=4; /* cookie */
441 while (j<size && buffer[j]!=255) {
442 printf("option %d %s\n",buffer[j],dhcp_options[buffer[j]]);
443
444 switch (buffer[j]) {
445 case 1:
446 printf("\tSubnet mask: %d.%d.%d.%d\n",
447 buffer[j+2],buffer[j+3],buffer[j+4],buffer[j+5]);
448 break;
449 case 3:
450 printf("\tRouter: %d.%d.%d.%d\n",
451 buffer[j+2],buffer[j+3],buffer[j+4],buffer[j+5]);
452 break;
453 case 50:
454 printf("\tRequested IP address: %d.%d.%d.%d\n",
455 buffer[j+2],buffer[j+3],buffer[j+4],buffer[j+5]);
456 break;
457 case 53:
458 printf("\tDHCP message type: %d (%s)\n",
459 buffer[j+2],dhcp_message_types[buffer[j+2]]);
460 break;
461 case 54:
462 memcpy(serveridentifier,buffer+j+2,4);
463 printf("\tServer identifier: %d.%d.%d.%d\n",
464 serveridentifier[0],serveridentifier[1],
465 serveridentifier[2],serveridentifier[3]);
466 break;
467 case 61:
468 printf("\tClient identifier: %02x%02x%02x%02x%02x%02x\n",
469 buffer[j+2],buffer[j+3],buffer[j+4],
470 buffer[j+5],buffer[j+6],buffer[j+7]);
471 break;
472 }
473
474 /*
475 // This might go wrong if a mallformed packet is received.
476 // Maybe from a bogus server which is instructed to reply
477 // with invalid data and thus causing an exploit.
478 // My head hurts... but I think it's solved by the checking
479 // for j<size at the begin of the while-loop.
480 */
481 j+=buffer[j+1]+2;
482 }
483 }
484
485
dhcp_close(void)486 void dhcp_close(void) {
487 close(dhcp_socket);
488 }
489
490