Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1760 lines
63 KiB

  1. /*++
  2. Copyright (c) 1994 Microsoft Corporation
  3. Module Name:
  4. protocol.cxx
  5. Abstract:
  6. This module contains the server to client protocol for DHCP.
  7. Author:
  8. Manny Weiser (mannyw) 21-Oct-1992
  9. Environment:
  10. User Mode - Win32
  11. Revision History:
  12. Madan Appiah (madana) 21-Oct-1993
  13. Arthur Bierer (arthurbi) 15-July-1998
  14. hacked up to use with Wininet's auto-proxy detection code
  15. --*/
  16. #include <wininetp.h>
  17. #include "aproxp.h"
  18. #include "apdetect.h"
  19. #ifndef VXD
  20. // ping routines.. ICMP
  21. #include <ipexport.h>
  22. //#include <icmpif.h>
  23. #include <icmpapi.h>
  24. #endif
  25. #ifdef NEWNT
  26. extern BOOL DhcpGlobalIsService;
  27. #endif // NEWNT
  28. DWORD // Time in seconds
  29. DhcpCalculateWaitTime( // how much time to wait
  30. IN DWORD RoundNum, // which round is this
  31. OUT DWORD *WaitMilliSecs // if needed the # in milli seconds
  32. );
  33. POPTION
  34. FormatDhcpInform(
  35. PDHCP_CONTEXT DhcpContext
  36. );
  37. DWORD
  38. SendDhcpInform(
  39. PDHCP_CONTEXT DhcpContext,
  40. PDWORD TransactionId
  41. );
  42. DWORD // status
  43. SendInformAndGetReplies( // send an inform packet and collect replies
  44. IN PDHCP_CONTEXT DhcpContext, // the context to send out of
  45. IN DWORD nInformsToSend,// how many informs to send?
  46. IN DWORD MaxAcksToWait, // how many acks to wait for
  47. OUT DHCP_EXPECTED_OPTIONS *pExpectedOptions // list of things parsed out of request
  48. );
  49. VOID
  50. DhcpExtractFullOrLiteOptions( // Extract some important options alone or ALL
  51. IN PDHCP_CONTEXT DhcpContext,
  52. IN LPBYTE OptStart, // start of the options stuff
  53. IN DWORD MessageSize, // # of bytes of options
  54. IN BOOL LiteOnly, // next struc is EXPECTED_OPTIONS and not FULL_OPTIONS
  55. OUT LPVOID DhcpOptions, // this is where the options would be stored
  56. IN OUT PLIST_ENTRY RecdOptions, // if !LiteOnly this gets filled with all incoming options
  57. IN OUT DWORD *LeaseExpiry, // if !LiteOnly input expiry time, else output expiry time
  58. IN LPBYTE ClassName, // if !LiteOnly this is used to add to the option above
  59. IN DWORD ClassLen // if !LiteOnly this gives the # of bytes of classname
  60. );
  61. DWORD
  62. SendDhcpMessage(
  63. PDHCP_CONTEXT DhcpContext,
  64. DWORD MessageLength,
  65. PDWORD TransactionId
  66. );
  67. DWORD
  68. OpenDhcpSocket(
  69. PDHCP_CONTEXT DhcpContext
  70. );
  71. DWORD
  72. GetSpecifiedDhcpMessage(
  73. PDHCP_CONTEXT DhcpContext,
  74. PDWORD BufferLength,
  75. DWORD TransactionId,
  76. DWORD TimeToWait
  77. );
  78. DWORD
  79. CloseDhcpSocket(
  80. PDHCP_CONTEXT DhcpContext
  81. );
  82. //
  83. // functions
  84. //
  85. DWORD // Time in seconds
  86. DhcpCalculateWaitTime( // how much time to wait
  87. IN DWORD RoundNum, // which round is this
  88. OUT DWORD *WaitMilliSecs // if needed the # in milli seconds
  89. ) {
  90. DWORD MilliSecs;
  91. //DWORD WaitTimes[4] = { 4000, 8000, 16000, 32000 };
  92. DWORD WaitTimes[4] = { 2000, 4000, 8000, 16000 };
  93. if( WaitMilliSecs ) *WaitMilliSecs = 0;
  94. if( RoundNum >= sizeof(WaitTimes)/sizeof(WaitTimes[0]) )
  95. return 0;
  96. MilliSecs = WaitTimes[RoundNum] - 1000 + ((rand()*((DWORD) 2000))/RAND_MAX);
  97. if( WaitMilliSecs ) *WaitMilliSecs = MilliSecs;
  98. return (MilliSecs + 501)/1000;
  99. }
  100. VOID _inline
  101. ConcatOption(
  102. IN OUT LPBYTE *Buf, // input buffer to re-alloc
  103. IN OUT ULONG *BufSize, // input buffer size
  104. IN BYTE UNALIGNED *Data, // data to append
  105. IN ULONG DataSize // how many bytes to add?
  106. )
  107. {
  108. LPBYTE NewBuf;
  109. ULONG NewSize;
  110. NewSize = (*BufSize) + DataSize;
  111. NewBuf = (LPBYTE) DhcpAllocateMemory(NewSize);
  112. if( NULL == NewBuf ) { // could not alloc memory?
  113. return; // can't do much
  114. }
  115. memcpy(NewBuf, *Buf, *BufSize); // copy existing part
  116. memcpy(NewBuf + *BufSize, Data, DataSize); // copy new stuff
  117. if( NULL != *Buf ) DhcpFreeMemory(*Buf); // if we alloc'ed mem, free it now
  118. *Buf = NewBuf;
  119. *BufSize = NewSize; // fill in new values..
  120. }
  121. VOID
  122. DhcpExtractFullOrLiteOptions( // Extract some important options alone or ALL
  123. IN PDHCP_CONTEXT DhcpContext, // input context
  124. IN LPBYTE OptStart, // start of the options stuff
  125. IN DWORD MessageSize, // # of bytes of options
  126. IN BOOL LiteOnly, // next struc is EXPECTED_OPTIONS and not FULL_OPTIONS
  127. OUT LPVOID DhcpOptions, // this is where the options would be stored
  128. IN OUT PLIST_ENTRY RecdOptions, // if !LiteOnly this gets filled with all incoming options
  129. IN OUT DWORD *LeaseExpiry, // if !LiteOnly input expiry time, else output expiry time
  130. IN LPBYTE ClassName, // if !LiteOnly this is used to add to the option above
  131. IN DWORD ClassLen // if !LiteOnly this gives the # of bytes of classname
  132. ) {
  133. BYTE UNALIGNED* ThisOpt;
  134. BYTE UNALIGNED* NextOpt;
  135. BYTE UNALIGNED* EndOpt;
  136. BYTE UNALIGNED* MagicCookie;
  137. DWORD Error;
  138. DWORD Size, ThisSize, UClassSize = 0;
  139. LPBYTE UClass= NULL; // concatenation of all OPTION_USER_CLASS options
  140. PDHCP_EXPECTED_OPTIONS ExpOptions;
  141. PDHCP_FULL_OPTIONS FullOptions;
  142. BYTE ReqdCookie[] = {
  143. (BYTE)DHCP_MAGIC_COOKIE_BYTE1,
  144. (BYTE)DHCP_MAGIC_COOKIE_BYTE2,
  145. (BYTE)DHCP_MAGIC_COOKIE_BYTE3,
  146. (BYTE)DHCP_MAGIC_COOKIE_BYTE4
  147. };
  148. EndOpt = OptStart + MessageSize; // all options should be < EndOpt;
  149. ExpOptions = (PDHCP_EXPECTED_OPTIONS)DhcpOptions;
  150. FullOptions = (PDHCP_FULL_OPTIONS)DhcpOptions;
  151. RtlZeroMemory((LPBYTE)DhcpOptions, LiteOnly?sizeof(*ExpOptions):sizeof(*FullOptions));
  152. // if(!LiteOnly) InitializeListHead(RecdOptions); -- clear off this list for getting ALL options
  153. // dont clear off options... just accumulate over..
  154. MagicCookie = OptStart;
  155. if( 0 == MessageSize ) goto DropPkt; // nothing to do in this case
  156. if( 0 != memcmp(MagicCookie, ReqdCookie, sizeof(ReqdCookie)) )
  157. goto DropPkt; // oops, cant handle this packet
  158. NextOpt = &MagicCookie[sizeof(ReqdCookie)];
  159. while( NextOpt < EndOpt && OPTION_END != *NextOpt ) {
  160. if( OPTION_PAD == *NextOpt ) { // handle pads right away
  161. NextOpt++;
  162. continue;
  163. }
  164. ThisOpt = NextOpt; // take a good look at this option
  165. if( NextOpt + 2 > EndOpt ) { // goes over boundary?
  166. break;
  167. }
  168. NextOpt += 2 + (unsigned)ThisOpt[1]; // Option[1] holds the size of this option
  169. Size = ThisOpt[1];
  170. if( NextOpt > EndOpt ) { // illegal option that goes over boundary!
  171. break; // ignore the error, but dont take this option
  172. }
  173. if(!LiteOnly) do { // look for any OPTION_MSFT_CONTINUED ..
  174. if( NextOpt >= EndOpt ) break; // no more options
  175. if( OPTION_MSFT_CONTINUED != NextOpt[0] ) break;
  176. if( NextOpt + 1 + NextOpt[1] > EndOpt ) {
  177. NextOpt = NULL; // do this so that we know to quit at the end..
  178. break;
  179. }
  180. NextOpt++; // skip opt code
  181. ThisSize = NextOpt[0]; // # of bytes to shift back..
  182. memcpy(ThisOpt+2+Size, NextOpt+1,ThisSize);
  183. NextOpt += ThisSize+1;
  184. Size += ThisSize;
  185. } while(1); // keep stringing up any "continued" options..
  186. if( NULL == NextOpt ) { // err parsing OPTION_MSFT_CONTINUED ..
  187. break;
  188. }
  189. if( LiteOnly ) { // handle the small subnet of options
  190. switch( ThisOpt[0] ) { // ThisOpt[0] is OptionId, ThisOpt[1] is size
  191. case OPTION_MESSAGE_TYPE:
  192. if( ThisOpt[1] != 1 ) goto DropPkt;
  193. ExpOptions->MessageType = &ThisOpt[2];
  194. continue;
  195. case OPTION_SUBNET_MASK:
  196. if( ThisOpt[1] != sizeof(DWORD) ) goto DropPkt;
  197. ExpOptions->SubnetMask = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
  198. continue;
  199. case OPTION_LEASE_TIME:
  200. if( ThisOpt[1] != sizeof(DWORD) ) goto DropPkt;
  201. ExpOptions->LeaseTime = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
  202. continue;
  203. case OPTION_SERVER_IDENTIFIER:
  204. if( ThisOpt[1] != sizeof(DWORD) ) goto DropPkt;
  205. ExpOptions->ServerIdentifier = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
  206. continue;
  207. case OPTION_DOMAIN_NAME:
  208. if( ThisOpt[1] == 0 ) goto DropPkt;
  209. ExpOptions->DomainName = (BYTE UNALIGNED *)&ThisOpt[2];
  210. ExpOptions->DomainNameSize = ThisOpt[1];
  211. break;
  212. case OPTION_WPAD_URL:
  213. if( ThisOpt[1] == 0 ) goto DropPkt;
  214. ExpOptions->WpadUrl = (BYTE UNALIGNED *)&ThisOpt[2];
  215. ExpOptions->WpadUrlSize = ThisOpt[1];
  216. break;
  217. default:
  218. continue;
  219. }
  220. } else { // Handle the full set of options
  221. switch( ThisOpt[0] ) {
  222. case OPTION_MESSAGE_TYPE:
  223. if( Size != 1 ) goto DropPkt;
  224. FullOptions->MessageType = &ThisOpt[2];
  225. break;
  226. case OPTION_SUBNET_MASK:
  227. if( Size != sizeof(DWORD) ) goto DropPkt;
  228. FullOptions->SubnetMask = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
  229. break;
  230. case OPTION_LEASE_TIME:
  231. if( Size != sizeof(DWORD) ) goto DropPkt;
  232. FullOptions->LeaseTime = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
  233. break;
  234. case OPTION_SERVER_IDENTIFIER:
  235. if( Size != sizeof(DWORD) ) goto DropPkt;
  236. FullOptions->ServerIdentifier = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
  237. break;
  238. case OPTION_RENEWAL_TIME: // T1Time
  239. if( Size != sizeof(DWORD) ) goto DropPkt;
  240. FullOptions->T1Time = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
  241. break;
  242. case OPTION_REBIND_TIME: // T2Time
  243. if( Size != sizeof(DWORD) ) goto DropPkt;
  244. FullOptions->T2Time = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
  245. break;
  246. case OPTION_ROUTER_ADDRESS:
  247. if( Size < sizeof(DWORD) || (Size % sizeof(DWORD) ) )
  248. goto DropPkt; // There can be many router addresses
  249. FullOptions->GatewayAddresses = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
  250. FullOptions->nGateways = Size / sizeof(DWORD);
  251. break;
  252. case OPTION_STATIC_ROUTES:
  253. if( Size < 2*sizeof(DWORD) || (Size % (2*sizeof(DWORD))) )
  254. goto DropPkt; // the static routes come in pairs
  255. FullOptions->StaticRouteAddresses = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
  256. FullOptions->nStaticRoutes = Size/(2*sizeof(DWORD));
  257. break;
  258. case OPTION_DYNDNS_BOTH:
  259. if( Size < 3 ) goto DropPkt;
  260. FullOptions->DnsFlags = (BYTE UNALIGNED *)&ThisOpt[2];
  261. FullOptions->DnsRcode1 = (BYTE UNALIGNED *)&ThisOpt[3];
  262. FullOptions->DnsRcode2 = (BYTE UNALIGNED *)&ThisOpt[3];
  263. break;
  264. case OPTION_DOMAIN_NAME:
  265. if( Size == 0 ) goto DropPkt;
  266. FullOptions->DomainName = (BYTE UNALIGNED *)&ThisOpt[2];
  267. FullOptions->DomainNameSize = Size;
  268. break;
  269. case OPTION_WPAD_URL:
  270. if( Size == 0 ) goto DropPkt;
  271. FullOptions->WpadUrl = (BYTE UNALIGNED *)&ThisOpt[2];
  272. FullOptions->WpadUrlSize = Size;
  273. break;
  274. case OPTION_DOMAIN_NAME_SERVERS:
  275. if( Size < sizeof(DWORD) || (Size % sizeof(DWORD) ))
  276. goto DropPkt;
  277. FullOptions->DnsServerList = (DHCP_IP_ADDRESS UNALIGNED *)&ThisOpt[2];
  278. FullOptions->nDnsServers = Size / sizeof(DWORD);
  279. break;
  280. case OPTION_MESSAGE:
  281. if( Size == 0 ) break; // ignore zero sized packets
  282. FullOptions->ServerMessage = &ThisOpt[2];
  283. FullOptions->ServerMessageLength = ThisOpt[1];
  284. break;
  285. case OPTION_MCAST_LEASE_START:
  286. if ( Size != sizeof(DATE_TIME) ) goto DropPkt;
  287. FullOptions->MCastLeaseStartTime = (DWORD UNALIGNED *)&ThisOpt[2];
  288. break;
  289. case OPTION_MCAST_TTL:
  290. if ( Size != 1 ) goto DropPkt;
  291. FullOptions->MCastTTL = (BYTE UNALIGNED *)&ThisOpt[2];
  292. break;
  293. case OPTION_USER_CLASS:
  294. if( Size <= 6) goto DropPkt;
  295. ConcatOption(&UClass, &UClassSize, &ThisOpt[2], Size);
  296. continue; // don't add this option yet...
  297. default:
  298. // unknowm message, nothing to do.. especially dont log this
  299. break;
  300. }
  301. } // if LiteOnly then else
  302. } // while NextOpt < EndOpt
  303. if( LiteOnly && LeaseExpiry ) { // If asked to calculate lease expiration time..
  304. DWORD LeaseTime;
  305. time_t TimeNow, ExpirationTime;
  306. // BBUGBUGBUG [arthurbi] broken intensionlly, dead code.
  307. //if( ExpOptions->LeaseTime ) LeaseTime = _I_ntohl(*ExpOptions->LeaseTime);
  308. if( ExpOptions->LeaseTime ) LeaseTime = 0;
  309. else LeaseTime = DHCP_MINIMUM_LEASE;
  310. ExpirationTime = (TimeNow = time(NULL)) + (time_t)LeaseTime;
  311. if( ExpirationTime < TimeNow ) {
  312. ExpirationTime = INFINIT_TIME;
  313. }
  314. *LeaseExpiry = (DWORD)ExpirationTime ;
  315. }
  316. if( !LiteOnly && NULL != UClass ) { // we have a user class list to pass on..
  317. DhcpAssert(UClassSize != 0 ); // we better have something here..
  318. DhcpFreeMemory(UClass); UClass = NULL;
  319. }
  320. return;
  321. DropPkt:
  322. RtlZeroMemory(DhcpOptions, LiteOnly?sizeof(ExpOptions):sizeof(FullOptions));
  323. if( LiteOnly && LeaseExpiry ) *LeaseExpiry = (DWORD) time(NULL) + DHCP_MINIMUM_LEASE;
  324. //if(!LiteOnly) DhcpFreeAllOptions(RecdOptions);// ok undo the options that we just added
  325. if(!LiteOnly && NULL != UClass ) DhcpFreeMemory(UClass);
  326. }
  327. POPTION // ptr to add additional options
  328. FormatDhcpInform( // format the packet for an INFORM
  329. IN PDHCP_CONTEXT DhcpContext // format for this context
  330. ) {
  331. LPOPTION option;
  332. LPBYTE OptionEnd;
  333. BYTE value;
  334. PDHCP_MESSAGE dhcpMessage;
  335. dhcpMessage = DhcpContext->MessageBuffer;
  336. RtlZeroMemory( dhcpMessage, DHCP_SEND_MESSAGE_SIZE );
  337. //
  338. // BUGBUG [arthurbi] -
  339. // For RAS client, use broadcast bit, otherwise the router will try
  340. // to send as unicast to made-up RAS client hardware address, which
  341. // will not work. So will this work without it?
  342. //
  343. //
  344. // Transaction ID is filled in during send
  345. //
  346. dhcpMessage->Operation = BOOT_REQUEST;
  347. dhcpMessage->HardwareAddressType = DhcpContext->HardwareAddressType;
  348. dhcpMessage->SecondsSinceBoot = (WORD) DhcpContext->SecondsSinceBoot;
  349. memcpy(dhcpMessage->HardwareAddress,DhcpContext->HardwareAddress,DhcpContext->HardwareAddressLength);
  350. dhcpMessage->HardwareAddressLength = (BYTE)DhcpContext->HardwareAddressLength;
  351. dhcpMessage->ClientIpAddress = DhcpContext->IpAddress;
  352. //dhcpMessage->Reserved = 0;
  353. //dhcpMessage->Reserved = _I_htons(DHCP_BROADCAST);
  354. //if ( IS_MDHCP_CTX(DhcpContext ) ) MDHCP_MESSAGE( dhcpMessage );
  355. option = &dhcpMessage->Option;
  356. OptionEnd = (LPBYTE)dhcpMessage + DHCP_SEND_MESSAGE_SIZE;
  357. //
  358. // always add magic cookie first
  359. //
  360. option = (LPOPTION) DhcpAppendMagicCookie( (LPBYTE) option, OptionEnd );
  361. value = DHCP_INFORM_MESSAGE;
  362. option = DhcpAppendOption(
  363. option,
  364. OPTION_MESSAGE_TYPE,
  365. &value,
  366. 1,
  367. OptionEnd
  368. );
  369. //
  370. // BUGBUG [arthurbi], shouldn't we uncomment this?
  371. //
  372. // un comment later on
  373. /*option = DhcpAppendClassIdOption(
  374. DhcpContext,
  375. (LPBYTE)option,
  376. OptionEnd
  377. );*/
  378. return( option );
  379. }
  380. DWORD // status
  381. SendDhcpInform( // send an inform packet after filling required options
  382. IN PDHCP_CONTEXT DhcpContext, // sned out for this context
  383. IN OUT DWORD *pdwXid // use this Xid (if zero fill something and return it)
  384. ) {
  385. DWORD size;
  386. DWORD Error;
  387. POPTION option;
  388. LPBYTE OptionEnd;
  389. BYTE SentOpt[OPTION_END+1];
  390. BYTE SentVOpt[OPTION_END+1];
  391. BYTE VendorOpt[OPTION_END+1];
  392. DWORD VendorOptSize;
  393. RtlZeroMemory(SentOpt, sizeof(SentOpt)); // initialize boolean arrays
  394. RtlZeroMemory(SentVOpt, sizeof(SentVOpt)); // so that no option is presumed sent
  395. VendorOptSize = 0; // encapsulated vendor option is empty
  396. option = FormatDhcpInform( DhcpContext ); // core format
  397. OptionEnd = (LPBYTE)(DhcpContext->MessageBuffer) + DHCP_SEND_MESSAGE_SIZE;
  398. if( DhcpContext->ClientIdentifier.fSpecified) // client id specified in registy
  399. option = DhcpAppendClientIDOption( // ==> use this client id as option
  400. option,
  401. DhcpContext->ClientIdentifier.bType,
  402. DhcpContext->ClientIdentifier.pbID,
  403. (BYTE)DhcpContext->ClientIdentifier.cbID,
  404. OptionEnd
  405. );
  406. else // client id was not specified
  407. option = DhcpAppendClientIDOption( // ==> use hw addr as client id
  408. option,
  409. DhcpContext->HardwareAddressType,
  410. DhcpContext->HardwareAddress,
  411. (BYTE)DhcpContext->HardwareAddressLength,
  412. OptionEnd
  413. );
  414. { // add hostname and comment options
  415. char szHostName[255];
  416. if ( _I_gethostname(szHostName, ARRAY_ELEMENTS(szHostName)) != SOCKET_ERROR )
  417. {
  418. option = DhcpAppendOption(
  419. option,
  420. OPTION_HOST_NAME,
  421. (LPBYTE)szHostName,
  422. (BYTE)((strlen(szHostName) + 1) * sizeof(CHAR)),
  423. OptionEnd
  424. );
  425. }
  426. }
  427. if( NULL != DhcpGlobalClientClassInfo ) { // if we have any info on client class..
  428. option = DhcpAppendOption(
  429. option,
  430. OPTION_CLIENT_CLASS_INFO,
  431. (LPBYTE)DhcpGlobalClientClassInfo,
  432. strlen(DhcpGlobalClientClassInfo),
  433. OptionEnd
  434. );
  435. }
  436. SentOpt[OPTION_MESSAGE_TYPE] = TRUE; // these must have been added by now
  437. if(DhcpContext->ClassIdLength) SentOpt[OPTION_USER_CLASS] = TRUE;
  438. SentOpt[OPTION_CLIENT_CLASS_INFO] = TRUE;
  439. SentOpt[OPTION_CLIENT_ID] = TRUE;
  440. SentOpt[OPTION_REQUESTED_ADDRESS] = TRUE;
  441. SentOpt[OPTION_HOST_NAME] = TRUE;
  442. option = DhcpAppendSendOptions( // append all other options we need to send
  443. DhcpContext, // for this context
  444. &DhcpContext->SendOptionsList, // this is the list of options to send out
  445. DhcpContext->ClassId, // which class.
  446. DhcpContext->ClassIdLength, // how many bytes are there in the class id
  447. (LPBYTE)option, // start of the buffer to add the options
  448. (LPBYTE)OptionEnd, // end of the buffer up to which we can add options
  449. SentOpt, // this is the boolean array that marks what opt were sent
  450. SentVOpt, // this is for vendor spec options
  451. VendorOpt, // this would contain some vendor specific options
  452. &VendorOptSize // the # of bytes of vendor options added to VendorOpt param
  453. );
  454. if( !SentOpt[OPTION_VENDOR_SPEC_INFO] && VendorOptSize && VendorOptSize <= OPTION_END )
  455. option = DhcpAppendOption( // add vendor specific options if we havent already sent it
  456. option,
  457. OPTION_VENDOR_SPEC_INFO,
  458. VendorOpt,
  459. (BYTE)VendorOptSize,
  460. OptionEnd
  461. );
  462. option = DhcpAppendOption( option, OPTION_END, NULL, 0, OptionEnd );
  463. size = (DWORD)((PBYTE)option - (PBYTE)DhcpContext->MessageBuffer);
  464. return SendDhcpMessage( // finally send the message and return
  465. DhcpContext,
  466. size,
  467. pdwXid
  468. );
  469. }
  470. DWORD
  471. InitializeDhcpSocket(
  472. SOCKET *Socket,
  473. DHCP_IP_ADDRESS IpAddress
  474. )
  475. /*++
  476. Routine Description:
  477. This function initializes and binds a socket to the specified IP address.
  478. Arguments:
  479. Socket - Returns a pointer to the initialized socket.
  480. IpAddress - The IP address to bind the socket to. It is legitimate
  481. to bind a socket to 0.0.0.0 if the card has no current IP address.
  482. Return Value:
  483. The status of the operation.
  484. --*/
  485. {
  486. DWORD error;
  487. DWORD closeError;
  488. DWORD value;
  489. struct sockaddr_in socketName;
  490. DWORD i;
  491. SOCKET sock;
  492. //
  493. // Sockets initialization
  494. //
  495. sock = _I_socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP );
  496. if ( sock == INVALID_SOCKET ) {
  497. error = _I_WSAGetLastError();
  498. DhcpPrint(("socket failed, error = %ld\n", error ));
  499. return( error );
  500. }
  501. //
  502. // Make the socket share-able
  503. //
  504. value = 1;
  505. error = _I_setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char FAR *)&value, sizeof(value) );
  506. if ( error != 0 ) {
  507. error = _I_WSAGetLastError();
  508. DhcpPrint(("setsockopt failed, err = %ld\n", error ));
  509. closeError = _I_closesocket( sock );
  510. if ( closeError != 0 ) {
  511. DhcpPrint(("closesocket failed, err = %d\n", closeError ));
  512. }
  513. return( error );
  514. }
  515. error = _I_setsockopt( sock, SOL_SOCKET, SO_BROADCAST, (char FAR *)&value, sizeof(value) );
  516. if ( error != 0 ) {
  517. error = _I_WSAGetLastError();
  518. DhcpPrint(("setsockopt failed, err = %ld\n", error ));
  519. closeError = _I_closesocket( sock );
  520. if ( closeError != 0 ) {
  521. DhcpPrint(("closesocket failed, err = %d\n", closeError ));
  522. }
  523. return( error );
  524. }
  525. //
  526. // If the IpAddress is zero, set the special socket option to make
  527. // stack work with zero address.
  528. //
  529. if( IpAddress == 0 ) {
  530. value = 1234;
  531. error = _I_setsockopt( sock, SOL_SOCKET, 0x8000, (char FAR *)&value, sizeof(value) );
  532. if ( error != 0 ) {
  533. error = _I_WSAGetLastError();
  534. DhcpPrint(("setsockopt failed, err = %ld\n", error ));
  535. closeError = _I_closesocket( sock );
  536. if ( closeError != 0 ) {
  537. DhcpPrint(("closesocket failed, err = %d\n", closeError ));
  538. }
  539. return( error );
  540. }
  541. }
  542. socketName.sin_family = PF_INET;
  543. socketName.sin_port = _I_htons( DHCP_CLIENT_PORT );
  544. socketName.sin_addr.s_addr = IpAddress;
  545. for ( i = 0; i < 8 ; i++ ) {
  546. socketName.sin_zero[i] = 0;
  547. }
  548. //
  549. // Bind this socket to the DHCP server port
  550. //
  551. error = _I_bind(
  552. sock,
  553. (struct sockaddr FAR *)&socketName,
  554. sizeof( socketName )
  555. );
  556. if ( error != 0 ) {
  557. error = _I_WSAGetLastError();
  558. DhcpPrint(("bind failed (address 0x%lx), err = %ld\n", IpAddress, error ));
  559. closeError = _I_closesocket( sock );
  560. if ( closeError != 0 ) {
  561. DhcpPrint(("closesocket failed, err = %d\n", closeError ));
  562. }
  563. return( error );
  564. }
  565. *Socket = sock;
  566. return( NO_ERROR );
  567. }
  568. DWORD // status
  569. SendInformAndGetReplies( // send an inform packet and collect replies
  570. IN PDHCP_CONTEXT DhcpContext, // the context to send out of
  571. IN DWORD nInformsToSend,// how many informs to send?
  572. IN DWORD MaxAcksToWait, // how many acks to wait for
  573. OUT DHCP_EXPECTED_OPTIONS *pExpectedOptions // list of things parsed out of request
  574. ) {
  575. time_t StartTime;
  576. time_t TimeNow;
  577. DWORD TimeToWait;
  578. DWORD Error;
  579. DWORD Xid;
  580. DWORD MessageSize;
  581. DWORD RoundNum;
  582. DWORD MessageCount;
  583. DWORD LeaseExpirationTime;
  584. DHCP_FULL_OPTIONS FullOptions;
  585. DhcpPrint(("SendInformAndGetReplies entered\n"));
  586. if((Error = OpenDhcpSocket(DhcpContext)) != ERROR_SUCCESS) {
  587. DhcpPrint(("Could not open socket for this interface! (%ld)\n", Error));
  588. return Error;
  589. }
  590. Xid = 0; // Will be generated by first SendDhcpPacket
  591. MessageCount = 0; // total # of messages we have got
  592. DhcpContext->SecondsSinceBoot = 0; // start at zero..
  593. for( RoundNum = 0; RoundNum < nInformsToSend; RoundNum ++ ) {
  594. Error = SendDhcpInform(DhcpContext, &Xid);
  595. if( ERROR_SUCCESS != Error ) {
  596. DhcpPrint(("SendDhcpInform: %ld\n", Error));
  597. goto Cleanup;
  598. } else {
  599. DhcpPrint(("Sent DhcpInform\n"));
  600. }
  601. TimeToWait = DhcpCalculateWaitTime(RoundNum, NULL);
  602. DhcpContext->SecondsSinceBoot += TimeToWait; // do this so that next time thru it can go thru relays..
  603. StartTime = time(NULL);
  604. while ( TRUE ) { // wiat for the specified wait time
  605. MessageSize = DHCP_MESSAGE_SIZE;
  606. DhcpPrint(("Waiting for ACK[Xid=%x]: %ld seconds\n",Xid, TimeToWait));
  607. Error = GetSpecifiedDhcpMessage( // try to receive an ACK
  608. DhcpContext,
  609. &MessageSize,
  610. Xid,
  611. (DWORD)TimeToWait
  612. );
  613. if ( Error == ERROR_SEM_TIMEOUT ) break;
  614. if( Error != ERROR_SUCCESS ) {
  615. DhcpPrint(("GetSpecifiedDhcpMessage: %ld\n", Error));
  616. goto Cleanup;
  617. }
  618. DhcpExtractFullOrLiteOptions( // Need to see if this is an ACK
  619. DhcpContext,
  620. (LPBYTE)&DhcpContext->MessageBuffer->Option,
  621. MessageSize - DHCP_MESSAGE_FIXED_PART_SIZE,
  622. TRUE, // do lite extract only
  623. pExpectedOptions, // check for only expected options
  624. NULL, // unused
  625. &LeaseExpirationTime,
  626. NULL, // unused
  627. 0 // unused
  628. );
  629. if( NULL == pExpectedOptions->MessageType ) {
  630. DhcpPrint(("Received no message type!\n"));
  631. } else if( DHCP_ACK_MESSAGE != *(pExpectedOptions->MessageType) ) {
  632. DhcpPrint(("Received unexpected message type: %ld\n", *(pExpectedOptions->MessageType)));
  633. } else if( NULL == pExpectedOptions->ServerIdentifier ) {
  634. DhcpPrint(("Received no server identifier, dropping inform ACK\n"));
  635. } else {
  636. MessageCount ++;
  637. DhcpPrint(("Received %ld ACKS so far\n", MessageCount));
  638. DhcpExtractFullOrLiteOptions( // do FULL options..
  639. DhcpContext,
  640. (LPBYTE)&DhcpContext->MessageBuffer->Option,
  641. MessageSize - DHCP_MESSAGE_FIXED_PART_SIZE,
  642. FALSE,
  643. &FullOptions,
  644. &(DhcpContext->RecdOptionsList),
  645. &LeaseExpirationTime,
  646. DhcpContext->ClassId,
  647. DhcpContext->ClassIdLength
  648. );
  649. if( MessageCount >= MaxAcksToWait ) goto Cleanup;
  650. } // if( it is an ACK and ServerId present )
  651. TimeNow = time(NULL); // Reset the time values to reflect new time
  652. if( TimeToWait < (DWORD) (TimeNow - StartTime) ) {
  653. break; // no more time left to wait..
  654. }
  655. TimeToWait -= (DWORD)(TimeNow - StartTime); // recalculate time now
  656. StartTime = TimeNow; // reset start time also
  657. } // end of while ( TimeToWait > 0)
  658. } // for (RoundNum = 0; RoundNum < nInformsToSend ; RoundNum ++ )
  659. Cleanup:
  660. CloseDhcpSocket(DhcpContext);
  661. if( MessageCount ) Error = ERROR_SUCCESS;
  662. DhcpPrint(("SendInformAndGetReplies: got %d ACKS (returning %ld)\n", MessageCount,Error));
  663. return Error;
  664. }
  665. //--------------------------------------------------------------------------------
  666. // This function gets the options from the server using DHCP_INFORM message.
  667. // It picks the first ACK and then processes it.
  668. // It ignores any errors caused by TIME_OUTS as that only means there is no
  669. // server, or the server does not have this functionality. No point giving up
  670. // because of that.
  671. //--------------------------------------------------------------------------------
  672. BOOL // win32 status
  673. DhcpDoInform( // send an inform packet if necessary
  674. IN CAdapterInterface * pAdapterInterface,
  675. IN BOOL fBroadcast, // Do we broadcast this inform, or unicast to server?
  676. OUT LPSTR lpszAutoProxyUrl,
  677. IN DWORD dwAutoProxyUrlLength
  678. ) {
  679. DHCP_CONTEXT StackDhcpContext; // input context to do inform on
  680. PDHCP_CONTEXT DhcpContext = &StackDhcpContext;
  681. DWORD Error;
  682. DWORD LocalError;
  683. BOOL WasPlumbedBefore;
  684. time_t OldT2Time;
  685. DHCP_EXPECTED_OPTIONS ExpectedOptions;
  686. *lpszAutoProxyUrl = '\0';
  687. if ( ! pAdapterInterface->IsDhcp() ) {
  688. return FALSE;
  689. }
  690. if (! pAdapterInterface->CopyAdapterInfoToDhcpContext(DhcpContext) ) {
  691. return FALSE;
  692. }
  693. // mdhcp uses INADDR_ANY so it does not have to have an ipaddress.
  694. if( 0 == DhcpContext->IpAddress && !IS_MDHCP_CTX( DhcpContext) ) {
  695. DhcpPrint(("Cannot do DhcpInform on an adapter without ip address!\n"));
  696. return FALSE;
  697. }
  698. // Open the socket ahead... so that things work. Tricky, else does not work!!!
  699. if((Error = OpenDhcpSocket(DhcpContext)) != ERROR_SUCCESS ) {
  700. DhcpPrint(("Could not open socket (%ld)\n", Error));
  701. return FALSE;
  702. }
  703. // If you always need to broadcast this message, the KLUDGE is to
  704. // set pContext->T2Time = 0; and pContext->fFlags &= ~DHCP_CONTEXT_FLAGS_PLUMBED
  705. // and that should do the trick! Safe to change the struct as it was cloned.
  706. OldT2Time = DhcpContext->T2Time;
  707. WasPlumbedBefore = IS_ADDRESS_PLUMBED(DhcpContext);
  708. if(fBroadcast) {
  709. DhcpContext->T2Time = 0; // !!!! KLUDGE.. look at SendDhcpMessage to understand this ..
  710. ADDRESS_UNPLUMBED(DhcpContext);
  711. CONNECTION_BROADCAST(DhcpContext);
  712. } else {
  713. DhcpContext->T2Time = (-1);
  714. }
  715. memset((void *) &ExpectedOptions, 0, sizeof(DHCP_EXPECTED_OPTIONS));
  716. Error = SendInformAndGetReplies( // get replies on this
  717. DhcpContext, // context to send on
  718. 2, // send atmost 2 informs
  719. 1, // wait for as many as 4 packets..
  720. &ExpectedOptions
  721. );
  722. DhcpContext->LastInformSent = time(NULL); // record when the last inform was sent
  723. DhcpContext->T2Time = OldT2Time;
  724. if( WasPlumbedBefore ) ADDRESS_PLUMBED(DhcpContext);
  725. LocalError = CloseDhcpSocket(DhcpContext);
  726. DhcpAssert(ERROR_SUCCESS == LocalError);
  727. if( ERROR_SUCCESS != Error ) {
  728. DhcpPrint(("DhcpDoInform:return [0x%lx]\n", Error));
  729. }
  730. else
  731. {
  732. //
  733. // Did we actually get a response with an URL that can be used ?
  734. //
  735. if ( ExpectedOptions.WpadUrl &&
  736. ExpectedOptions.WpadUrlSize > 0 &&
  737. dwAutoProxyUrlLength > ExpectedOptions.WpadUrlSize )
  738. {
  739. memcpy(lpszAutoProxyUrl, ExpectedOptions.WpadUrl, ExpectedOptions.WpadUrlSize );
  740. return TRUE;
  741. }
  742. }
  743. return FALSE;
  744. }
  745. DWORD
  746. SendDhcpMessage(
  747. PDHCP_CONTEXT DhcpContext,
  748. DWORD MessageLength,
  749. PDWORD TransactionId
  750. )
  751. /*++
  752. Routine Description:
  753. This function sends a UDP message to the DHCP server specified
  754. in the DhcpContext.
  755. Arguments:
  756. DhcpContext - A pointer to a DHCP context block.
  757. MessageLength - The length of the message to send.
  758. TransactionID - The transaction ID for this message. If 0, the
  759. function generates a random ID, and returns it.
  760. Return Value:
  761. The status of the operation.
  762. --*/
  763. {
  764. DWORD error;
  765. int i;
  766. struct sockaddr_in socketName;
  767. time_t TimeNow;
  768. BOOL LockedInterface = FALSE;
  769. if ( *TransactionId == 0 ) {
  770. *TransactionId = (rand() << 16) + rand();
  771. }
  772. DhcpContext->MessageBuffer->TransactionID = *TransactionId;
  773. //
  774. // Initialize the outgoing address.
  775. //
  776. socketName.sin_family = PF_INET;
  777. socketName.sin_port = _I_htons( DHCP_SERVR_PORT );
  778. if ( IS_MDHCP_CTX(DhcpContext) ) {
  779. socketName.sin_addr.s_addr = DhcpContext->DhcpServerAddress;
  780. if ( CLASSD_NET_ADDR( DhcpContext->DhcpServerAddress ) ) {
  781. int TTL = 16;
  782. //
  783. // Set TTL
  784. // MBUG: we need to read this from the registry.
  785. //
  786. if (_I_setsockopt(
  787. DhcpContext->Socket,
  788. IPPROTO_IP,
  789. IP_MULTICAST_TTL,
  790. (char *)&TTL,
  791. sizeof((int)TTL)) == SOCKET_ERROR){
  792. error = _I_WSAGetLastError();
  793. DhcpPrint(("could not set MCast TTL %ld\n",error ));
  794. return error;
  795. }
  796. }
  797. } else if( IS_ADDRESS_PLUMBED(DhcpContext) &&
  798. !IS_MEDIA_RECONNECTED(DhcpContext) && // media reconnect - braodcast
  799. !IS_POWER_RESUMED(DhcpContext) ) { // power resumed - broadcast
  800. //
  801. // If we are past T2, use the broadcast address; otherwise,
  802. // direct this to the server.
  803. //
  804. TimeNow = time( NULL );
  805. // BUGBUG why did we broadcast here before ?
  806. if ( TimeNow > DhcpContext->T2Time && IS_CONNECTION_BROADCAST(DhcpContext)) {
  807. socketName.sin_addr.s_addr = (DHCP_IP_ADDRESS)(INADDR_BROADCAST);
  808. } else {
  809. socketName.sin_addr.s_addr = DhcpContext->DhcpServerAddress;
  810. }
  811. }
  812. else {
  813. socketName.sin_addr.s_addr = (DHCP_IP_ADDRESS)(INADDR_BROADCAST);
  814. INET_ASSERT(FALSE);
  815. }
  816. for ( i = 0; i < 8 ; i++ ) {
  817. socketName.sin_zero[i] = 0;
  818. }
  819. if( socketName.sin_addr.s_addr ==
  820. (DHCP_IP_ADDRESS)(INADDR_BROADCAST) ) {
  821. DWORD Error = ERROR_SUCCESS;
  822. DWORD InterfaceId;
  823. //
  824. // BUGBUG TODO [arthurbi] This code below is needed for
  825. // Broadcasts to work. We need to make some fancy driver
  826. // calls to work...
  827. //
  828. //
  829. // if we broadcast a message, inform IP stack - the adapter we
  830. // like to send this broadcast on, otherwise it will pick up the
  831. // first uninitialized adapter.
  832. //
  833. // InterfaceId = DhcpContext->IpInterfaceContext;
  834. //
  835. // if( !IPSetInterface( InterfaceId ) ) {
  836. // // DhcpAssert( FALSE );
  837. // Error = ERROR_GEN_FAILURE;
  838. // }
  839. // InterfaceId = ((PLOCAL_CONTEXT_INFO)
  840. // DhcpContext->LocalInformation)->IpInterfaceContext;
  841. //
  842. // LOCK_INTERFACE();
  843. // LockedInterface = TRUE;
  844. // Error = IPSetInterface( InterfaceId );
  845. // DhcpAssert( Error == ERROR_SUCCESS );
  846. if( ERROR_SUCCESS != Error ) {
  847. DhcpPrint(("IPSetInterface failed with %lx error\n", Error));
  848. UNLOCK_INTERFACE();
  849. return Error;
  850. }
  851. }
  852. //
  853. // send minimum DHCP_MIN_SEND_RECV_PK_SIZE (300) bytes, otherwise
  854. // bootp relay agents don't like the packet.
  855. //
  856. MessageLength = (DWORD)((MessageLength > DHCP_MIN_SEND_RECV_PK_SIZE) ?
  857. MessageLength : DHCP_MIN_SEND_RECV_PK_SIZE);
  858. error = _I_sendto(
  859. DhcpContext->Socket,
  860. (PCHAR)DhcpContext->MessageBuffer,
  861. MessageLength,
  862. 0,
  863. (struct sockaddr *)&socketName,
  864. sizeof( struct sockaddr )
  865. );
  866. #ifndef VXD
  867. if( LockedInterface ) { UNLOCK_INTERFACE(); }
  868. #endif VXD
  869. if ( error == SOCKET_ERROR ) {
  870. error = _I_WSAGetLastError();
  871. DhcpPrint(("Send failed, error = %ld\n", error ));
  872. } else {
  873. IF_DEBUG( PROTOCOL ) {
  874. DhcpPrint(("Sent message to %s: \n", _I_inet_ntoa( socketName.sin_addr )));
  875. }
  876. DhcpDumpMessage( DEBUG_PROTOCOL_DUMP, DhcpContext->MessageBuffer );
  877. error = NO_ERROR;
  878. }
  879. return( error );
  880. }
  881. DWORD
  882. OpenDhcpSocket(
  883. PDHCP_CONTEXT DhcpContext
  884. )
  885. {
  886. DWORD Error;
  887. PLOCAL_CONTEXT_INFO localInfo;
  888. if ( DhcpContext->Socket != INVALID_SOCKET ) {
  889. return ( ERROR_SUCCESS );
  890. }
  891. //
  892. // create a socket for the dhcp protocol. it's important to bind the
  893. // socket to the correct ip address. There are currently three cases:
  894. //
  895. // 1. If the interface has been autoconfigured, it already has an address,
  896. // say, IP1. If the client receives a unicast offer from a dhcp server
  897. // the offer will be addressed to IP2, which is the client's new dhcp
  898. // address. If we bind the dhcp socket to IP1, the client won't be able
  899. // to receive unicast responses. So, we bind the socket to 0.0.0.0.
  900. // This will allow the socket to receive a unicast datagram addressed to
  901. // any address.
  902. //
  903. // 2. If the interface in not plumbed (i.e. doesn't have an address) bind
  904. // the socket to 0.0.0.0
  905. //
  906. // 3. If the interface has been plumbed has in *not* autoconfigured, then
  907. // bind to the current address.
  908. Error = InitializeDhcpSocket(
  909. &DhcpContext->Socket,
  910. DhcpContext->IpAddress
  911. );
  912. if( Error != ERROR_SUCCESS ) {
  913. DhcpContext->Socket = INVALID_SOCKET;
  914. DhcpPrint((" Socket Open failed, %ld\n", Error ));
  915. }
  916. return(Error);
  917. }
  918. DWORD
  919. CloseDhcpSocket(
  920. PDHCP_CONTEXT DhcpContext
  921. )
  922. {
  923. DWORD Error = ERROR_SUCCESS;
  924. if( DhcpContext->Socket != INVALID_SOCKET ) {
  925. BOOL Bool;
  926. Error = _I_closesocket( DhcpContext->Socket );
  927. if( Error != ERROR_SUCCESS ) {
  928. DhcpPrint((" Socket close failed, %ld\n", Error ));
  929. }
  930. DhcpContext->Socket = INVALID_SOCKET;
  931. //
  932. // Reset the IP stack to send DHCP broadcast to first
  933. // uninitialized stack.
  934. //
  935. //Bool = IPResetInterface();
  936. //DhcpAssert( Bool == TRUE );
  937. }
  938. return( Error );
  939. }
  940. typedef struct /* anonymous */ { // structure to hold waiting recvfroms
  941. LIST_ENTRY RecvList; // other elements in this list
  942. PDHCP_CONTEXT Ctxt; // which context is this wait for?
  943. DWORD InBufLen; // what was the buffer size to recv in?
  944. PDWORD BufLen; // how many bytes did we recvd?
  945. DWORD Xid; // what xid is this wait for?
  946. time_t ExpTime; // wait until what time?
  947. HANDLE WaitEvent; // event for waiting on..
  948. BOOL Recd; // was a packet received..?
  949. } RECV_CTXT, *PRECV_CTXT; // ctxt used to recv on..
  950. VOID
  951. InsertInPriorityList( // insert in priority list according to Secs
  952. IN OUT PRECV_CTXT Ctxt, // Secs field changed to hold offset
  953. IN PLIST_ENTRY List,
  954. OUT PBOOL First // adding in first location?
  955. )
  956. {
  957. PRECV_CTXT ThisCtxt;
  958. PLIST_ENTRY InitList; // "List" param at function entry
  959. if( IsListEmpty(List) ) { // no element in list? add this and quit
  960. *First = TRUE; // adding at head
  961. } else {
  962. *First = FALSE; // adding at tail..
  963. }
  964. InsertTailList( List, &Ctxt->RecvList); // insert element..
  965. //LeaveCriticalSection( &DhcpGlobalRecvFromCritSect );
  966. }
  967. DWORD
  968. TryReceive( // try to recv pkt on 0.0.0.0 socket
  969. IN SOCKET Socket, // socket to recv on
  970. IN LPBYTE Buffer, // buffer to fill
  971. OUT PDWORD BufLen, // # of bytes filled in buffer
  972. OUT PDWORD Xid, // Xid of recd pkt
  973. IN DWORD Secs // # of secs to spend waiting?
  974. )
  975. {
  976. DWORD Error;
  977. struct timeval timeout;
  978. fd_set SockSet;
  979. struct sockaddr SockName;
  980. int SockNameSize;
  981. FD_ZERO(&SockSet);
  982. FD_SET(Socket,&SockSet);
  983. SockNameSize = sizeof( SockName );
  984. timeout.tv_sec = Secs;
  985. timeout.tv_usec = 0;
  986. DhcpPrint(("Select: waiting for: %ld seconds\n", Secs));
  987. Error = _I_select( 0, &SockSet, NULL, NULL, &timeout );
  988. if( ERROR_SUCCESS == Error ) { // timed out..
  989. DhcpPrint(("Recv timed out..\n"));
  990. return ERROR_SEM_TIMEOUT;
  991. }
  992. Error = _I_recvfrom(Socket,(char *)Buffer,*BufLen, 0, &SockName, &SockNameSize);
  993. if( SOCKET_ERROR == Error ) {
  994. Error = _I_WSAGetLastError();
  995. DhcpPrint(("Recv failed 0x%lx\n",Error));
  996. } else {
  997. *BufLen = Error;
  998. Error = ERROR_SUCCESS;
  999. *Xid = ((PDHCP_MESSAGE)Buffer)->TransactionID;
  1000. DhcpPrint(("Recd msg XID: 0x%lx [Mdhcp? %s]\n", *Xid,
  1001. IS_MDHCP_MESSAGE(((PDHCP_MESSAGE)Buffer))?"yes":"no" ));
  1002. }
  1003. return Error;
  1004. }
  1005. VOID
  1006. DispatchPkt( // find out any takers for Xid
  1007. IN OUT PRECV_CTXT Ctxt, // ctxt that has buffer and buflen
  1008. IN DWORD Xid // recd Xid
  1009. )
  1010. {
  1011. do { // not a loop, just for ease of use
  1012. LPBYTE Tmp;
  1013. PLIST_ENTRY Entry;
  1014. PRECV_CTXT ThisCtxt;
  1015. Entry = DhcpGlobalRecvFromList.Flink;
  1016. while(Entry != &DhcpGlobalRecvFromList ) {
  1017. ThisCtxt = CONTAINING_RECORD(Entry, RECV_CTXT, RecvList);
  1018. Entry = Entry->Flink;
  1019. if(Xid != ThisCtxt->Xid ) continue; // mismatch.. nothing more todo
  1020. // now check for same type of message and ctxt...
  1021. if( (unsigned)IS_MDHCP_MESSAGE((Ctxt->Ctxt->MessageBuffer))
  1022. !=
  1023. IS_MDHCP_CTX( (ThisCtxt->Ctxt) )
  1024. ) {
  1025. //
  1026. // The contexts dont match.. give up
  1027. //
  1028. continue;
  1029. }
  1030. //
  1031. // check for same hardware address..
  1032. //
  1033. if( ThisCtxt->Ctxt->HardwareAddressLength != Ctxt->Ctxt->MessageBuffer->HardwareAddressLength ) {
  1034. continue;
  1035. }
  1036. if( 0 != memcmp(ThisCtxt->Ctxt->HardwareAddress,
  1037. Ctxt->Ctxt->MessageBuffer->HardwareAddress,
  1038. ThisCtxt->Ctxt->HardwareAddressLength
  1039. ) ) {
  1040. continue;
  1041. }
  1042. // matched.. switch buffers to give this guy this due..
  1043. DhcpDumpMessage(DEBUG_PROTOCOL_DUMP, (PDHCP_MESSAGE)(Ctxt->Ctxt->MessageBuffer) );
  1044. *(ThisCtxt->BufLen) = *(Ctxt->BufLen);
  1045. Tmp = (LPBYTE)(Ctxt->Ctxt)->MessageBuffer;
  1046. (Ctxt->Ctxt)->MessageBuffer = (ThisCtxt->Ctxt)->MessageBuffer;
  1047. (ThisCtxt->Ctxt)->MessageBuffer = (PDHCP_MESSAGE)Tmp;
  1048. RemoveEntryList(&ThisCtxt->RecvList);
  1049. InitializeListHead(&ThisCtxt->RecvList);
  1050. DhcpAssert(FALSE == ThisCtxt->Recd);
  1051. ThisCtxt->Recd = TRUE;
  1052. if( 0 == SetEvent(ThisCtxt->WaitEvent) ) {
  1053. DhcpAssert(FALSE);
  1054. }
  1055. break;
  1056. }
  1057. } while (FALSE);
  1058. //LeaveCriticalSection(&DhcpGlobalRecvFromCritSect);
  1059. }
  1060. DWORD
  1061. ProcessRecvFromSocket( // wait using select and process incoming pkts
  1062. IN OUT PRECV_CTXT Ctxt // ctxt to use
  1063. )
  1064. {
  1065. time_t TimeNow;
  1066. SOCKET Socket;
  1067. LPBYTE Buffer;
  1068. DWORD Xid;
  1069. DWORD Error;
  1070. PLIST_ENTRY Entry;
  1071. Socket = (Ctxt->Ctxt)->Socket;
  1072. TimeNow = time(NULL);
  1073. Error = ERROR_SEM_TIMEOUT;
  1074. while(TimeNow <= Ctxt->ExpTime ) { // while required to wait
  1075. Buffer = (LPBYTE)((Ctxt->Ctxt)->MessageBuffer);
  1076. *(Ctxt->BufLen) = Ctxt->InBufLen;
  1077. Error = TryReceive(Socket, Buffer, Ctxt->BufLen, &Xid, (DWORD)(Ctxt->ExpTime - TimeNow));
  1078. if( ERROR_SUCCESS != Error ) { // did not recv?
  1079. if( WSAECONNRESET != Error ) break; // ignore possibly spurious conn-resets..
  1080. else { TimeNow = time(NULL); continue; }
  1081. }
  1082. if( Xid == Ctxt->Xid ) break; // this was destined for this ctxt only..
  1083. DispatchPkt(Ctxt, Xid);
  1084. TimeNow = time(NULL);
  1085. }
  1086. if( TimeNow > Ctxt->ExpTime ) { // we timed out.
  1087. Error = ERROR_SEM_TIMEOUT;
  1088. }
  1089. // now done.. so we must remove this ctxt from the list and signal first guy
  1090. //EnterCriticalSection(&DhcpGlobalRecvFromCritSect);
  1091. RemoveEntryList(&Ctxt->RecvList);
  1092. CloseHandle(Ctxt->WaitEvent);
  1093. if( !IsListEmpty(&DhcpGlobalRecvFromList)) { // ok got an elt.. signal this.
  1094. Entry = DhcpGlobalRecvFromList.Flink;
  1095. Ctxt = CONTAINING_RECORD(Entry, RECV_CTXT, RecvList);
  1096. if( 0 == SetEvent(Ctxt->WaitEvent) ) {
  1097. DhcpAssert(FALSE);
  1098. }
  1099. }
  1100. //LeaveCriticalSection(&DhcpGlobalRecvFromCritSect);
  1101. return Error;
  1102. }
  1103. //================================================================================
  1104. // get dhcp message with requested transaction id, but also make sure only one
  1105. // socket is used at any given time (one socket bound to 0.0.0.0), and also
  1106. // re-distribute message for some other thread if that is also required..
  1107. //================================================================================
  1108. DWORD
  1109. GetSpecifiedDhcpMessageEx(
  1110. IN OUT PDHCP_CONTEXT DhcpContext, // which context to recv for
  1111. OUT PDWORD BufferLength, // how big a buffer was read?
  1112. IN DWORD Xid, // which xid to look for?
  1113. IN DWORD TimeToWait // how many seconds to sleep?
  1114. )
  1115. {
  1116. RECV_CTXT Ctxt; // element in list for this call to getspe..
  1117. BOOL First; // is this the first element in list?
  1118. DWORD Result;
  1119. Ctxt.Ctxt = DhcpContext; // fill in the context
  1120. Ctxt.InBufLen = *BufferLength;
  1121. Ctxt.BufLen = BufferLength;
  1122. Ctxt.Xid = Xid;
  1123. Ctxt.ExpTime = time(NULL) + TimeToWait;
  1124. Ctxt.WaitEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
  1125. Ctxt.Recd = FALSE;
  1126. if( NULL == Ctxt.WaitEvent ) {
  1127. DhcpAssert(NULL != Ctxt.WaitEvent);
  1128. return GetLastError();
  1129. }
  1130. First = FALSE;
  1131. InsertInPriorityList(&Ctxt, &DhcpGlobalRecvFromList, &First);
  1132. if( First ) { // this *is* the first call to GetSpec..
  1133. Result = ProcessRecvFromSocket(&Ctxt);
  1134. } else { // we wait for other calls to go thru..
  1135. Result = WaitForSingleObject(Ctxt.WaitEvent, TimeToWait * 1000);
  1136. //EnterCriticalSection(&DhcpGlobalRecvFromCritSect);
  1137. if( Ctxt.Recd || WAIT_FAILED == Result || WAIT_TIMEOUT == Result ) {
  1138. if( WAIT_FAILED == Result ) Result = GetLastError();
  1139. else if (WAIT_TIMEOUT == Result ) Result = ERROR_SEM_TIMEOUT;
  1140. else Result = ERROR_SUCCESS;
  1141. RemoveEntryList(&Ctxt.RecvList); // remove it from list
  1142. //LeaveCriticalSection(&DhcpGlobalRecvFromCritSect);
  1143. CloseHandle(Ctxt.WaitEvent);
  1144. return Result;
  1145. } else {
  1146. DhcpAssert(WAIT_OBJECT_0 == Result && Ctxt.Recd == FALSE );
  1147. // have not received a packet but have been woken up? must be first in line now..
  1148. //LeaveCriticalSection(&DhcpGlobalRecvFromCritSect);
  1149. Result = ProcessRecvFromSocket(&Ctxt);
  1150. }
  1151. }
  1152. return Result;
  1153. }
  1154. #define RATIO 1
  1155. DWORD
  1156. GetSpecifiedDhcpMessage(
  1157. PDHCP_CONTEXT DhcpContext,
  1158. PDWORD BufferLength,
  1159. DWORD TransactionId,
  1160. DWORD TimeToWait
  1161. )
  1162. /*++
  1163. Routine Description:
  1164. This function waits TimeToWait seconds to receives the specified
  1165. DHCP response.
  1166. Arguments:
  1167. DhcpContext - A pointer to a DHCP context block.
  1168. BufferLength - Returns the size of the input buffer.
  1169. TransactionID - A filter. Wait for a message with this TID.
  1170. TimeToWait - Time, in milli seconds, to wait for the message.
  1171. Return Value:
  1172. The status of the operation. If the specified message has been
  1173. been returned, the status is ERROR_TIMEOUT.
  1174. --*/
  1175. {
  1176. struct sockaddr socketName;
  1177. int socketNameSize = sizeof( socketName );
  1178. struct timeval timeout;
  1179. time_t startTime, now;
  1180. DWORD error;
  1181. DWORD actualTimeToWait;
  1182. SOCKET clientSocket;
  1183. fd_set readSocketSet;
  1184. if( !IS_ADDRESS_PLUMBED(DhcpContext) ) {
  1185. //
  1186. // For RAS server Lease API this call won't happen as we don't have to do this nonsense
  1187. //
  1188. error = GetSpecifiedDhcpMessageEx(
  1189. DhcpContext,
  1190. BufferLength,
  1191. TransactionId,
  1192. TimeToWait
  1193. );
  1194. if( ERROR_SUCCESS == error ) {
  1195. // received a message frm the dhcp server..
  1196. SERVER_REACHED(DhcpContext);
  1197. }
  1198. return error;
  1199. }
  1200. startTime = time( NULL );
  1201. actualTimeToWait = TimeToWait;
  1202. //
  1203. // Setup the file descriptor set for select.
  1204. //
  1205. clientSocket = DhcpContext->Socket;
  1206. FD_ZERO( &readSocketSet );
  1207. FD_SET( clientSocket, &readSocketSet );
  1208. while ( 1 ) {
  1209. timeout.tv_sec = actualTimeToWait / RATIO;
  1210. timeout.tv_usec = actualTimeToWait % RATIO;
  1211. DhcpPrint(("Select: waiting for: %ld seconds\n", actualTimeToWait));
  1212. error = _I_select( 0, &readSocketSet, NULL, NULL, &timeout );
  1213. if ( error == 0 ) {
  1214. //
  1215. // Timeout before read data is available.
  1216. //
  1217. DhcpPrint(("Recv timed out\n", 0 ));
  1218. error = ERROR_SEM_TIMEOUT;
  1219. break;
  1220. }
  1221. error = _I_recvfrom(
  1222. clientSocket,
  1223. (PCHAR)DhcpContext->MessageBuffer,
  1224. *BufferLength,
  1225. 0,
  1226. &socketName,
  1227. &socketNameSize
  1228. );
  1229. if ( error == SOCKET_ERROR ) {
  1230. error = _I_WSAGetLastError();
  1231. DhcpPrint(("Recv failed, error = %ld\n", error ));
  1232. if( WSAECONNRESET != error ) break;
  1233. //
  1234. // ignore connreset -- this could be caused by someone sending random ICMP port unreachable.
  1235. //
  1236. } else if (DhcpContext->MessageBuffer->TransactionID == TransactionId ) {
  1237. DhcpPrint(( "Received Message, XID = %lx, MDhcp = %d.\n",
  1238. TransactionId,
  1239. IS_MDHCP_MESSAGE( DhcpContext->MessageBuffer) ));
  1240. if (((unsigned)IS_MDHCP_MESSAGE( DhcpContext->MessageBuffer) == IS_MDHCP_CTX( DhcpContext))) {
  1241. DhcpDumpMessage(DEBUG_PROTOCOL_DUMP, DhcpContext->MessageBuffer );
  1242. *BufferLength = error;
  1243. error = NO_ERROR;
  1244. if( DhcpContext->MessageBuffer->HardwareAddressLength == DhcpContext->HardwareAddressLength
  1245. && 0 == memcmp(DhcpContext->MessageBuffer->HardwareAddress,
  1246. DhcpContext->HardwareAddress,
  1247. DhcpContext->HardwareAddressLength
  1248. )) {
  1249. //
  1250. // Transction IDs match, same type (MDHCP/DHCP), Hardware addresses match!
  1251. //
  1252. break;
  1253. }
  1254. }
  1255. } else {
  1256. DhcpPrint(( "Received a buffer with unknown XID = %lx\n",
  1257. DhcpContext->MessageBuffer->TransactionID ));
  1258. }
  1259. //
  1260. // We received a message, but not the one we're interested in.
  1261. // Reset the timeout to reflect elapsed time, and wait for
  1262. // another message.
  1263. //
  1264. now = time( NULL );
  1265. actualTimeToWait = (DWORD)(TimeToWait - RATIO * (now - startTime));
  1266. if ( (LONG)actualTimeToWait < 0 ) {
  1267. error = ERROR_SEM_TIMEOUT;
  1268. break;
  1269. }
  1270. }
  1271. if ( ERROR_SEM_TIMEOUT != error )
  1272. {
  1273. //
  1274. // a message was received from a DHCP server. disable IP autoconfiguration.
  1275. //
  1276. SERVER_REACHED(DhcpContext);
  1277. }
  1278. return( error );
  1279. }
  1280. DWORD
  1281. QueryWellKnownDnsName(
  1282. IN OUT LPSTR lpszAutoProxyUrl,
  1283. IN DWORD dwAutoProxyUrlLength
  1284. )
  1285. /*++
  1286. Routine Description:
  1287. This function walks a list of standard DNS names trying to find
  1288. an entry for "wpad.some-domain-here.org" If it does, it constructs
  1289. an URL that is suitable for use in auto-proxy.
  1290. Arguments:
  1291. lpszAutoProxyUrl - Url used to return a successful auto-proxy discover
  1292. dwAutoProxyUrlLength - length of buffer passed in above
  1293. Return Value:
  1294. ERROR_SUCCESS - if we found a URL/DNS name
  1295. ERROR_NOT_FOUND - on error
  1296. revised: joshco 7-oct-1998
  1297. if we dont get a valid domain back, be sure and try
  1298. the netbios name ("wpad") no trailing dot.
  1299. revised: joshco 7-oct-1998
  1300. use the define PROXY_AUTO_DETECT_PATH instead
  1301. of hardcoding "wpad.dat"
  1302. --*/
  1303. {
  1304. #define WORK_BUFFER_SIZE 356
  1305. DEBUG_ENTER((DBG_SOCKETS,
  1306. Dword,
  1307. "QueryWellKnownDnsName",
  1308. "%x, %u",
  1309. lpszAutoProxyUrl,
  1310. dwAutoProxyUrlLength
  1311. ));
  1312. char szHostDomain[WORK_BUFFER_SIZE];
  1313. char * pszTemp = szHostDomain ;
  1314. char *pszDot1 = NULL;
  1315. char *pszDot2 = NULL;
  1316. DWORD error = ERROR_NOT_FOUND;
  1317. DWORD dwMinDomain = 2; // By default, assume domain is of the form: .domain-name.org
  1318. lstrcpy(szHostDomain, "wpad.");
  1319. pszTemp += (sizeof("wpad.") - 1);
  1320. if ( SockGetSingleValue(CONFIG_DOMAIN,
  1321. (LPBYTE)pszTemp,
  1322. WORK_BUFFER_SIZE - sizeof("wpad.")
  1323. ) != ERROR_SUCCESS )
  1324. {
  1325. lstrcpy(szHostDomain, "wpad.");
  1326. pszTemp = szHostDomain ;
  1327. pszTemp += (sizeof("wpad.") - 1);
  1328. }
  1329. if ( (GetProxyDetectType() & PROXY_AUTO_DETECT_TYPE_NO_DOMAIN ) ||
  1330. *pszTemp == '\0' )
  1331. {
  1332. // if the debug setting for no domain (netbios) or
  1333. // we didnt get back a valid domain, then just do the
  1334. // netbios name.
  1335. // XXBUG sockgetsinglevalue returns true even if there is no domain
  1336. INET_ASSERT(*(pszTemp - 1 ) == '.');
  1337. *(pszTemp - 1) = '\0';
  1338. }
  1339. // Now determine which form the domain name follows:
  1340. // domain-name.org
  1341. // domain-name.co.uk
  1342. pszDot1 = &szHostDomain[lstrlen(szHostDomain)-1];
  1343. while (pszDot1 >= szHostDomain && *pszDot1 != '.')
  1344. pszDot1--;
  1345. // Only check .?? endings
  1346. if (pszDot1 >= szHostDomain && (pszDot1 + 3 == &szHostDomain[lstrlen(szHostDomain)]) )
  1347. {
  1348. pszDot2 = pszDot1 - 1;
  1349. while (pszDot2 >= szHostDomain && *pszDot2 != '.')
  1350. pszDot2--;
  1351. if (pszDot2 >= szHostDomain && pszDot2 + 3 >= pszDot1)
  1352. {
  1353. // Domain ended in something of the form: .co.uk
  1354. // This requires at least 3 pieces then to be considered a domain
  1355. dwMinDomain = 3;
  1356. }
  1357. else if ((pszDot2 + 4) == pszDot1)
  1358. {
  1359. // Check domain endings of the form ending in .com.uk
  1360. // These special 3-letter pieces also need 3 dots to be classified
  1361. // as a domain. Unfortunately, we can't leverage the equivalent
  1362. // code used by cookies because there, the strings are reversed.
  1363. static const char *s_pachSpecialDomains[] = {"COM", "EDU", "NET", "ORG", "GOV", "MIL", "INT" };
  1364. for (int i=0; i < ARRAY_ELEMENTS(s_pachSpecialDomains); i++)
  1365. {
  1366. if (StrCmpNIC(pszDot2+1, s_pachSpecialDomains[i], 3) == 0)
  1367. {
  1368. dwMinDomain = 3;
  1369. break;
  1370. }
  1371. }
  1372. }
  1373. }
  1374. // IE6 BUG #4242
  1375. // Append a "." suffix to the domain name in order to suppress
  1376. // the DNS suffix search list (if there's room in the string).
  1377. if (lstrlen(szHostDomain)+1 < WORK_BUFFER_SIZE)
  1378. {
  1379. lstrcat(szHostDomain, ".");
  1380. dwMinDomain++;
  1381. }
  1382. while (TRUE)
  1383. {
  1384. PHOSTENT lpHostent = _I_gethostbyname(szHostDomain);
  1385. if ( lpHostent != NULL )
  1386. {
  1387. //
  1388. // Found a host, extract the IP address and form an URL to use.
  1389. //
  1390. char *pszAddressStr;
  1391. LPBYTE * addressList;
  1392. struct in_addr sin_addr;
  1393. DWORD dwIPAddressSize;
  1394. addressList = (LPBYTE *)lpHostent->h_addr_list;
  1395. *(LPDWORD)&sin_addr = *(LPDWORD)addressList[0] ;
  1396. pszAddressStr = _I_inet_ntoa (sin_addr);
  1397. INET_ASSERT(pszAddressStr);
  1398. dwIPAddressSize = lstrlen(pszAddressStr);
  1399. if ( dwAutoProxyUrlLength < (dwIPAddressSize +
  1400. sizeof("http:///") + sizeof(PROXY_AUTO_DETECT_PATH) ) )
  1401. {
  1402. error = ERROR_INSUFFICIENT_BUFFER;
  1403. goto quit;
  1404. }
  1405. wsprintf(lpszAutoProxyUrl, "http://%s/%s", pszAddressStr, PROXY_AUTO_DETECT_PATH );
  1406. error = ERROR_SUCCESS;
  1407. goto quit;
  1408. }
  1409. else
  1410. {
  1411. //
  1412. // Did not find anything yet, reduce the domain level,
  1413. // and if we're in the root domain stop and return error
  1414. //
  1415. DWORD dwPeriodCnt = 0, dwNewEndLength = 0;
  1416. LPSTR lpszPeriod1 = NULL, lpszPeriod2 = NULL;
  1417. for (pszTemp = szHostDomain; *pszTemp; pszTemp++ )
  1418. {
  1419. if ( *pszTemp == '.' ) {
  1420. dwPeriodCnt ++;
  1421. if ( lpszPeriod1 == NULL ) {
  1422. lpszPeriod1 = pszTemp;
  1423. }
  1424. else if ( lpszPeriod2 == NULL ) {
  1425. lpszPeriod2 = pszTemp;
  1426. }
  1427. }
  1428. }
  1429. if ( dwPeriodCnt <= dwMinDomain)
  1430. {
  1431. error = ERROR_NOT_FOUND;
  1432. goto quit;
  1433. }
  1434. dwNewEndLength = lstrlen(lpszPeriod2);
  1435. MoveMemory(lpszPeriod1, lpszPeriod2, dwNewEndLength);
  1436. *(lpszPeriod1 + dwNewEndLength) = '\0';
  1437. }
  1438. }
  1439. quit:
  1440. DEBUG_LEAVE(error);
  1441. return error;
  1442. }
  1443. //================================================================================
  1444. // End of file
  1445. //================================================================================