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