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.

1689 lines
40 KiB

  1. /*++
  2. Copyright (c) 1992-1996 Microsoft Corporation
  3. Module Name:
  4. tftpd.c
  5. Author:
  6. Sam Patton (sampa) 08-Apr-1992
  7. History:
  8. MohsinA, 02-Dec-96. - Winsock2 asynchronous version with
  9. synchronisation events, graceful termination,
  10. event logging, debugging info.
  11. Credits: Anirudh Sahni - Info on SC.
  12. AdamBa, 11-Jun-97 - add security
  13. --*/
  14. #include "tftpd.h"
  15. char USAGE[] =
  16. " ======================================================================== \n"
  17. "Abstract: \n"
  18. " This implements an RFC 783 tftp daemon. \n"
  19. " It listens on port 69 for requests \n"
  20. " and spawns a thread to process each request. \n"
  21. " \n"
  22. "TFTPD USAGE and Installation: \n"
  23. " \n"
  24. " md d:/tftpd (the StartDirectory). \n"
  25. " copy //MohsinA_p90/test/tftpd.exe . \n"
  26. " sc create tftpd binPath= d:/tftpd/tftpd.exe (give full path). \n"
  27. " sc query tftpd (check if installed). \n"
  28. " \n"
  29. "Start: \n"
  30. " sc start tftpd -f (creates a log file). \n"
  31. "or sc start tftpd \n"
  32. "or net start tftpd \n"
  33. "or sc start tftpd [-dStartDirectory] [-e] [-f] \n"
  34. " Options: -e use event log. \n"
  35. " -f log to file. \n"
  36. " -dStartDirectory \n"
  37. "Info: \n"
  38. " sc interrogate tftpd (logs will be updated). \n"
  39. " sc query tftpd Check whether running. \n"
  40. "Stop: \n"
  41. " sc stop tftpd \n"
  42. " net stop tftpd \n"
  43. " \n"
  44. "Variables that control what files can be read/written and by whom: \n"
  45. " StartDirectory - only files there will be accessible. \n"
  46. " LogFile is created here. \n"
  47. " ValidClients - Clients matching this ip address can read files. \n"
  48. " eg. you can set it to \"157.55.8?.*\" \n"
  49. " ValidMasters - clients matching this can write and read files. \n"
  50. " eg. you can set it to \"\" and no one can write. \n"
  51. " ValidReadFiles - only matching files will be served out, eg. \"r*.t?t\"\n"
  52. " ValidWriteFiles- only matching files will be accepted, eg. \"w*.txt\" \n"
  53. " \n"
  54. "Client: \n"
  55. " tftp [-i] servername {get|put} src_file dest_file \n"
  56. " -i from binary mode, else ascii mode is used. \n"
  57. " \n"
  58. " ======================================================================== \n"
  59. ;
  60. #define NUM_INITIAL_THREADS 1
  61. #define MAX_THREAD_COUNT 25
  62. #define RCV_WAIT_INTERVAL_MSEC 5
  63. #define RECEIVE_HEAP_THREASHOLD1 10
  64. #define RECEIVE_HEAP_THREASHOLD2 3
  65. #define RECEIVE_NUM_FREE 2
  66. #define MAX_SOCKET_INIT_ATTEMPT 10
  67. #define LOOPBACK 0x100007f
  68. #define SE_SOCKET_REOPEN 0x1
  69. #define MAX_LOCK_TRIES 125
  70. SERVICE_STATUS TftpdServiceStatus;
  71. SERVICE_STATUS_HANDLE TftpdServiceStatusHandle;
  72. char TftpdServiceName[] = TEXT("Tftpd");
  73. HANDLE MasterThreadHandle;
  74. DWORD MasterThreadId;
  75. WSADATA WsaData;
  76. FILE * LogFile = NULL;
  77. BOOL LoggingEvent = FALSE;
  78. BOOL LoggingFile = FALSE;
  79. HANDLE eMasterTerminate = NULL; // Event to stop main thread.
  80. HANDLE eSock = NULL; // Event to start WSARecvFrom
  81. DWORD TotalThreadCount=0; // total worker threads
  82. DWORD AvailableThreads=0; // number of idle worker threads
  83. TFTP_GLOBALS Globals;
  84. HANDLE MasterSocketEvent; // set when data available on TFTPD socket
  85. HANDLE MasterSocketWait; // handle to RtlRegisterWait for above event
  86. LIST_ENTRY ReceiveList;
  87. CRITICAL_SECTION ReceiveLock;
  88. LIST_ENTRY SocketList;
  89. CRITICAL_SECTION SocketLock;
  90. HANDLE ReceiveHeap=0;
  91. HANDLE ReaperWait;
  92. DWORD ReceiveHeapSize=0;
  93. DWORD ActiveReceive=0;
  94. OVERLAPPED AddrChangeOverlapped;
  95. HANDLE AddrChangeEvent=NULL;
  96. HANDLE AddrChangeWaitHandle=NULL;
  97. HANDLE AddrChangeHandle=NULL;
  98. const char BuildString[] =
  99. __FILE__ " built " __DATE__ " " __TIME__ "\n";
  100. struct TFTPD_STAT tftpd_stat;
  101. // See sdk/inc/winsvc.h
  102. SERVICE_TABLE_ENTRY TftpdDispatchTable[] = {
  103. {
  104. TftpdServiceName, // TEXT "Tftpd"
  105. TftpdStart // main function.
  106. },
  107. { NULL, NULL }
  108. };
  109. #if defined(REMOTE_BOOT_SECURITY)
  110. BOOL
  111. TftpdInitSecurityArray(
  112. VOID
  113. );
  114. VOID
  115. TftpdUninitSecurityArray(
  116. VOID
  117. );
  118. #endif //defined(REMOTE_BOOT_SECURITY)
  119. // ========================================================================
  120. void
  121. __cdecl
  122. main(
  123. int argc,
  124. char * argv[]
  125. )
  126. {
  127. int ok;
  128. DbgPrint( "main: %s"
  129. "Starting service \"%s\".\n",
  130. BuildString,
  131. TftpdDispatchTable[0].lpServiceName );
  132. if( argc > 1 && !strcmp( argv[1], "-?" ) ){
  133. printf( USAGE );
  134. printf( " TFTPD_DEFAULT_DIR is %s\n", TFTPD_DEFAULT_DIR );
  135. printf( " TFTPD_LOGFILE is %s\n\n", TFTPD_LOGFILE );
  136. printf( "Registry key names, all strings: HKEY_LOCAL_MACHINE %s\n",
  137. TFTPD_REGKEY );
  138. printf( " o StartDirectory keyname \"%s\"\n", TFTPD_REGKEY_DIR );
  139. printf( "These keys are shell patterns with * and ? (see examples above):\n");
  140. printf( " o ValidClients keyname \"%s\"\n", TFTPD_REGKEY_CLIENTS );
  141. printf( " o ValidMasters keyname \"%s\"\n", TFTPD_REGKEY_MASTERS );
  142. printf( " o Readable files keyname \"%s\"\n", TFTPD_REGKEY_READABLE );
  143. printf( " o writable files keyname \"%s\"\n", TFTPD_REGKEY_WRITEABLE);
  144. exit( -1 );
  145. }
  146. ok =
  147. StartServiceCtrlDispatcher(TftpdDispatchTable);
  148. if( ok ){
  149. DbgPrint("tftpd StartServiceCtrlDispatcher ok.\n" );
  150. }else{
  151. DbgPrint("tftpd StartServiceCtrlDispatcher error=%d\n",
  152. GetLastError() );
  153. }
  154. ExitProcess(0);
  155. }
  156. // ========================================================================
  157. // Main function called by service controller.
  158. DWORD
  159. TftpdStart(
  160. IN DWORD argc,
  161. IN LPTSTR argv[]
  162. )
  163. /*++
  164. Routine Description:
  165. This sits waiting on the tftpd well-known port (69) until it gets a request
  166. Then it spawns a thread to either TftpdHandleRead or TftpdHandleWrite which
  167. processes the request.
  168. Arguments:
  169. argc:
  170. argv: [-dStartDirectory] option.
  171. Return Value:
  172. None
  173. Error?
  174. --*/
  175. {
  176. int Status;
  177. struct sockaddr_in TftpdAddress;
  178. int err, ok, i;
  179. DWORD WaitStatus;
  180. //
  181. // Initialize all the status fields so that subsequent calls to
  182. // SetServiceStatus need to only update fields that changed.
  183. //
  184. TftpdServiceStatus.dwServiceType = SERVICE_WIN32;
  185. TftpdServiceStatus.dwCurrentState = SERVICE_START_PENDING;
  186. TftpdServiceStatus.dwControlsAccepted = 0;
  187. TftpdServiceStatus.dwCheckPoint = 1;
  188. TftpdServiceStatus.dwWaitHint = 20000; // 20 seconds
  189. SET_SERVICE_EXITCODE(
  190. NO_ERROR,
  191. TftpdServiceStatus.dwWin32ExitCode,
  192. TftpdServiceStatus.dwServiceSpecificExitCode
  193. );
  194. //
  195. // Initialize server to receive service requests by registering the
  196. // control handler.
  197. //
  198. TftpdServiceStatusHandle =
  199. RegisterServiceCtrlHandler(
  200. TftpdServiceName,
  201. TftpdControlHandler
  202. );
  203. if ( TftpdServiceStatusHandle == 0 ) {
  204. DbgPrint("TftpdStart: RegisterServiceCtrlHandler failed: %lx\n",
  205. GetLastError());
  206. goto failed;
  207. }
  208. ok =
  209. SetServiceStatus( TftpdServiceStatusHandle, &TftpdServiceStatus);
  210. if( !ok ){
  211. DbgPrint("TftpdStart: SetServiceStatus failed=%d\n",
  212. GetLastError() );
  213. goto failed;
  214. }
  215. //
  216. // Create the main synchronisation events.
  217. //
  218. eMasterTerminate =
  219. CreateEvent(
  220. NULL, // LPSECURITY_ATTRIBUTES.
  221. FALSE, // bManualReset,
  222. FALSE, // bInitialState
  223. NULL // LPCTSTR pointer to event-object name
  224. );
  225. eSock = CreateEvent( NULL, FALSE, FALSE, NULL );
  226. if( ! eMasterTerminate || ! eSock ){
  227. DbgPrint("TftpdStart: CreateEvent failed.\n");
  228. goto failed;
  229. }
  230. //
  231. // Start Winsock
  232. //
  233. err =
  234. WSAStartup( 0x0101, &WsaData );
  235. if (err == SOCKET_ERROR ) {
  236. DbgPrint("TftpdStart: WSAStartup failed, Error=%d.\n",
  237. WSAGetLastError() );
  238. goto failed;
  239. }
  240. DbgPrint("TftpdStart: winsock: %x/%x, %s\n %s\n",
  241. WsaData.wVersion, WsaData.wHighVersion,
  242. WsaData.szDescription, WsaData.szSystemStatus );
  243. #if defined(REMOTE_BOOT_SECURITY)
  244. //
  245. // Initialize security-related info.
  246. //
  247. if (!TftpdInitSecurityArray()) {
  248. DbgPrint("TftpdStart: cannot initialize security array\n");
  249. goto failed;
  250. }
  251. #endif //defined(REMOTE_BOOT_SECURITY)
  252. //
  253. // Startup worked - announce that we're all done.
  254. //
  255. TftpdServiceStatus.dwCurrentState = SERVICE_RUNNING;
  256. TftpdServiceStatus.dwControlsAccepted =
  257. SERVICE_ACCEPT_STOP
  258. | SERVICE_ACCEPT_PAUSE_CONTINUE
  259. | SERVICE_ACCEPT_SHUTDOWN;
  260. TftpdServiceStatus.dwCheckPoint = 0;
  261. TftpdServiceStatus.dwWaitHint = 0;
  262. ok =
  263. SetServiceStatus(
  264. TftpdServiceStatusHandle,
  265. &TftpdServiceStatus
  266. );
  267. if( !ok ){
  268. DbgPrint("TftpdStart: SetServiceStatus failed=%d\n",
  269. GetLastError() );
  270. goto failed;
  271. }
  272. //
  273. // Clear the stats.
  274. //
  275. memset( &tftpd_stat, '\0', sizeof( struct TFTPD_STAT ) );
  276. time( &tftpd_stat.started_at );
  277. //
  278. // Process Command line arguments/options.
  279. //
  280. #ifdef DBG
  281. DbgPrint("TftpdStart: argc=%d\n", argc);
  282. for( ok = 0; ok < (int) argc; ok++ ){
  283. DbgPrint("\targv[%d]=%s.\n", ok, argv[ok] );
  284. }
  285. #endif
  286. while( --argc > 0 && argv[argc][0] == '-' ){
  287. switch( argv[argc][1] ){
  288. case 'd' : // ie. -ddirectory.
  289. strcpy( StartDirectory, &argv[argc][2] );
  290. DbgPrint("TftpdStart: StartDirectory=%s\n", StartDirectory );
  291. break;
  292. case 'e' :
  293. LoggingEvent = TRUE;
  294. break;
  295. case 'f' :
  296. LoggingFile = TRUE;
  297. break;
  298. default:
  299. DbgPrint("TftpdStart: invalid option %s\n", argv[0] );
  300. break;
  301. }
  302. }
  303. ReadRegistryValues();
  304. Set_StartDirectory();
  305. //
  306. // Start in the dir from where we want to export files.
  307. //
  308. err = _chdir( StartDirectory );
  309. if( err == -1 ){
  310. DbgPrint( "TftpdStart: _chdir(%s) failed, errno=%d\n",
  311. StartDirectory, errno
  312. );
  313. err = _mkdir(StartDirectory);
  314. if (err == ERROR_SUCCESS) {
  315. err = _chdir( StartDirectory );
  316. }
  317. if (err != ERROR_SUCCESS) {
  318. goto failed;
  319. }
  320. }
  321. //
  322. // Open Logfile only after chdir.
  323. //
  324. if( LoggingFile ){
  325. LogFile = fopen( TFTPD_LOGFILE, "a+" );
  326. if( LogFile == NULL ){
  327. LoggingFile = FALSE;
  328. DbgPrint( "Cannot open TFTPD_LOGFILE=%s\n", TFTPD_LOGFILE );
  329. }
  330. }
  331. //
  332. // Only the risque and successful come here.
  333. //
  334. DbgPrint("TftpdStart in %s at %s.\n",
  335. StartDirectory,
  336. ctime( &tftpd_stat.started_at )
  337. );
  338. // Initialize Thread Pool
  339. TftpdInitializeThreadPool();
  340. TftpdInitializeReceiveHeap();
  341. WaitStatus=WaitForSingleObject(eMasterTerminate,INFINITE);
  342. if (WaitStatus != WAIT_OBJECT_0) {
  343. DbgPrint("Wait for termination error %d",GetLastError());
  344. }
  345. // Thread pool thread still active, but oh well ... exiting process.
  346. return(0);
  347. //
  348. // Failures jump here.
  349. //
  350. failed:
  351. TftpdServiceExit(ERROR_GEN_FAILURE);
  352. exit(1);
  353. return(0);
  354. }
  355. HANDLE RegisterSocket(SOCKET Sock, HANDLE Event, DWORD Flag)
  356. /*++
  357. Routine Description:
  358. Schedule calling of TftpdReceive when data received on socket Sock
  359. Arguments:
  360. Return Value:
  361. Handle to registered function, NULL on failure
  362. --*/
  363. {
  364. NTSTATUS status;
  365. HANDLE MasterSocketWait;
  366. DbgPrint("RegisterSocket: Socket %d, Event %d\n",Sock,Event);
  367. if (WSAEventSelect(Sock,Event,FD_READ|FD_WRITE) != 0) {
  368. DbgPrint("EventSelect failed %d",GetLastError());
  369. return 0;
  370. }
  371. if (Flag & REG_NEW_SOCKET) {
  372. status = RtlRegisterWait(&MasterSocketWait,
  373. Event,
  374. TftpdNewReceive,
  375. (PVOID)Sock,
  376. INFINITE,
  377. 0);
  378. } else {
  379. status = RtlRegisterWait(&MasterSocketWait,
  380. Event,
  381. TftpdContinueReceive,
  382. (PVOID)Sock,
  383. INFINITE,
  384. 0);
  385. }
  386. if (!NT_SUCCESS(status)) {
  387. DbgPrint("Failed to register wait %d",status);
  388. }
  389. return MasterSocketWait;
  390. }
  391. DWORD TftpdInitializeThreadPool()
  392. /*++
  393. Routine Description:
  394. Create initial pool of thread
  395. Arguments:
  396. Return Value:
  397. Exit status
  398. 0 == success
  399. 1 == failure
  400. --*/
  401. {
  402. DWORD i;
  403. HANDLE ThreadHandle;
  404. DWORD ThreadId;
  405. NTSTATUS status;
  406. PMIB_IPADDRTABLE IpAddrTable;
  407. InitializeCriticalSection(&Globals.Lock);
  408. InitializeCriticalSection(&SocketLock);
  409. InitializeListHead(&Globals.WorkList);
  410. InitializeListHead(&SocketList);
  411. if (GetIpTable(&IpAddrTable) == ERROR_SUCCESS) {
  412. for (i=0; i < IpAddrTable->dwNumEntries; i++) {
  413. if ( (IpAddrTable->table[i].dwAddr != 0) &&
  414. (IpAddrTable->table[i].dwAddr != LOOPBACK)) {
  415. AddSocket(IpAddrTable->table[i].dwAddr);
  416. }
  417. }
  418. free(IpAddrTable);
  419. }
  420. status=RtlCreateTimerQueue(&Globals.TimerQueueHandle);
  421. if (status != ERROR_SUCCESS) {
  422. return status;
  423. }
  424. status=RtlCreateTimer(Globals.TimerQueueHandle,
  425. &ReaperWait,
  426. TftpdReaper,
  427. (PVOID)NULL,
  428. REAPER_INTERVAL_SEC*1000,
  429. REAPER_INTERVAL_SEC*1000,
  430. 0);
  431. if (!NT_SUCCESS(status)) {
  432. DbgPrint("Failed to Arm Timer %d",status);
  433. }
  434. AddrChangeEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
  435. if (!AddrChangeEvent) {
  436. return status;
  437. }
  438. status =
  439. RtlRegisterWait(
  440. &AddrChangeWaitHandle,
  441. AddrChangeEvent,
  442. InterfaceChange,
  443. NULL,
  444. INFINITE,
  445. 0
  446. );
  447. if (status != ERROR_SUCCESS) {
  448. return status;
  449. }
  450. memset(&AddrChangeOverlapped,0,sizeof(OVERLAPPED));
  451. AddrChangeOverlapped.hEvent=AddrChangeEvent;
  452. status = NotifyAddrChange(&AddrChangeHandle,&AddrChangeOverlapped);
  453. if (status != ERROR_SUCCESS && status != ERROR_IO_PENDING) {
  454. return status;
  455. }
  456. return ERROR_SUCCESS;
  457. }
  458. VOID TftpdInitializeReceiveHeap()
  459. {
  460. DWORD size;
  461. InitializeListHead(&ReceiveList);
  462. InitializeCriticalSection(&ReceiveLock);
  463. size = 5 * (sizeof(TFTP_REQUEST));
  464. ReceiveHeap = HeapCreate(0, size ,0);
  465. ASSERT(ReceiveHeap);
  466. }
  467. /*
  468. Called on the reaper interval. Cleanup extra heap entries
  469. */
  470. VOID TftpdCleanHeap()
  471. {
  472. DWORD i=0;
  473. PLIST_ENTRY pEntry;
  474. PTFTP_REQUEST Request=NULL;
  475. DWORD NumToFree=0;
  476. EnterCriticalSection(&ReceiveLock);
  477. if (ReceiveHeapSize - ActiveReceive > RECEIVE_HEAP_THREASHOLD1) {
  478. NumToFree = ((ReceiveHeapSize - ActiveReceive) / 2);
  479. } else if ((ReceiveHeapSize - ActiveReceive > RECEIVE_HEAP_THREASHOLD2)) {
  480. NumToFree = RECEIVE_NUM_FREE;
  481. }
  482. while(i < NumToFree) {
  483. pEntry=RemoveHeadList(&ReceiveList);
  484. Request=CONTAINING_RECORD(pEntry,TFTP_REQUEST,RequestLinkage);
  485. CloseHandle(Request->RcvEvent);
  486. HeapFree(ReceiveHeap,0,Request);
  487. i++;
  488. ReceiveHeapSize--;
  489. }
  490. LeaveCriticalSection(&ReceiveLock);
  491. }
  492. DWORD HandleRequest(SOCKET Sock, IPAddr MyAddr)
  493. {
  494. unsigned short Opcode;
  495. struct sockaddr_in PeerAddress;
  496. int PeerAddressLength;
  497. PTFTP_REQUEST Request;
  498. char RequestPacket[MAX_TFTP_DATAGRAM + 1];
  499. int Status;
  500. struct sockaddr_in TftpdAddress;
  501. HANDLE ThreadHandle;
  502. DWORD ThreadId;
  503. LPTHREAD_START_ROUTINE ThreadRoutine=NULL;
  504. WSABUF RecvBuf[1];
  505. WSAOVERLAPPED Overlap;
  506. DWORD dwNumberBytes = 0;
  507. DWORD dwFlags = 0;
  508. int err, ok;
  509. HANDLE handle_list[2];
  510. DWORD handle_ready;
  511. PLIST_ENTRY pEntry;
  512. DWORD size;
  513. DWORD IterCount=0;
  514. SocketEntry *SE;
  515. IterCount=0;
  516. while(1) {
  517. // loop while data available on this socket
  518. {
  519. int ret;
  520. DWORD DataAvail;
  521. ret=ioctlsocket(Sock,
  522. FIONREAD,
  523. &DataAvail);
  524. if (ret != 0) {
  525. DbgPrint("ioctlsocket failed %d",WSAGetLastError());
  526. return 0;
  527. }
  528. if (DataAvail == 0) {
  529. return 0;
  530. }
  531. }
  532. err = 0;
  533. handle_ready = 0;
  534. memset( &Overlap, 0, sizeof(WSAOVERLAPPED));
  535. EnterCriticalSection(&ReceiveLock);
  536. if (!IsListEmpty(&ReceiveList)) {
  537. pEntry=RemoveHeadList(&ReceiveList);
  538. Request=CONTAINING_RECORD(pEntry,TFTP_REQUEST,RequestLinkage);
  539. ResetEvent(Request->RcvEvent);
  540. Overlap.hEvent = Request->RcvEvent;
  541. } else {
  542. size = sizeof(TFTP_REQUEST);
  543. Request = HeapAlloc(ReceiveHeap, HEAP_ZERO_MEMORY, size);
  544. if (Request == NULL) {
  545. DWORD bytesReceived = 0, flags = 0;
  546. // Failed to allocate REQUEST structure, perform no-op winsock recv
  547. // so as to re-enable its event-signalling mechanism.
  548. if (WSARecvFrom(Sock, NULL, 0, &bytesReceived, &flags,
  549. NULL, NULL, NULL, NULL) == SOCKET_ERROR)
  550. DbgPrint("HandleRequest: Failed to allocate REQUEST structure, "
  551. "and failed to re-enable socket event.\n");
  552. LeaveCriticalSection(&ReceiveLock);
  553. return 0;
  554. }
  555. ReceiveHeapSize++;
  556. Request->RcvEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  557. if (Request->RcvEvent == NULL) {
  558. DWORD bytesReceived = 0, flags = 0;
  559. HeapFree(ReceiveHeap, 0, Request);
  560. ReceiveHeapSize--;
  561. // Failed to create event, perform no-op winsock recv
  562. // so as to re-enable its event-signalling mechanism.
  563. if (WSARecvFrom(Sock, NULL, 0, &bytesReceived, &flags,
  564. NULL, NULL, NULL, NULL) == SOCKET_ERROR)
  565. DbgPrint("HandleRequest: Failed to allocate REQUEST structure, "
  566. "and failed to re-enable socket event.\n");
  567. LeaveCriticalSection(&ReceiveLock);
  568. return 0;
  569. }
  570. Overlap.hEvent = Request->RcvEvent;
  571. }
  572. LeaveCriticalSection(&ReceiveLock);
  573. ActiveReceive++;
  574. // Zero the request packet, to streamline option negotiation code.
  575. memset( Request->Packet1, 0, MAX_TFTP_DATAGRAM + 1);
  576. RecvBuf[0].buf = (char*)&Request->Packet1;
  577. RecvBuf[0].len = MAX_TFTP_DATAGRAM + 1;
  578. PeerAddressLength = sizeof(PeerAddress);
  579. Request->MyAddr=MyAddr;
  580. Status =
  581. WSARecvFrom(
  582. Sock,
  583. RecvBuf,
  584. 1,
  585. &Request->DataSize,
  586. &dwFlags,
  587. (struct sockaddr *) &Request->ForeignAddress,
  588. &PeerAddressLength,
  589. &Overlap,
  590. NULL
  591. );
  592. DbgPrint("Received from Peer Addr: %x Port %d\n",Request->ForeignAddress.sin_addr.s_addr,
  593. ntohs(Request->ForeignAddress.sin_port));
  594. if( Status ){
  595. err = WSAGetLastError();
  596. DbgPrint("err %d\n",err);
  597. if( err == WSA_IO_PENDING){
  598. DbgPrint("Operation pending\n");
  599. handle_list[0] = eMasterTerminate;
  600. handle_list[1] = Overlap.hEvent;
  601. handle_ready =
  602. WaitForMultipleObjects(
  603. 2,
  604. handle_list,
  605. FALSE,
  606. INFINITE
  607. );
  608. if( handle_ready == WAIT_FAILED ){
  609. DbgPrint("TftpdMasterThread: WAIT_FAILED\n");
  610. goto failed;
  611. }
  612. if (handle_ready == WAIT_TIMEOUT) {
  613. DbgPrint("Wait timed out Socket %d\n",Sock);
  614. goto failed;
  615. }
  616. if( handle_ready == WAIT_OBJECT_0 ){
  617. goto terminated;
  618. } else {
  619. ok =
  620. WSAGetOverlappedResult(
  621. Sock, // SOCKET s,
  622. &Overlap, // LPWSAOVERLAPPED lpOverlapped,
  623. &Request->DataSize, // LPDWORD lpcbTransfer,
  624. FALSE, // BOOL fWait,
  625. &dwFlags // LPDWORD lpdwFlags
  626. );
  627. if( ! ok ){
  628. DbgPrint("WSAGetOverlappedResult failed=%d",
  629. WSAGetLastError() );
  630. goto cleanup;
  631. }
  632. }
  633. }else{
  634. DbgPrint("TftpdMasterThread: WSARecvFrom failed=%d.\n", err );
  635. goto failed;
  636. }
  637. }
  638. IterCount++;
  639. if( WaitForSingleObject( eMasterTerminate, 0 ) == WAIT_OBJECT_0 ){
  640. goto terminated;
  641. }
  642. Status = Request->DataSize; // winsock1 <= winsock2.
  643. if( Status >= 2 ){
  644. //
  645. // Process the packet
  646. //
  647. if (MyAddr != 0) {
  648. // New request
  649. ThreadRoutine=NULL;
  650. Opcode = *((unsigned short *) &Request->Packet1[0]);
  651. Opcode = htons(Opcode);
  652. switch(Opcode) {
  653. case TFTPD_RRQ:
  654. case TFTPD_WRQ:
  655. #if defined(REMOTE_BOOT_SECURITY)
  656. case TFTPD_LOGIN:
  657. case TFTPD_KEY:
  658. #endif // defined(REMOTE_BOOT_SECURITY)
  659. if ( Opcode == TFTPD_RRQ ) {
  660. tftpd_stat.req_read++;
  661. ThreadRoutine = TftpdHandleRead;
  662. } else if ( Opcode == TFTPD_WRQ ) {
  663. tftpd_stat.req_write++;
  664. ThreadRoutine = TftpdHandleWrite;
  665. }
  666. #if defined(REMOTE_BOOT_SECURITY)
  667. else if ( Opcode == TFTPD_LOGIN ) {
  668. tftpd_stat.req_login++;
  669. ThreadRoutine = TftpdHandleLogin;
  670. } else {
  671. tftpd_stat.req_key++;
  672. ThreadRoutine = TftpdHandleKey;
  673. }
  674. #endif //defined(REMOTE_BOOT_SECURITY)
  675. Request->TftpdPort = Sock;
  676. DbgPrint("Posting work");
  677. if (ThreadRoutine) {
  678. (*ThreadRoutine)(Request);
  679. }
  680. break;
  681. case TFTPD_ERROR:
  682. break;
  683. case TFTPD_ACK:
  684. case TFTPD_DATA:
  685. case TFTPD_OACK:
  686. default:
  687. DbgPrint("TftpdMasterThread: invalid TFTPD_(%d).\n", Opcode );
  688. tftpd_stat.req_error++;
  689. TftpdErrorPacket(
  690. (struct sockaddr *) &PeerAddress,
  691. RequestPacket,
  692. Sock,
  693. TFTPD_ERROR_ILLEGAL_OPERATION,
  694. NULL);
  695. }
  696. } else {
  697. // Continue serving existing request
  698. Request->TftpdPort=Sock;
  699. TftpdResumeProcessing(Request);
  700. }
  701. }
  702. cleanup:
  703. EnterCriticalSection(&ReceiveLock);
  704. pEntry=&Request->RequestLinkage;
  705. InsertHeadList(&ReceiveList,pEntry);
  706. InterlockedIncrement(&AvailableThreads);
  707. ActiveReceive--;
  708. LeaveCriticalSection(&ReceiveLock);
  709. }
  710. terminated:
  711. DbgPrint("TftpdReceive: exiting\n");
  712. failed:
  713. EnterCriticalSection(&ReceiveLock);
  714. pEntry=&Request->RequestLinkage;
  715. InsertHeadList(&ReceiveList,pEntry);
  716. InterlockedIncrement(&AvailableThreads);
  717. ActiveReceive--;
  718. LeaveCriticalSection(&ReceiveLock);
  719. return (0);
  720. }
  721. // ========================================================================
  722. DWORD
  723. TftpdNewReceive(
  724. PVOID Argument,
  725. BYTE Flags
  726. )
  727. /*++
  728. Routine Description:
  729. This handles all incoming requests and dispatches them to appropriate
  730. worker threads.
  731. Arguments:
  732. Argument - not used
  733. Return Value:
  734. Exit status
  735. 0 == success
  736. 1 == failure, stop service and exit if severe error.
  737. --*/
  738. {
  739. IPAddr MyAddr=0;
  740. DWORD IterCount=0;
  741. BOOL LockHeld=FALSE;
  742. SocketEntry *SE;
  743. DbgPrint("TftpdReceive: entered. Socket %d\n", (DWORD)((DWORD_PTR)Argument));
  744. LockHeld=TryEnterCriticalSection(&SocketLock);
  745. while(IterCount < MAX_LOCK_TRIES && !LockHeld) {
  746. //Potential for deadlock on interface change if this socket is being deleted.
  747. // Break the deadlock by not waiting forever
  748. Sleep(200);
  749. LockHeld=TryEnterCriticalSection(&SocketLock);
  750. IterCount++;
  751. }
  752. if (!LockHeld) {
  753. DbgPrint("TftpdReceived: SocketLock held. Dropping packet");
  754. return 0;
  755. }
  756. if (LookupSocketEntryBySock((SOCKET)Argument,&SE) == ERROR_SUCCESS) {
  757. MyAddr=SE->IPAddress;
  758. DbgPrint("TftpdReceive: Found Addr in SocketList %x\n",MyAddr);
  759. }
  760. LeaveCriticalSection(&SocketLock);
  761. HandleRequest((SOCKET)Argument,MyAddr);
  762. return(0);
  763. }
  764. DWORD
  765. TftpdContinueReceive(
  766. PVOID Argument,
  767. BYTE Flags
  768. )
  769. /*++
  770. Routine Description:
  771. This handles all incoming requests and dispatches them to appropriate
  772. worker threads.
  773. Arguments:
  774. Argument - not used
  775. Return Value:
  776. Exit status
  777. 0 == success
  778. 1 == failure, stop service and exit if severe error.
  779. --*/
  780. {
  781. DbgPrint("TftpdReceive: entered. Socket %d\n", (DWORD)((DWORD_PTR)Argument));
  782. HandleRequest((SOCKET)Argument,0);
  783. return(0);
  784. }
  785. // ========================================================================
  786. VOID
  787. TftpdControlHandler(
  788. DWORD Opcode)
  789. /*++
  790. Routine Description:
  791. This does a write with the appropriate conversions for netascii or octet
  792. modes.
  793. Arguments:
  794. WriteFd - file to read from
  795. Buffer - Buffer to read into
  796. BufferSize - size of buffer
  797. WriteMode - O_TEXT or O_BINARY
  798. O_TEXT means the netascii conversions must be done
  799. O_BINARY means octet mode
  800. Return Value:
  801. BytesWritten
  802. Error?
  803. --*/
  804. {
  805. int ok;
  806. time_t current_time;
  807. time( &current_time );
  808. TftpdServiceStatus.dwCheckPoint++;
  809. DbgPrint("TftpdControlHandler(%d) dwCheckPoint=%d, at %s.\n",
  810. Opcode,
  811. TftpdServiceStatus.dwCheckPoint,
  812. ctime( &current_time ) );
  813. switch (Opcode) {
  814. case SERVICE_CONTROL_PAUSE:
  815. // Actually can we pause listening?
  816. DbgPrint("TftpdControlHandler: got SERVICE_CONTROL_PAUSE\n");
  817. SuspendThread(MasterThreadHandle);
  818. TftpdServiceStatus.dwCurrentState = SERVICE_PAUSED;
  819. break;
  820. case SERVICE_CONTROL_CONTINUE:
  821. DbgPrint("TftpdControlHandler: got SERVICE_CONTROL_CONTINUE\n");
  822. ResumeThread(MasterThreadHandle);
  823. TftpdServiceStatus.dwCurrentState = SERVICE_RUNNING;
  824. break;
  825. case SERVICE_CONTROL_STOP:
  826. case SERVICE_CONTROL_SHUTDOWN:
  827. DbgPrint("TftpdControlHandler: got SERVICE_CONTROL_STOP/SHUTDOWN\n");
  828. // TerminateThread(MasterThreadHandle, 0);
  829. TftpdServiceExit(NO_ERROR);
  830. goto done;
  831. break;
  832. case SERVICE_CONTROL_INTERROGATE:
  833. DbgPrint("TftpdControlHandler: got SERVICE_CONTROL_INTERROGATE\n");
  834. DbgPrint( " req_read=%d, req_write=%d, req_error=%d",
  835. tftpd_stat.req_read,
  836. tftpd_stat.req_write,
  837. tftpd_stat.req_error);
  838. #if defined(REMOTE_BOOT_SECURITY)
  839. DbgPrint("req_login=%d, req_key=%d.\n",
  840. tftpd_stat.req_login,
  841. tftpd_stat.req_key );
  842. #endif //defined(REMOTE_BOOT_SECURITY)
  843. break;
  844. default:
  845. DbgPrint("TftpdControlHandler: got SERVICE_CONTROL_(%d)?\n", Opcode );
  846. break;
  847. }
  848. /** Send a status response **/
  849. ok =
  850. SetServiceStatus( TftpdServiceStatusHandle,
  851. &TftpdServiceStatus );
  852. if( !ok ){
  853. DbgPrint("TftpdControlHandler: SetServiceStatus failed=%d\n",
  854. GetLastError() );
  855. }
  856. done:
  857. return;
  858. }
  859. // ========================================================================
  860. //
  861. // Announce that we're going down, clean up, stop master thread.
  862. //
  863. VOID
  864. TftpdServiceExit(
  865. IN ULONG Error
  866. )
  867. {
  868. int ok;
  869. DbgPrint("TftpdServiceExit(%d)\n", Error );
  870. // ====================
  871. // stage one, stop pending, signal master thread.
  872. // ====================
  873. TftpdServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
  874. ok =
  875. SetServiceStatus( TftpdServiceStatusHandle,
  876. &TftpdServiceStatus );
  877. if( !ok ){
  878. DbgPrint("TftpdServiceExit: SetServiceStatus failed=%d\n",
  879. GetLastError() );
  880. }
  881. SetEvent( eMasterTerminate );
  882. // ====================
  883. // stage two, stop.
  884. // ====================
  885. TftpdServiceStatus.dwCurrentState = SERVICE_STOPPED;
  886. TftpdServiceStatus.dwCheckPoint = 0;
  887. TftpdServiceStatus.dwWaitHint = 0;
  888. SET_SERVICE_EXITCODE(
  889. Error,
  890. TftpdServiceStatus.dwWin32ExitCode,
  891. TftpdServiceStatus.dwServiceSpecificExitCode
  892. );
  893. ok =
  894. SetServiceStatus( TftpdServiceStatusHandle,
  895. &TftpdServiceStatus );
  896. if( !ok ){
  897. DbgPrint("TftpdServiceExit: SetServiceStatus failed=%d\n",
  898. GetLastError() );
  899. }
  900. // ====================
  901. // stage three, cleanup.
  902. // ====================
  903. if( eSock ){
  904. CloseHandle( eSock );
  905. eSock = NULL;
  906. }
  907. if( eMasterTerminate ){
  908. CloseHandle( eMasterTerminate );
  909. eMasterTerminate = NULL;
  910. }
  911. if( LogFile ){
  912. fclose( LogFile );
  913. LogFile = NULL;
  914. }
  915. #if defined(REMOTE_BOOT_SECURITY)
  916. TftpdUninitSecurityArray();
  917. #endif //defined(REMOTE_BOOT_SECURITY)
  918. }
  919. /*++
  920. Remove Entry from list, and free it. SocketLock must be held by caller.
  921. --*/
  922. VOID DeleteSocketEntry(SocketEntry *SE)
  923. {
  924. DbgPrint("Deregister wait %x",SE->WaitHandle);
  925. RtlDeregisterWaitEx(SE->WaitHandle,INVALID_HANDLE_VALUE);
  926. closesocket(SE->Sock);
  927. WSACloseEvent(SE->WaitEvent);
  928. if (SE->Linkage.Flink == SE->Linkage.Blink) {
  929. // single entry case
  930. RemoveHeadList(&SocketList);
  931. } else {
  932. SE->Linkage.Blink->Flink=SE->Linkage.Flink;
  933. SE->Linkage.Flink->Blink=SE->Linkage.Blink;
  934. }
  935. DbgPrint("removing socket to %x",SE->IPAddress);
  936. free(SE);
  937. }
  938. DWORD GetIpTable(PMIB_IPADDRTABLE *AddrTable)
  939. {
  940. HRESULT hr;
  941. DWORD IpAddrTableSize=0;
  942. PMIB_IPADDRTABLE IpAddrTable=NULL, TmpAddrTable=NULL;
  943. DWORD ReturnValue=STATUS_NO_MEMORY;
  944. *AddrTable=NULL;
  945. hr=GetIpAddrTable(NULL,
  946. &IpAddrTableSize,
  947. FALSE);
  948. if (hr != ERROR_SUCCESS && hr != ERROR_INSUFFICIENT_BUFFER) {
  949. DbgPrint("GetIpAddrTable failed with %x",hr);
  950. goto ret;
  951. }
  952. IpAddrTable=(PMIB_IPADDRTABLE)malloc(IpAddrTableSize);
  953. if (IpAddrTable == NULL) {
  954. goto ret;
  955. }
  956. while (1) {
  957. hr=GetIpAddrTable(IpAddrTable,
  958. &IpAddrTableSize,
  959. FALSE);
  960. if (hr == ERROR_SUCCESS) {
  961. break;
  962. }
  963. if (hr == ERROR_INSUFFICIENT_BUFFER) {
  964. TmpAddrTable=realloc(IpAddrTable,IpAddrTableSize);
  965. if (TmpAddrTable == NULL) {
  966. free(IpAddrTable);
  967. goto ret;
  968. } else {
  969. IpAddrTable = TmpAddrTable;
  970. }
  971. continue;
  972. }
  973. DbgPrint("GetIpAddrTable failed with %x",hr);
  974. goto ret;
  975. }
  976. ReturnValue=ERROR_SUCCESS;
  977. *AddrTable=IpAddrTable;
  978. ret:
  979. return ReturnValue;
  980. }
  981. VOID inet_ntoa_copy(struct in_addr IP, BYTE* IPString)
  982. {
  983. BYTE *Tmp;
  984. Tmp=inet_ntoa(IP);
  985. if (Tmp) {
  986. strcpy(IPString,Tmp);
  987. }
  988. }
  989. /*++
  990. Addr : IP Addr to bind socket to.
  991. Return: NULL of failure. Pointer to SocketEntry that is added to SocketList on success.
  992. SocketLock must be held by caller.
  993. --*/
  994. SocketEntry* AddSocket(IPAddr Addr)
  995. {
  996. struct sockaddr_in TftpdAddress;
  997. SOCKET Sock;
  998. HANDLE SocketEvent;
  999. SocketEntry *SE;
  1000. struct servent * serventry;
  1001. int Count=0;
  1002. DWORD ErrStatus;
  1003. do {
  1004. Sock = WSASocket(AF_INET, SOCK_DGRAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
  1005. if (Sock != INVALID_SOCKET) {
  1006. memset(&TftpdAddress, 0, sizeof(struct sockaddr_in));
  1007. serventry = getservbyname("tftp", "udp");
  1008. if (serventry == NULL) {
  1009. DbgPrint("TftpdStart: getservbyname cannot find tftp port.\n");
  1010. continue;
  1011. }
  1012. TftpdAddress.sin_family = AF_INET;
  1013. TftpdAddress.sin_port = serventry->s_port;
  1014. TftpdAddress.sin_addr.s_addr = Addr;
  1015. if (bind(Sock, (struct sockaddr *)&TftpdAddress, sizeof(struct sockaddr_in))) {
  1016. DWORD err=GetLastError();
  1017. DbgPrint("Bind failed %d\n",err);
  1018. return NULL;
  1019. } else {
  1020. break;
  1021. }
  1022. }
  1023. ErrStatus=WSAGetLastError();
  1024. DbgPrint("WSASocket failed with %d\n",ErrStatus);
  1025. Sleep(750);
  1026. Count++;
  1027. } while (Count < MAX_SOCKET_INIT_ATTEMPT);
  1028. if (Sock == INVALID_SOCKET) {
  1029. DbgPrint("Failed to create socket for addr %x\n",Addr);
  1030. return NULL;
  1031. }
  1032. SE=(SocketEntry*)malloc(sizeof(SocketEntry));
  1033. if (!SE) {
  1034. goto out;
  1035. } else {
  1036. BYTE TmpAddr[20];
  1037. memset(SE,0,sizeof(SocketEntry));
  1038. SE->Sock=Sock;
  1039. SE->IPAddress=Addr;
  1040. inet_ntoa_copy(*((struct in_addr*)&Addr),TmpAddr);
  1041. SocketEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
  1042. if (SocketEvent == NULL) {
  1043. goto out;
  1044. }
  1045. SE->WaitEvent=SocketEvent;
  1046. SE->WaitHandle=RegisterSocket(Sock,SocketEvent,REG_NEW_SOCKET);
  1047. if (SE->WaitHandle == 0) {
  1048. goto out;
  1049. }
  1050. InsertHeadList(&SocketList,&SE->Linkage);
  1051. DbgPrint("Adding socket: %d addr: %s\n",Sock,TmpAddr);
  1052. }
  1053. return SE;
  1054. out:
  1055. closesocket(Sock);
  1056. if (SocketEvent) {
  1057. CloseHandle(SocketEvent);
  1058. }
  1059. if (SE) {
  1060. free(SE);
  1061. }
  1062. return NULL;
  1063. }
  1064. /*++
  1065. Walk through SocketList, deleting unreferenced entries, cleaning reference on referenced entries.
  1066. SocketList lock must be held by caller.
  1067. --*/
  1068. DWORD CleanSocketList()
  1069. {
  1070. PLIST_ENTRY pEntry, pNextEntry;
  1071. SocketEntry *SE;
  1072. BOOL DeletedEntry=FALSE;
  1073. pEntry = SocketList.Flink;
  1074. while ( pEntry != &SocketList) {
  1075. SE = CONTAINING_RECORD(pEntry,
  1076. SocketEntry,
  1077. Linkage);
  1078. pNextEntry=pEntry->Flink;
  1079. if (!SE->Referenced) {
  1080. DeleteSocketEntry(SE);
  1081. DeletedEntry=TRUE;
  1082. } else {
  1083. SE->Referenced = FALSE;
  1084. }
  1085. pEntry=pNextEntry;
  1086. }
  1087. return DeletedEntry;
  1088. }
  1089. /*++
  1090. SocketLock must be held by caller.
  1091. Returns: S_OK if found. SE contains pointer.
  1092. S_FALSE if failed. SE contains NULL.
  1093. --*/
  1094. DWORD LookupInterfaceEntry(IPAddr Addr,SocketEntry **SE)
  1095. {
  1096. PLIST_ENTRY pEntry;
  1097. SocketEntry *LocalSE;
  1098. *SE=NULL;
  1099. for ( pEntry = SocketList.Flink;
  1100. pEntry != &SocketList;
  1101. pEntry = pEntry->Flink) {
  1102. LocalSE = CONTAINING_RECORD(pEntry,
  1103. SocketEntry,
  1104. Linkage);
  1105. if (LocalSE->IPAddress == Addr) {
  1106. *SE=LocalSE;
  1107. return TRUE;
  1108. }
  1109. }
  1110. return FALSE;
  1111. }
  1112. /*++
  1113. SocketLock must be held by caller.
  1114. Returns: S_OK if found. SE contains pointer.
  1115. S_FALSE if failed. SE contains NULL.
  1116. --*/
  1117. DWORD LookupSocketEntryBySock(SOCKET Sock, SocketEntry **SE)
  1118. {
  1119. PLIST_ENTRY pEntry;
  1120. SocketEntry *LocalSE;
  1121. *SE=NULL;
  1122. for ( pEntry = SocketList.Flink;
  1123. pEntry != &SocketList;
  1124. pEntry = pEntry->Flink) {
  1125. LocalSE = CONTAINING_RECORD(pEntry,
  1126. SocketEntry,
  1127. Linkage);
  1128. if (LocalSE->Sock == Sock) {
  1129. *SE=LocalSE;
  1130. break;
  1131. }
  1132. }
  1133. if (*SE) {
  1134. return ERROR_SUCCESS;
  1135. } else {
  1136. return ERROR_INVALID_PARAMETER;
  1137. }
  1138. }
  1139. /*++
  1140. Handle interface change event signalled by PA.
  1141. --*/
  1142. VOID NTAPI InterfaceChange(PVOID Context, BOOLEAN Flag)
  1143. {
  1144. PMIB_IPADDRTABLE IpAddrTable;
  1145. DWORD i;
  1146. DWORD EntryCount=0;
  1147. PLIST_ENTRY pEntry,pNextEntry;
  1148. SocketEntry *SE,*NewSE;
  1149. int Count=0;
  1150. DWORD ErrStatus;
  1151. DWORD Added=FALSE, Removed=FALSE;
  1152. IPAddr IPAddress;
  1153. DbgPrint("InterfaceChange\n");
  1154. EnterCriticalSection(&SocketLock);
  1155. if (GetIpTable(&IpAddrTable) == ERROR_SUCCESS) {
  1156. for (i=0; i < IpAddrTable->dwNumEntries; i++) {
  1157. if ( (IpAddrTable->table[i].dwAddr != 0) &&
  1158. (IpAddrTable->table[i].dwAddr != LOOPBACK)) {
  1159. if (LookupInterfaceEntry(IpAddrTable->table[i].dwAddr,&SE)) {
  1160. SE->Referenced=TRUE;
  1161. } else {
  1162. Added=TRUE;
  1163. SE=AddSocket(IpAddrTable->table[i].dwAddr);
  1164. if (SE) {
  1165. SE->Referenced=TRUE;
  1166. }
  1167. }
  1168. DbgPrint("Referenced Socket %x\n",IpAddrTable->table[i].dwAddr);
  1169. }
  1170. }
  1171. Removed=CleanSocketList();
  1172. free(IpAddrTable);
  1173. }
  1174. if (!Added && !Removed) {
  1175. // Got a notify, and didn't detect any differences. Could be a race condition, and
  1176. // addr got removed, and readded before we could process the notify
  1177. // Reopen all sockets to handle this case
  1178. pEntry=SocketList.Flink;
  1179. while (pEntry != &SocketList) {
  1180. SE = CONTAINING_RECORD(pEntry,
  1181. SocketEntry,
  1182. Linkage);
  1183. pNextEntry=pEntry->Flink;
  1184. if (!(SE->Flags & SE_SOCKET_REOPEN)) {
  1185. IPAddress=SE->IPAddress;
  1186. DeleteSocketEntry(SE);
  1187. NewSE=AddSocket(IPAddress);
  1188. if (NewSE) {
  1189. NewSE->Flags |= SE_SOCKET_REOPEN;
  1190. }
  1191. }
  1192. pEntry=pNextEntry;
  1193. }
  1194. }
  1195. ErrStatus = NotifyAddrChange(&AddrChangeHandle,&AddrChangeOverlapped);
  1196. if (ErrStatus != ERROR_SUCCESS && ErrStatus != ERROR_IO_PENDING) {
  1197. DbgPrint("NotifyAddrChange failed %d",ErrStatus);
  1198. }
  1199. LeaveCriticalSection(&SocketLock);
  1200. }
  1201. // ========================================================================
  1202. // EOF.