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.

617 lines
16 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. algmsg.c
  5. Abstract:
  6. This module contains code for the ALG transparent proxy's
  7. message-processing.
  8. Author:
  9. Qiang Wang (qiangw) 10-Apr-2000
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. #include <ipnatapi.h>
  15. #define MAKE_ADDRESS(a,b,c,d) \
  16. ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
  17. #define MAKE_PORT(a,b) ((a) | ((b) << 8))
  18. #define TOUPPER(c) ((c) > 'z' ? (c) : ((c) < 'a' ? (c) : (c) ^ 0x20))
  19. //
  20. // Constant string for the 'PASV' command reply
  21. //
  22. static CONST CHAR PasvReply[] = "227 ";
  23. //
  24. // Constant string for the 'PORT' command (must be upper-case)
  25. //
  26. static CONST CHAR PortCommand[] = "PORT ";
  27. static CONST CHAR Eol[] = "\x0d\x0a\x00\x51\x69\x61\x6e\x67\x20\x57\x61\x6e\x67";
  28. //
  29. // FORWARD DECLARATIONS
  30. //
  31. BOOLEAN
  32. AlgpExtractOctet(
  33. CHAR **Buffer,
  34. CHAR *BufferEnd,
  35. UCHAR *Octet
  36. );
  37. VOID
  38. AlgpWriteOctet(
  39. CHAR **Buffer,
  40. UCHAR Octet
  41. );
  42. VOID
  43. AlgProcessMessage(
  44. PALG_INTERFACE Interfacep,
  45. PALG_ENDPOINT Endpointp,
  46. PNH_BUFFER Bufferp
  47. )
  48. /*++
  49. Routine Description:
  50. This routine is called to process a full message read from an ALG
  51. client or server on a control channel.
  52. Arguments:
  53. Interfacep - the interface on which the control-channel was accepted
  54. Endpointp - the active endpoint corresponding to the control channel
  55. Bufferp - contains the message read, along with other context information
  56. Return Value:
  57. none.
  58. Notes:
  59. Invoked with the interface's lock held by the caller, and with two
  60. references made to the interface on our behalf. It is this routine's
  61. responsibility to release both the references and the buffer.
  62. --*/
  63. {
  64. BOOLEAN Success;
  65. BOOLEAN Continuation;
  66. SOCKET Socket;
  67. ULONG Error;
  68. ULONG i;
  69. LONG NewLength;
  70. ULONG PublicAddress;
  71. USHORT PublicPort;
  72. ULONG PrivateAddress;
  73. USHORT PrivatePort;
  74. UCHAR Numbers[6];
  75. CHAR *HostPortStartp;
  76. CHAR *CommandBufferp = reinterpret_cast<CHAR*>(Bufferp->Buffer);
  77. CHAR *EndOfBufferp =
  78. reinterpret_cast<CHAR*>(Bufferp->Buffer + Bufferp->TransferOffset);
  79. CONST CHAR *Commandp =
  80. Endpointp->Type == AlgClientEndpointType ?
  81. (PCHAR)PasvReply : (PCHAR)PortCommand;
  82. PROFILE("AlgProcessMessage");
  83. if ((Bufferp->UserFlags & ALG_BUFFER_FLAG_FROM_ACTUAL_CLIENT) != 0) {
  84. Socket = Endpointp->ClientSocket;
  85. } else {
  86. ASSERT((Bufferp->UserFlags & ALG_BUFFER_FLAG_FROM_ACTUAL_HOST) != 0);
  87. Socket = Endpointp->HostSocket;
  88. }
  89. #if DBG
  90. NhTrace(
  91. TRACE_FLAG_ALG,
  92. "AlgProcessMessage: received (0x%08x) (%d) \"%s\"",
  93. Bufferp->UserFlags, Bufferp->TransferOffset, CommandBufferp
  94. );
  95. #endif
  96. if ((Bufferp->UserFlags & ALG_BUFFER_FLAG_FROM_ACTUAL_CLIENT) != 0 &&
  97. (Bufferp->UserFlags & ALG_BUFFER_FLAG_CONTINUATION) == 0) {
  98. while (*Commandp != '\0' && *Commandp == TOUPPER(*CommandBufferp)) {
  99. Commandp++;
  100. CommandBufferp++;
  101. }
  102. if (*Commandp == '\0') {
  103. //
  104. // We found a match.
  105. //
  106. if (Endpointp->Type == AlgClientEndpointType) {
  107. //
  108. // Skip non-numerical characters.
  109. //
  110. while (CommandBufferp < EndOfBufferp &&
  111. (*CommandBufferp < '0' || *CommandBufferp > '9')) {
  112. CommandBufferp++;
  113. }
  114. } else {
  115. //
  116. // Skip white space.
  117. //
  118. while (*CommandBufferp == ' ') {
  119. CommandBufferp++;
  120. }
  121. }
  122. HostPortStartp = CommandBufferp;
  123. //
  124. // Extract host and port numbers.
  125. //
  126. Success =
  127. AlgpExtractOctet(
  128. &CommandBufferp,
  129. EndOfBufferp,
  130. &Numbers[0]
  131. );
  132. i = 1;
  133. while (i < 6 && Success && *CommandBufferp == ',') {
  134. CommandBufferp++;
  135. Success =
  136. AlgpExtractOctet(
  137. &CommandBufferp,
  138. EndOfBufferp,
  139. &Numbers[i]
  140. );
  141. i++;
  142. }
  143. if (i == 6 && Success) {
  144. //
  145. // We extract all of them successfully.
  146. //
  147. PrivateAddress =
  148. MAKE_ADDRESS(
  149. Numbers[0],
  150. Numbers[1],
  151. Numbers[2],
  152. Numbers[3]
  153. );
  154. PrivatePort = MAKE_PORT(Numbers[4], Numbers[5]);
  155. PublicAddress = Endpointp->BoundaryAddress;
  156. if (PublicAddress == IP_NAT_ADDRESS_UNSPECIFIED) {
  157. PublicAddress =
  158. NhQueryAddressSocket(Endpointp->ClientSocket);
  159. }
  160. //
  161. // Cancel the previous one first.
  162. //
  163. if (Endpointp->ReservedPort != 0) {
  164. PTIMER_CONTEXT TimerContextp;
  165. NatCancelRedirect(
  166. AlgTranslatorHandle,
  167. NAT_PROTOCOL_TCP,
  168. Endpointp->DestinationAddress,
  169. Endpointp->DestinationPort,
  170. Endpointp->SourceAddress,
  171. Endpointp->SourcePort,
  172. Endpointp->NewDestinationAddress,
  173. Endpointp->NewDestinationPort,
  174. Endpointp->NewSourceAddress,
  175. Endpointp->NewSourcePort
  176. );
  177. TimerContextp = reinterpret_cast<PTIMER_CONTEXT>(
  178. NH_ALLOCATE(sizeof(TIMER_CONTEXT))
  179. );
  180. if (TimerContextp != NULL) {
  181. TimerContextp->TimerQueueHandle = AlgTimerQueueHandle;
  182. TimerContextp->ReservedPort = Endpointp->ReservedPort;
  183. CreateTimerQueueTimer(
  184. &(TimerContextp->TimerHandle),
  185. AlgTimerQueueHandle,
  186. AlgDelayedPortRelease,
  187. (PVOID)TimerContextp,
  188. ALG_PORT_RELEASE_DELAY,
  189. 0,
  190. WT_EXECUTEDEFAULT
  191. );
  192. } else {
  193. NhTrace(
  194. TRACE_FLAG_ALG,
  195. "AlgProcessMessage:"
  196. " memory allocation failed for timer context"
  197. );
  198. NhErrorLog(
  199. IP_ALG_LOG_ALLOCATION_FAILED,
  200. 0,
  201. "%d",
  202. sizeof(TIMER_CONTEXT)
  203. );
  204. }
  205. Endpointp->ReservedPort = 0;
  206. }
  207. //
  208. // Reserve a port for the new data session.
  209. //
  210. Error =
  211. NatAcquirePortReservation(
  212. AlgPortReservationHandle,
  213. 1,
  214. &PublicPort
  215. );
  216. if (Error) {
  217. NhTrace(
  218. TRACE_FLAG_ALG,
  219. "AlgProcessMessage: error %d acquiring port",
  220. Error
  221. );
  222. AlgDeleteActiveEndpoint(Endpointp);
  223. ALG_DEREFERENCE_INTERFACE(Interfacep);
  224. return;
  225. }
  226. Endpointp->ReservedPort = PublicPort;
  227. //
  228. // Create a redirect for the new data session.
  229. //
  230. if (Endpointp->Type == AlgClientEndpointType) {
  231. Endpointp->DestinationAddress = PublicAddress;
  232. Endpointp->SourceAddress = 0;
  233. Endpointp->NewDestinationAddress = PrivateAddress;
  234. Endpointp->NewSourceAddress = 0;
  235. Endpointp->DestinationPort = PublicPort;
  236. Endpointp->SourcePort = 0;
  237. Endpointp->NewDestinationPort = PrivatePort;
  238. Endpointp->NewSourcePort = 0;
  239. Error =
  240. NatCreatePartialRedirect(
  241. AlgTranslatorHandle,
  242. NatRedirectFlagLoopback,
  243. NAT_PROTOCOL_TCP,
  244. PublicAddress,
  245. PublicPort,
  246. PrivateAddress,
  247. PrivatePort,
  248. NULL,
  249. NULL,
  250. NULL
  251. );
  252. } else {
  253. Endpointp->DestinationAddress = PublicAddress;
  254. Endpointp->SourceAddress = Endpointp->ActualHostAddress;
  255. Endpointp->NewDestinationAddress = PrivateAddress;
  256. Endpointp->NewSourceAddress = Endpointp->ActualHostAddress;
  257. Endpointp->DestinationPort = PublicPort;
  258. Endpointp->SourcePort = ALG_PORT_DATA;
  259. Endpointp->NewDestinationPort = PrivatePort;
  260. Endpointp->NewSourcePort = ALG_PORT_DATA;
  261. Error =
  262. NatCreateRedirect(
  263. AlgTranslatorHandle,
  264. NatRedirectFlagLoopback,
  265. NAT_PROTOCOL_TCP,
  266. PublicAddress,
  267. PublicPort,
  268. Endpointp->ActualHostAddress,
  269. ALG_PORT_DATA,
  270. PrivateAddress,
  271. PrivatePort,
  272. Endpointp->ActualHostAddress,
  273. ALG_PORT_DATA,
  274. NULL,
  275. NULL,
  276. NULL
  277. );
  278. }
  279. if (Error) {
  280. NhTrace(
  281. TRACE_FLAG_ALG,
  282. "AlgProcessMessage: error %d creating redirect",
  283. Error
  284. );
  285. AlgDeleteActiveEndpoint(Endpointp);
  286. ALG_DEREFERENCE_INTERFACE(Interfacep);
  287. return;
  288. }
  289. //
  290. // Modify the ALG command.
  291. //
  292. Numbers[0] = (UCHAR)(PublicAddress & 0xff);
  293. Numbers[1] = (UCHAR)((PublicAddress >> 8) & 0xff);
  294. Numbers[2] = (UCHAR)((PublicAddress >> 16) & 0xff);
  295. Numbers[3] = (UCHAR)((PublicAddress >> 24) & 0xff);
  296. Numbers[4] = (UCHAR)(PublicPort & 0xff);
  297. Numbers[5] = (UCHAR)((PublicPort >> 8) & 0xff);
  298. NewLength = 17;
  299. for (i = 0; i < 6; i++) {
  300. if (Numbers[i] > 99) {
  301. NewLength++;
  302. } else if (Numbers[i] <= 9) {
  303. NewLength--;
  304. }
  305. }
  306. Bufferp->TransferOffset +=
  307. NewLength - (ULONG)(CommandBufferp - HostPortStartp);
  308. ASSERT(Bufferp->TransferOffset <= NH_BUFFER_SIZE);
  309. MoveMemory(
  310. HostPortStartp + NewLength,
  311. CommandBufferp,
  312. EndOfBufferp - CommandBufferp
  313. );
  314. AlgpWriteOctet(&HostPortStartp, Numbers[0]);
  315. i = 1;
  316. do {
  317. *HostPortStartp = ',';
  318. HostPortStartp++;
  319. AlgpWriteOctet(&HostPortStartp, Numbers[i]);
  320. i++;
  321. } while (i < 6);
  322. }
  323. }
  324. }
  325. //
  326. // Forward the message.
  327. //
  328. Continuation =
  329. AlgIsFullMessage(
  330. reinterpret_cast<CHAR*>(
  331. &(Bufferp->Buffer[Bufferp->TransferOffset - 2])
  332. ),
  333. 2
  334. ) == NULL;
  335. if (Continuation) {
  336. Bufferp->UserFlags |= ALG_BUFFER_FLAG_CONTINUATION;
  337. NhTrace(
  338. TRACE_FLAG_ALG,
  339. "AlgProcessMessage: message to be continued (%d)",
  340. Bufferp->TransferOffset
  341. );
  342. } else {
  343. Bufferp->UserFlags &= ~(ULONG)ALG_BUFFER_FLAG_CONTINUATION;
  344. }
  345. #if DBG
  346. NhTrace(
  347. TRACE_FLAG_ALG,
  348. "AlgProcessMessage: written (%d) \"%s\"",
  349. Bufferp->TransferOffset,
  350. Bufferp->Buffer
  351. );
  352. #endif
  353. Error =
  354. AlgWriteActiveEndpoint(
  355. Interfacep,
  356. Endpointp,
  357. Socket,
  358. Bufferp,
  359. Bufferp->TransferOffset,
  360. Bufferp->UserFlags
  361. );
  362. if (Error) {
  363. NhTrace(
  364. TRACE_FLAG_ALG,
  365. "AlgProcessMessage: deleting endpoint %d, "
  366. "AlgWriteActiveEndpoint=%d",
  367. Endpointp->EndpointId, Error
  368. );
  369. AlgDeleteActiveEndpoint(Endpointp);
  370. }
  371. }
  372. CHAR *
  373. AlgIsFullMessage(
  374. CHAR *Bufferp,
  375. ULONG Length
  376. )
  377. /*++
  378. Routine Description:
  379. This routine is called to determine whether the passed-in buffer includes
  380. a full ALG command.
  381. Arguments:
  382. Bufferp - contains the message read, along with other context information
  383. Return Value:
  384. CHAR * - points to the start of the next ALG command, or NULL if no
  385. complete ALG command is detected.
  386. --*/
  387. {
  388. ULONG Count = Length;
  389. CONST CHAR *CommandBufferp = Bufferp;
  390. CONST CHAR *CommandDelimiter = Eol;
  391. PROFILE("AlgIsFullMessage");
  392. ASSERT(
  393. Eol[0] != '\0' &&
  394. (Eol[1] == '\0' || Eol[2] == '\0') &&
  395. Eol[0] != Eol[1]
  396. );
  397. while (Count > 0 && *CommandDelimiter != '\0') {
  398. if (*CommandBufferp == *CommandDelimiter) {
  399. CommandDelimiter++;
  400. CommandBufferp++;
  401. Count--;
  402. } else {
  403. if (CommandDelimiter == Eol) {
  404. CommandBufferp++;
  405. Count--;
  406. } else {
  407. CommandDelimiter = Eol;
  408. }
  409. }
  410. }
  411. return *CommandDelimiter == '\0' ? (CHAR *)CommandBufferp : NULL;
  412. }
  413. VOID CALLBACK
  414. AlgDelayedPortRelease(
  415. PVOID Parameter,
  416. BOOLEAN TimerOrWaitFired
  417. )
  418. /*++
  419. Routine Description:
  420. This routine is called to release a reserved port.
  421. Arguments:
  422. Parameter - callback context
  423. TimerOrWaitFired - wait timed out
  424. Return Value:
  425. None.
  426. --*/
  427. {
  428. PTIMER_CONTEXT TimerContextp = (PTIMER_CONTEXT)Parameter;
  429. PROFILE("AlgDelayedPortRelease");
  430. NatReleasePortReservation(
  431. AlgPortReservationHandle,
  432. TimerContextp->ReservedPort,
  433. 1
  434. );
  435. DeleteTimerQueueTimer(
  436. TimerContextp->TimerQueueHandle,
  437. TimerContextp->TimerHandle,
  438. NULL
  439. );
  440. NH_FREE(TimerContextp);
  441. }
  442. BOOLEAN
  443. AlgpExtractOctet(
  444. CHAR **Buffer,
  445. CHAR *BufferEnd,
  446. UCHAR *Octet
  447. )
  448. /*++
  449. Routine Description:
  450. This routine is called to extrcat an octet from a string.
  451. Arguments:
  452. Buffer - points to a pointer to a string where conversion starts; on
  453. return it points to the pointer to the string where conversion ends
  454. BufferEnd - points to the end of the string
  455. Octet - points to a caller-suplied storage to store converted octet
  456. Return Value:
  457. BOOLEAN - TRUE if successfuly converted, FALSE otherwise.
  458. --*/
  459. {
  460. BOOLEAN Success;
  461. ULONG i = 0;
  462. ULONG Value = 0;
  463. while (i < 3 && **Buffer >= '0' && **Buffer <= '9') {
  464. Value *= 10;
  465. Value += **Buffer - '0';
  466. (*Buffer)++;
  467. i++;
  468. }
  469. Success = i > 0 && Value < 0x100;
  470. if (Success) {
  471. *Octet = (UCHAR)Value;
  472. }
  473. return Success;
  474. }
  475. VOID
  476. AlgpWriteOctet(
  477. CHAR **Buffer,
  478. UCHAR Octet
  479. )
  480. /*++
  481. Routine Description:
  482. This routine is called to convert an octet to a string.
  483. Arguments:
  484. Buffer - points to a pointer to a string where conversion starts; on
  485. return it points to the pointer to the string where conversion ends
  486. Octet - octet to convert
  487. Return Value:
  488. None.
  489. --*/
  490. {
  491. UCHAR Value = Octet;
  492. if (Octet > 99) {
  493. **Buffer = '0' + Value / 100;
  494. Value %= 100;
  495. (*Buffer)++;
  496. }
  497. if (Octet > 9) {
  498. **Buffer = '0' + Value / 10;
  499. Value %= 10;
  500. (*Buffer)++;
  501. }
  502. ASSERT(Value <= 9);
  503. **Buffer = '0' + Value;
  504. (*Buffer)++;
  505. }