Source code of Windows XP (NT5)
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.

560 lines
16 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. network.c
  5. Abstract:
  6. This module contains the network interface for the BINL server.
  7. Author:
  8. Colin Watson (colinw) 2-May-1997
  9. Environment:
  10. User Mode - Win32
  11. Revision History:
  12. --*/
  13. #include "binl.h"
  14. #pragma hdrstop
  15. DWORD
  16. BinlWaitForMessage(
  17. BINL_REQUEST_CONTEXT *pRequestContext
  18. )
  19. /*++
  20. Routine Description:
  21. This function waits for a request on the BINL port on any of the
  22. configured interfaces.
  23. Arguments:
  24. RequestContext - A pointer to a request context block for
  25. this request.
  26. Return Value:
  27. The status of the operation.
  28. --*/
  29. {
  30. DWORD length;
  31. DWORD error;
  32. fd_set readSocketSet;
  33. DWORD i;
  34. int readySockets;
  35. struct timeval timeout = { 0x7FFFFFFF, 0 }; // forever.
  36. LPOPTION Option;
  37. LPBYTE EndOfScan;
  38. LPBYTE MagicCookie;
  39. BOOLEAN informPacket;
  40. #define CLIENTOPTIONSTRING "PXEClient"
  41. #define CLIENTOPTIONSIZE (sizeof(CLIENTOPTIONSTRING) - 1)
  42. //
  43. // Loop until we get an extended DHCP request or an error
  44. //
  45. while (1) {
  46. //
  47. // Setup the file descriptor set for select.
  48. //
  49. FD_ZERO( &readSocketSet );
  50. for ( i = 0; i < BinlGlobalNumberOfNets ; i++ ) {
  51. if (BinlGlobalEndpointList[i].Socket) {
  52. FD_SET(
  53. BinlGlobalEndpointList[i].Socket,
  54. &readSocketSet
  55. );
  56. }
  57. }
  58. readySockets = select( 0, &readSocketSet, NULL, NULL, &timeout );
  59. //
  60. // return to caller when the service is shutting down or select()
  61. // times out.
  62. //
  63. if( (readySockets == 0) ||
  64. (WaitForSingleObject( BinlGlobalProcessTerminationEvent, 0 ) == 0) ) {
  65. return( ERROR_SEM_TIMEOUT );
  66. }
  67. if( readySockets == SOCKET_ERROR) {
  68. continue; // Closed the DHCP socket?
  69. }
  70. //
  71. // Time to play 20 question with winsock. Which socket is ready?
  72. //
  73. pRequestContext->ActiveEndpoint = NULL;
  74. for ( i = 0; i < BinlGlobalNumberOfNets ; i++ ) {
  75. if ( FD_ISSET( BinlGlobalEndpointList[i].Socket, &readSocketSet ) ) {
  76. pRequestContext->ActiveEndpoint = &BinlGlobalEndpointList[i];
  77. break;
  78. }
  79. }
  80. //BinlAssert(pRequestContext->ActiveEndpoint != NULL );
  81. if ( pRequestContext->ActiveEndpoint == NULL ) {
  82. return ERROR_SEM_TIMEOUT;
  83. }
  84. //
  85. // Read data from the net. If multiple sockets have data, just
  86. // process the first available socket.
  87. //
  88. pRequestContext->SourceNameLength = sizeof( struct sockaddr );
  89. //
  90. // clean the receive buffer before receiving data in it. We clear
  91. // out one more byte than we actually hand to recvfrom, so we can
  92. // be sure the message has a NULL after it (in case we do a
  93. // wcslen etc. into the received packet).
  94. //
  95. RtlZeroMemory( pRequestContext->ReceiveBuffer, DHCP_MESSAGE_SIZE + 1 );
  96. pRequestContext->ReceiveMessageSize = DHCP_MESSAGE_SIZE;
  97. length = recvfrom(
  98. pRequestContext->ActiveEndpoint->Socket,
  99. (char *)pRequestContext->ReceiveBuffer,
  100. pRequestContext->ReceiveMessageSize,
  101. 0,
  102. &pRequestContext->SourceName,
  103. (int *)&pRequestContext->SourceNameLength
  104. );
  105. if ( length == SOCKET_ERROR ) {
  106. error = WSAGetLastError();
  107. BinlPrintDbg(( DEBUG_ERRORS, "Recv failed, error = %ld\n", error ));
  108. } else {
  109. //
  110. // Ignore all messages that do not look like DHCP or doesn't have the
  111. // option "PXEClient", OR that is not an oschooser message (they
  112. // all start with 0x81).
  113. //
  114. if ( ((LPDHCP_MESSAGE)pRequestContext->ReceiveBuffer)->Operation == OSC_REQUEST) {
  115. //
  116. // All OSC request packets have a 4-byte signature (first byte
  117. // is OSC_REQUEST) followed by a DWORD length (that does not
  118. // include the signature/length). Make sure the length matches
  119. // what we got from recvfrom (we allow padding at the end). We
  120. // use SIGNED_PACKET but any of the XXX_PACKET structures in
  121. // oscpkt.h would work.
  122. //
  123. if (length < FIELD_OFFSET(SIGNED_PACKET, SequenceNumber)) {
  124. BinlPrintDbg(( DEBUG_OSC_ERROR, "Discarding runt packet %d bytes\n", length ));
  125. continue;
  126. }
  127. if ((length - FIELD_OFFSET(SIGNED_PACKET, SequenceNumber)) <
  128. ((SIGNED_PACKET UNALIGNED *)pRequestContext->ReceiveBuffer)->Length) {
  129. BinlPrintDbg(( DEBUG_OSC_ERROR, "Discarding invalid length message %d bytes (header said %d)\n",
  130. length, ((SIGNED_PACKET UNALIGNED *)pRequestContext->ReceiveBuffer)->Length));
  131. continue;
  132. }
  133. BinlPrintDbg(( DEBUG_MESSAGE, "Received OSC message\n", 0 ));
  134. error = ERROR_SUCCESS;
  135. } else {
  136. if ( length < FIELD_OFFSET(DHCP_MESSAGE, Option) + 4 ) {
  137. //
  138. // Message isn't long enough to include the magic cookie, ignore it.
  139. //
  140. continue;
  141. }
  142. if ( ((LPDHCP_MESSAGE)pRequestContext->ReceiveBuffer)->Operation != BOOT_REQUEST) {
  143. continue; // Doesn't look like an interesting DHCP frame
  144. }
  145. // Stop scanning when there isn't room for a ClientOption, including
  146. // the type, length, and the CLIENTOPTIONSTRING.
  147. EndOfScan = pRequestContext->ReceiveBuffer +
  148. pRequestContext->ReceiveMessageSize -
  149. (FIELD_OFFSET(OPTION, OptionValue[0]) + CLIENTOPTIONSIZE);
  150. //
  151. // check magic cookie.
  152. //
  153. MagicCookie = (LPBYTE)&((LPDHCP_MESSAGE)pRequestContext->ReceiveBuffer)->Option;
  154. if( (*MagicCookie != (BYTE)DHCP_MAGIC_COOKIE_BYTE1) ||
  155. (*(MagicCookie+1) != (BYTE)DHCP_MAGIC_COOKIE_BYTE2) ||
  156. (*(MagicCookie+2) != (BYTE)DHCP_MAGIC_COOKIE_BYTE3) ||
  157. (*(MagicCookie+3) != (BYTE)DHCP_MAGIC_COOKIE_BYTE4))
  158. {
  159. continue; // this is a vendor specific magic cookie.
  160. }
  161. Option = (LPOPTION) (MagicCookie + 4);
  162. informPacket = FALSE;
  163. while (((LPBYTE)Option <= EndOfScan) &&
  164. ((Option->OptionType != OPTION_CLIENT_CLASS_INFO) ||
  165. (Option->OptionLength < CLIENTOPTIONSIZE) ||
  166. (memcmp(Option->OptionValue, CLIENTOPTIONSTRING, CLIENTOPTIONSIZE) != 0))) {
  167. if ( Option->OptionType == OPTION_END ){
  168. break;
  169. } else if ( Option->OptionType == OPTION_PAD ){
  170. Option = (LPOPTION)( (LPBYTE)(Option) + 1);
  171. } else {
  172. if (( Option->OptionType == OPTION_MESSAGE_TYPE ) &&
  173. ( Option->OptionLength == 1 ) &&
  174. ( Option->OptionValue[0] == DHCP_INFORM_MESSAGE )) {
  175. informPacket = TRUE;
  176. }
  177. Option = (LPOPTION)( (LPBYTE)(Option) + Option->OptionLength + 2);
  178. }
  179. }
  180. if ((((LPBYTE)Option > EndOfScan) ||
  181. (Option->OptionType == OPTION_END)) &&
  182. (informPacket == FALSE)) {
  183. continue; // Not an extended DHCP packet so ignore it
  184. }
  185. BinlPrintDbg(( DEBUG_MESSAGE, "Received message\n", 0 ));
  186. error = ERROR_SUCCESS;
  187. }
  188. }
  189. pRequestContext->ReceiveMessageSize = length;
  190. return( error );
  191. }
  192. }
  193. DWORD
  194. BinlSendMessage(
  195. LPBINL_REQUEST_CONTEXT RequestContext
  196. )
  197. /*++
  198. Routine Description:
  199. This function send a response to a BINL client.
  200. Arguments:
  201. RequestContext - A pointer to the BinlRequestContext block for
  202. this request.
  203. Return Value:
  204. The status of the operation.
  205. --*/
  206. {
  207. DWORD error;
  208. struct sockaddr_in *source;
  209. LPDHCP_MESSAGE binlMessage;
  210. LPDHCP_MESSAGE binlReceivedMessage;
  211. DWORD MessageLength;
  212. BOOL ArpCacheUpdated = FALSE;
  213. binlMessage = (LPDHCP_MESSAGE) RequestContext->SendBuffer;
  214. binlReceivedMessage = (LPDHCP_MESSAGE) RequestContext->ReceiveBuffer;
  215. //
  216. // if the request arrived from a relay agent, then send the reply
  217. // on server port otherwise leave it as the client's source port.
  218. //
  219. source = (struct sockaddr_in *)&RequestContext->SourceName;
  220. if ( binlReceivedMessage->RelayAgentIpAddress != 0 ) {
  221. source->sin_port = htons( DHCP_SERVR_PORT );
  222. }
  223. //
  224. // if this request arrived from relay agent then send the
  225. // response to the address the relay agent says.
  226. //
  227. if ( binlReceivedMessage->RelayAgentIpAddress != 0 ) {
  228. source->sin_addr.s_addr = binlReceivedMessage->RelayAgentIpAddress;
  229. }
  230. else {
  231. //
  232. // if the client didnt specify broadcast bit and if
  233. // we know the ipaddress of the client then send unicast.
  234. //
  235. //
  236. // But if IgnoreBroadcastFlag is set in the registry and
  237. // if the client requested to broadcast or the server is
  238. // nacking or If the client doesn't have an address yet,
  239. // respond via broadcast.
  240. // Note that IgnoreBroadcastFlag is off by default. But it
  241. // can be set as a workaround for the clients that are not
  242. // capable of receiving unicast
  243. // and they also dont set the broadcast bit.
  244. //
  245. if ( (RequestContext->MessageType == DHCP_INFORM_MESSAGE) &&
  246. (ntohs(binlMessage->Reserved) & DHCP_BROADCAST) ) {
  247. source->sin_addr.s_addr = (DWORD)-1;
  248. } else if ( BinlGlobalIgnoreBroadcastFlag ) {
  249. if ((ntohs(binlReceivedMessage->Reserved) & DHCP_BROADCAST) ||
  250. (binlReceivedMessage->ClientIpAddress == 0) ||
  251. (source->sin_addr.s_addr == 0) ) {
  252. source->sin_addr.s_addr = (DWORD)-1;
  253. binlMessage->Reserved = 0;
  254. // this flag should be zero in the local response.
  255. }
  256. } else {
  257. if( (ntohs(binlReceivedMessage->Reserved) & DHCP_BROADCAST) ||
  258. (!source->sin_addr.s_addr ) ){
  259. source->sin_addr.s_addr = (DWORD)-1;
  260. binlMessage->Reserved = 0;
  261. // this flag should be zero in the local response.
  262. } else {
  263. //
  264. // Send back to the same IP address that the request came in on (
  265. // i.e. source->sin_addr.s_addr)
  266. //
  267. }
  268. }
  269. }
  270. BinlPrint(( DEBUG_STOC, "Sending response to = %s, XID = %lx.\n",
  271. inet_ntoa(source->sin_addr), binlMessage->TransactionID));
  272. //
  273. // send minimum DHCP_MIN_SEND_RECV_PK_SIZE (300) bytes, otherwise
  274. // bootp relay agents don't like the packet.
  275. //
  276. MessageLength = (RequestContext->SendMessageSize >
  277. DHCP_MIN_SEND_RECV_PK_SIZE) ?
  278. RequestContext->SendMessageSize :
  279. DHCP_MIN_SEND_RECV_PK_SIZE;
  280. error = sendto(
  281. RequestContext->ActiveEndpoint->Socket,
  282. (char *)RequestContext->SendBuffer,
  283. MessageLength,
  284. 0,
  285. &RequestContext->SourceName,
  286. RequestContext->SourceNameLength
  287. );
  288. if ( error == SOCKET_ERROR ) {
  289. error = WSAGetLastError();
  290. BinlPrintDbg(( DEBUG_ERRORS, "Send failed, error = %ld\n", error ));
  291. } else {
  292. error = ERROR_SUCCESS;
  293. }
  294. return( error );
  295. }
  296. NTSTATUS
  297. GetIpAddressInfo (
  298. ULONG Delay
  299. )
  300. {
  301. PDNS_ADDRESS_INFO pAddressInfo = NULL;
  302. ULONG count;
  303. //
  304. // We can get out ahead of the dns cached info here... let's delay a bit
  305. // if the pnp logic told us there was a change.
  306. //
  307. if (Delay) {
  308. Sleep( Delay );
  309. }
  310. count = DnsGetIpAddressInfoList( &pAddressInfo );
  311. if (count == 0) {
  312. //
  313. // we don't know what went wrong, we'll fall back to old APIs.
  314. //
  315. DHCP_IP_ADDRESS ipaddr = 0;
  316. PHOSTENT Host = gethostbyname( NULL );
  317. if (Host) {
  318. ipaddr = *(PDHCP_IP_ADDRESS)Host->h_addr;
  319. if ((Host->h_addr_list[0] != NULL) &&
  320. (Host->h_addr_list[1] != NULL)) {
  321. BinlIsMultihomed = TRUE;
  322. } else {
  323. BinlIsMultihomed = FALSE;
  324. }
  325. BinlGlobalMyIpAddress = ipaddr;
  326. } else {
  327. //
  328. // what's with the ip stack? we can't get any type of address
  329. // info out of it... for now, we won't answer any if we don't
  330. // already have the info we need.
  331. //
  332. if (BinlDnsAddressInfo == NULL) {
  333. BinlIsMultihomed = TRUE;
  334. }
  335. }
  336. return STATUS_SUCCESS;
  337. }
  338. EnterCriticalSection(&gcsParameters);
  339. if (BinlDnsAddressInfo) {
  340. LocalFree( BinlDnsAddressInfo );
  341. }
  342. BinlDnsAddressInfo = pAddressInfo;
  343. BinlDnsAddressInfoCount = count;
  344. BinlIsMultihomed = (count != 1);
  345. if (!BinlIsMultihomed) {
  346. BinlGlobalMyIpAddress = pAddressInfo->ipAddress;
  347. }
  348. LeaveCriticalSection(&gcsParameters);
  349. return STATUS_SUCCESS;
  350. }
  351. DHCP_IP_ADDRESS
  352. BinlGetMyNetworkAddress (
  353. LPBINL_REQUEST_CONTEXT RequestContext
  354. )
  355. {
  356. ULONG RemoteIp;
  357. DHCP_IP_ADDRESS ipaddr;
  358. ULONG i;
  359. ULONG subnetMask;
  360. ULONG localAddr;
  361. BinlAssert( RequestContext != NULL);
  362. //
  363. // If we're not multihomed, then we know the address since there's just one.
  364. //
  365. if (!BinlIsMultihomed) {
  366. return BinlGlobalMyIpAddress;
  367. }
  368. RemoteIp = ((struct sockaddr_in *)&RequestContext->SourceName)->sin_addr.s_addr;
  369. if (RemoteIp == 0) {
  370. //
  371. // If we're multihomed and the client doesn't yet have an IP address,
  372. // then we return 0, because we don't know which of our addresses to
  373. // use to talk to the client.
  374. //
  375. return 0;
  376. }
  377. EnterCriticalSection(&gcsParameters);
  378. if (BinlDnsAddressInfo == NULL) {
  379. LeaveCriticalSection(&gcsParameters);
  380. return (BinlIsMultihomed ? 0 : BinlGlobalMyIpAddress);
  381. }
  382. ipaddr = 0;
  383. for (i = 0; i < BinlDnsAddressInfoCount; i++) {
  384. localAddr = BinlDnsAddressInfo[i].ipAddress;
  385. subnetMask = BinlDnsAddressInfo[i].subnetMask;
  386. //
  387. // check that the remote ip address may have come from this subnet.
  388. // note that the address could be the address of a dhcp relay agent,
  389. // which is fine since we're just looking for the address of the
  390. // local subnet to broadcast the response on.
  391. //
  392. if ((RemoteIp & subnetMask) == (localAddr & subnetMask)) {
  393. ipaddr = localAddr;
  394. break;
  395. }
  396. }
  397. LeaveCriticalSection(&gcsParameters);
  398. return ipaddr;
  399. }
  400. VOID
  401. FreeIpAddressInfo (
  402. VOID
  403. )
  404. {
  405. EnterCriticalSection(&gcsParameters);
  406. if (BinlDnsAddressInfo != NULL) {
  407. LocalFree( BinlDnsAddressInfo );
  408. }
  409. BinlDnsAddressInfo = NULL;
  410. BinlDnsAddressInfoCount = 0;
  411. LeaveCriticalSection(&gcsParameters);
  412. return;
  413. }