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