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.

631 lines
21 KiB

  1. //
  2. // genproxy.c - Generic application level proxy for IPv6/IPv4
  3. //
  4. // This program accepts connections on a socket with a given address family
  5. // and port, and forwards them on a socket of the other address family to
  6. // a given address (default loopback) using the same port.
  7. //
  8. // Basically, it makes an unmodified IPv4 server look like an IPv6 server
  9. // (or vice-versa). Typically, the proxy will run on the same machine as
  10. // the server it is fronting, but that doesn't have to be the case.
  11. //
  12. // Copyright 1996 - 2000 Microsoft Corporation.
  13. // All rights reserved.
  14. //
  15. #include <winsock2.h>
  16. #include <ws2tcpip.h>
  17. #include <stdlib.h>
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <wspiapi.h>
  21. //
  22. // What should the proxy server pretend to be?
  23. // Default is an IPv6 web server.
  24. //
  25. #define DEFAULT_PROXY_FAMILY PF_INET6
  26. #define DEFAULT_SOCKTYPE SOCK_STREAM
  27. #define DEFAULT_PORT "http"
  28. //
  29. // Configuration parameters.
  30. //
  31. #define BUFFER_SIZE (4 * 1024) // Big enough?
  32. typedef struct PerConnection PerConnection;
  33. //
  34. // Information we keep for each direction of a bi-directional connection.
  35. //
  36. typedef struct PerOperation {
  37. BOOL Inbound; // Is this the "receive from client, send to server" side?
  38. BOOL Receiving; // Is this operation a recv?
  39. WSABUF Buffer;
  40. WSAOVERLAPPED Overlapped;
  41. PerConnection *Connection;
  42. } PerOperation;
  43. //
  44. // Information we keep for each client connection.
  45. //
  46. typedef struct PerConnection {
  47. int Number;
  48. BOOL HalfOpen; // Has one side or the other stopped sending?
  49. SOCKET Client;
  50. SOCKET Server;
  51. PerOperation Inbound;
  52. PerOperation Outbound;
  53. } PerConnection;
  54. //
  55. // Global variables
  56. //
  57. BOOL Verbose = FALSE;
  58. //
  59. // Create state information for a client.
  60. //
  61. PerConnection*
  62. CreateConnectionState(Client, Server)
  63. {
  64. static TotalConnections = 1;
  65. PerConnection *Conn;
  66. //
  67. // Allocate space for a PerConnection structure and two buffers.
  68. //
  69. Conn = (PerConnection *)malloc(sizeof(*Conn) + (2 * BUFFER_SIZE));
  70. if (Conn == NULL)
  71. return NULL;
  72. //
  73. // Fill everything in.
  74. //
  75. Conn->Number = TotalConnections++;
  76. Conn->HalfOpen = FALSE;
  77. Conn->Client = Client;
  78. Conn->Server = Server;
  79. Conn->Inbound.Inbound = TRUE; // Recv from client, send to server.
  80. Conn->Inbound.Receiving = TRUE; // Start out receiving.
  81. Conn->Inbound.Buffer.len = BUFFER_SIZE;
  82. Conn->Inbound.Buffer.buf = (char *)(Conn + 1);
  83. Conn->Inbound.Connection = Conn;
  84. Conn->Outbound.Inbound = FALSE; // Recv from server, send to client.
  85. Conn->Outbound.Receiving = TRUE; // Start out receiving.
  86. Conn->Outbound.Buffer.len = BUFFER_SIZE;
  87. Conn->Outbound.Buffer.buf = Conn->Inbound.Buffer.buf + BUFFER_SIZE;
  88. Conn->Outbound.Connection = Conn;
  89. printf("Created Connecton #%d\n", Conn->Number);
  90. return Conn;
  91. }
  92. void Usage(char *ProgName) {
  93. fprintf(stderr, "\nGeneric server proxy.\n");
  94. fprintf(stderr, "\n%s [-f family] [-p port] [-s server] [-v]\n\n",
  95. ProgName);
  96. fprintf(stderr, " family\tFamily (PF_INET or PF_INET6) proxy exports. (default %s)\n",
  97. (DEFAULT_PROXY_FAMILY == PF_INET) ? "PF_INET" : "PF_INET6");
  98. fprintf(stderr, " port\t\tPort on which to bind. (default %s)\n",
  99. DEFAULT_PORT);
  100. fprintf(stderr, " server\tName or address to forward requests to. (default: loopback)\n");
  101. WSACleanup();
  102. exit(1);
  103. }
  104. LPSTR DecodeError(int ErrorCode)
  105. {
  106. static char Message[1024];
  107. // If this program was multi-threaded, we'd want to use
  108. // FORMAT_MESSAGE_ALLOCATE_BUFFER instead of a static buffer here.
  109. // (And of course, free the buffer when we were done with it)
  110. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
  111. FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, ErrorCode,
  112. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  113. (LPSTR)Message, 1024, NULL);
  114. return Message;
  115. }
  116. //
  117. // Find out how many processors are on the system.
  118. //
  119. DWORD
  120. GetNumberOfProcessors(void)
  121. {
  122. SYSTEM_INFO SystemInfo;
  123. GetSystemInfo(&SystemInfo);
  124. return SystemInfo.dwNumberOfProcessors;
  125. }
  126. //
  127. // This routine waits for asynchronous operations to complete on
  128. // a particular completion port and handles them.
  129. //
  130. // There should be one of these threads per processor on the machine.
  131. //
  132. WINAPI
  133. CompletionPortHandler(LPVOID Param)
  134. {
  135. HANDLE CompletionPort = *(HANDLE *)Param;
  136. PerConnection *Connection;
  137. PerOperation *Operation;
  138. OVERLAPPED *Overlapped;
  139. DWORD BytesTransferred, AmountSent, AmountReceived, RecvFlags;
  140. int RetVal;
  141. while (1) {
  142. //
  143. // Wait for one of the asych operations to complete.
  144. //
  145. RetVal = GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
  146. (PULONG_PTR)&Connection,
  147. &Overlapped, INFINITE);
  148. //
  149. // Retrieve the state of this operation.
  150. //
  151. Operation = CONTAINING_RECORD(Overlapped, PerOperation, Overlapped);
  152. if (Operation->Connection != Connection) {
  153. printf("Pointer mismatch in completion status!\n");
  154. continue;
  155. }
  156. if (Verbose) {
  157. printf("Handling %s %s on %s side of connection #%d\n",
  158. RetVal ? "completed" : "aborted",
  159. Operation->Receiving ? "recv" : "send",
  160. Operation->Inbound ? "inbound" : "outbound",
  161. Connection->Number);
  162. }
  163. if (RetVal == 0) {
  164. if (GetLastError() == 64) {
  165. printf("Connection #%d %s was reset\n", Connection->Number,
  166. Operation->Inbound ? "inbound" : "outbound");
  167. // Fall through, it'll be treated as a close...
  168. } else {
  169. fprintf(stderr, "GetQueuedCompletionStatus() failed with error %d: %s\n",
  170. GetLastError(), DecodeError(GetLastError()));
  171. // REVIEW: CloseConnection?
  172. continue;
  173. }
  174. }
  175. if (Operation->Receiving == TRUE) {
  176. //
  177. // We just completed a recv.
  178. // Look for closed/closing connection.
  179. //
  180. if (BytesTransferred == 0) {
  181. if (Operation->Inbound == TRUE) {
  182. //
  183. // The client has closed its side of the connection.
  184. //
  185. if (Connection->HalfOpen == FALSE) {
  186. // Server is still around,
  187. // tell it that the client quits.
  188. shutdown(Connection->Server, SD_SEND);
  189. Connection->HalfOpen = TRUE;
  190. printf("Connection #%d Client quit sending\n",
  191. Connection->Number);
  192. } else {
  193. // Server already quit sending, so close the sockets.
  194. printf("Connection #%d Client quit too\n",
  195. Connection->Number);
  196. closesocket(Connection->Client);
  197. closesocket(Connection->Server);
  198. free(Connection);
  199. }
  200. } else {
  201. //
  202. // The server has closed its side of the connection.
  203. //
  204. if (Connection->HalfOpen == FALSE) {
  205. // Client is still around,
  206. // tell it that the server quits.
  207. shutdown(Connection->Client, SD_SEND);
  208. Connection->HalfOpen = TRUE;
  209. printf("Connection #%d Server quit sending\n",
  210. Connection->Number);
  211. } else {
  212. // Client already quit sending, so close the sockets.
  213. printf("Connection #%d Server quit too\n",
  214. Connection->Number);
  215. closesocket(Connection->Client);
  216. closesocket(Connection->Server);
  217. free(Connection);
  218. }
  219. }
  220. if (Verbose)
  221. printf("Leaving Recv Handler\n");
  222. continue;
  223. }
  224. //
  225. // Connection is still active, and we received some data.
  226. // Post a send request to forward it onward.
  227. //
  228. Operation->Receiving = FALSE;
  229. Operation->Buffer.len = BytesTransferred;
  230. RetVal = WSASend(Operation->Inbound ? Connection->Server :
  231. Connection->Client, &Operation->Buffer, 1,
  232. &AmountSent, 0, Overlapped, NULL);
  233. if ((RetVal == SOCKET_ERROR) &&
  234. (WSAGetLastError() != WSA_IO_PENDING)) {
  235. //
  236. // Something bad happened.
  237. //
  238. fprintf(stderr, "WSASend() failed with error %d: %s\n",
  239. WSAGetLastError(), DecodeError(WSAGetLastError()));
  240. closesocket(Connection->Client);
  241. closesocket(Connection->Server);
  242. free(Connection);
  243. }
  244. if (Verbose)
  245. printf("Leaving Recv Handler\n");
  246. } else {
  247. //
  248. // We just completed a send.
  249. //
  250. if (BytesTransferred != Operation->Buffer.len) {
  251. fprintf(stderr, "WSASend() didn't send entire buffer!\n");
  252. goto CloseConnection;
  253. }
  254. //
  255. // Post another recv request since we but live to serve.
  256. //
  257. RecvFlags = 0;
  258. Operation->Receiving = TRUE;
  259. Operation->Buffer.len = BUFFER_SIZE;
  260. RetVal = WSARecv(Operation->Inbound ? Connection->Client :
  261. Connection->Server, &Operation->Buffer, 1,
  262. &AmountReceived, &RecvFlags, Overlapped, NULL);
  263. if ((RetVal == SOCKET_ERROR) &&
  264. (WSAGetLastError() != WSA_IO_PENDING)) {
  265. //
  266. // Something bad happened.
  267. //
  268. fprintf(stderr, "WSARecv() failed with error %d: %s\n",
  269. WSAGetLastError(), DecodeError(WSAGetLastError()));
  270. CloseConnection:
  271. closesocket(Connection->Client);
  272. closesocket(Connection->Server);
  273. free(Connection);
  274. }
  275. }
  276. }
  277. }
  278. //
  279. // Start serving on this socket.
  280. //
  281. StartProxy(SOCKET Proxy, ADDRINFO *ServerAI)
  282. {
  283. char Hostname[NI_MAXHOST];
  284. int FromLen, AmountReceived, RetVal;
  285. SOCKADDR_STORAGE From;
  286. PerConnection *Conn;
  287. SOCKET Client, Server;
  288. DWORD NumberOfWorkers, Flags;
  289. HANDLE *CompletionPorts;
  290. unsigned int Loop;
  291. //
  292. // Create a completion port and a worker thread to service it.
  293. // Do this once for each processor on the system.
  294. //
  295. NumberOfWorkers = GetNumberOfProcessors();
  296. CompletionPorts = malloc(sizeof(HANDLE) * NumberOfWorkers);
  297. for (Loop = 0; Loop < NumberOfWorkers; Loop++) {
  298. HANDLE WorkerThread;
  299. CompletionPorts[Loop] = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
  300. NULL, 0, 0);
  301. if (CompletionPorts[Loop] == NULL) {
  302. fprintf(stderr, "Couldn't create completion port, error %d: %s\n",
  303. WSAGetLastError(), DecodeError(WSAGetLastError()));
  304. closesocket(Proxy);
  305. WSACleanup();
  306. return -1;
  307. }
  308. WorkerThread = CreateThread(NULL, 0, CompletionPortHandler,
  309. &CompletionPorts[Loop], 0, NULL);
  310. if (WorkerThread == NULL) {
  311. fprintf(stderr, "Couldn't create worker thread, error %d: %s\n",
  312. WSAGetLastError(), DecodeError(WSAGetLastError()));
  313. closesocket(Proxy);
  314. WSACleanup();
  315. return -1;
  316. }
  317. }
  318. //
  319. // We now put the server into an eternal loop,
  320. // serving requests as they arrive.
  321. //
  322. Loop = 0;
  323. while(1) {
  324. //
  325. // Wait for a client to connect.
  326. //
  327. if (Verbose) {
  328. printf("Before accept\n");
  329. }
  330. FromLen = sizeof(From);
  331. Client = accept(Proxy, (LPSOCKADDR)&From, &FromLen);
  332. if (Client == INVALID_SOCKET) {
  333. if (WSAGetLastError() == 10022)
  334. continue;
  335. fprintf(stderr, "accept() failed with error %d: %s\n",
  336. WSAGetLastError(), DecodeError(WSAGetLastError()));
  337. break;
  338. }
  339. if (Verbose) {
  340. if (getnameinfo((LPSOCKADDR)&From, FromLen, Hostname,
  341. sizeof(Hostname), NULL, 0, NI_NUMERICHOST) != 0)
  342. strcpy(Hostname, "<unknown>");
  343. printf("\nAccepted connection from %s\n", Hostname);
  344. }
  345. //
  346. // Connect to real server on client's behalf.
  347. //
  348. Server = socket(ServerAI->ai_family, ServerAI->ai_socktype,
  349. ServerAI->ai_protocol);
  350. if (Server == INVALID_SOCKET) {
  351. fprintf(stderr,"Error opening real server socket, error %d: %s\n",
  352. WSAGetLastError(), DecodeError(WSAGetLastError()));
  353. closesocket(Client);
  354. continue;
  355. }
  356. if (connect(Server, ServerAI->ai_addr, ServerAI->ai_addrlen) == SOCKET_ERROR) {
  357. fprintf(stderr, "connect() to server failed with error %d: %s\n",
  358. WSAGetLastError(), DecodeError(WSAGetLastError()));
  359. closesocket(Client);
  360. closesocket(Server);
  361. continue;
  362. }
  363. if (Verbose) {
  364. FromLen = sizeof(From);
  365. if (getpeername(Server, (LPSOCKADDR)&From, &FromLen) == SOCKET_ERROR) {
  366. fprintf(stderr, "getpeername() failed with error %d: %s\n",
  367. WSAGetLastError(), DecodeError(WSAGetLastError()));
  368. } else {
  369. if (getnameinfo((LPSOCKADDR)&From, FromLen, Hostname,
  370. sizeof(Hostname), NULL, 0, NI_NUMERICHOST) != 0)
  371. strcpy(Hostname, "<unknown>");
  372. printf("Connected to server %s, port %d\n",
  373. Hostname, ntohs(SS_PORT(&From)));
  374. }
  375. }
  376. Conn = CreateConnectionState(Client, Server);
  377. if (CreateIoCompletionPort((HANDLE) Client, CompletionPorts[Loop],
  378. (ULONG_PTR)Conn, 0) == NULL) {
  379. fprintf(stderr, "Couldn't attach completion port, error %d: %s\n",
  380. WSAGetLastError(), DecodeError(WSAGetLastError()));
  381. closesocket(Client);
  382. closesocket(Server);
  383. free(Conn);
  384. continue;
  385. }
  386. if (CreateIoCompletionPort((HANDLE) Server, CompletionPorts[Loop],
  387. (ULONG_PTR)Conn, 0) == NULL) {
  388. fprintf(stderr, "Couldn't attach completion port, error %d: %s\n",
  389. WSAGetLastError(), DecodeError(WSAGetLastError()));
  390. closesocket(Client);
  391. closesocket(Server);
  392. free(Conn);
  393. continue;
  394. }
  395. //
  396. // Start things going by posting a recv on both client and server.
  397. //
  398. Flags = 0;
  399. RetVal = WSARecv(Client, &(Conn->Inbound.Buffer), 1, &AmountReceived,
  400. &Flags, &(Conn->Inbound.Overlapped), NULL);
  401. if ((RetVal == SOCKET_ERROR) &&
  402. (WSAGetLastError() != WSA_IO_PENDING)) {
  403. //
  404. // Something bad happened.
  405. //
  406. fprintf(stderr, "WSARecv() on Client failed with error %d: %s\n",
  407. WSAGetLastError(), DecodeError(WSAGetLastError()));
  408. closesocket(Client);
  409. closesocket(Server);
  410. free(Conn);
  411. continue;
  412. }
  413. Flags = 0;
  414. RetVal = WSARecv(Server, &(Conn->Outbound.Buffer), 1, &AmountReceived,
  415. &Flags, &(Conn->Outbound.Overlapped), NULL);
  416. if ((RetVal == SOCKET_ERROR) &&
  417. (WSAGetLastError() != WSA_IO_PENDING)) {
  418. //
  419. // Something bad happened.
  420. //
  421. fprintf(stderr, "WSARecv() on Server failed with error %d: %s\n",
  422. WSAGetLastError(), DecodeError(WSAGetLastError()));
  423. closesocket(Client);
  424. closesocket(Server);
  425. free(Conn);
  426. continue;
  427. }
  428. if (++Loop == NumberOfWorkers)
  429. Loop = 0;
  430. }
  431. //
  432. // Only get here if something bad happened.
  433. //
  434. closesocket(Proxy);
  435. WSACleanup();
  436. return -1;
  437. }
  438. int __cdecl
  439. main(int argc, char **argv)
  440. {
  441. int ServerFamily;
  442. int ProxyFamily = DEFAULT_PROXY_FAMILY;
  443. char *Port = DEFAULT_PORT;
  444. char *Address = NULL;
  445. int i, RetVal;
  446. WSADATA wsaData;
  447. ADDRINFO Hints, *AI;
  448. SOCKET Proxy;
  449. // Parse arguments
  450. if (argc > 1) {
  451. for (i = 1;i < argc; i++) {
  452. if ((argv[i][0] == '-') || (argv[i][0] == '/') &&
  453. (argv[i][1] != 0) && (argv[i][2] == 0)) {
  454. switch(tolower(argv[i][1])) {
  455. case 'f':
  456. if (!argv[i+1])
  457. Usage(argv[0]);
  458. if (!strcmp(argv[i+1], "PF_INET"))
  459. ProxyFamily = PF_INET;
  460. else if (!strcmp(argv[i+1], "PF_INET6"))
  461. ProxyFamily = PF_INET6;
  462. else
  463. Usage(argv[0]);
  464. i++;
  465. break;
  466. case 's':
  467. if (argv[i+1]) {
  468. if (argv[i+1][0] != '-') {
  469. Address = argv[++i];
  470. break;
  471. }
  472. }
  473. Usage(argv[0]);
  474. break;
  475. case 'p':
  476. if (argv[i+1]) {
  477. if (argv[i+1][0] != '-') {
  478. Port = argv[++i];
  479. break;
  480. }
  481. }
  482. Usage(argv[0]);
  483. break;
  484. case 'v':
  485. Verbose = TRUE;
  486. break;
  487. default:
  488. Usage(argv[0]);
  489. break;
  490. }
  491. } else
  492. Usage(argv[0]);
  493. }
  494. }
  495. ServerFamily = (ProxyFamily == PF_INET6) ? PF_INET : PF_INET6;
  496. // Ask for Winsock version 2.2.
  497. if ((RetVal = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) {
  498. fprintf(stderr, "WSAStartup failed with error %d: %s\n",
  499. RetVal, DecodeError(RetVal));
  500. WSACleanup();
  501. return -1;
  502. }
  503. if (Port == NULL) {
  504. Usage(argv[0]);
  505. }
  506. //
  507. // Determine parameters to use to create and bind the proxy's socket.
  508. //
  509. memset(&Hints, 0, sizeof(Hints));
  510. Hints.ai_family = ProxyFamily;
  511. Hints.ai_socktype = DEFAULT_SOCKTYPE;
  512. Hints.ai_flags = AI_PASSIVE;
  513. RetVal = getaddrinfo(NULL, Port, &Hints, &AI);
  514. if (RetVal != 0) {
  515. fprintf(stderr, "getaddrinfo failed with error %d: %s\n",
  516. RetVal, gai_strerror(RetVal));
  517. WSACleanup();
  518. return -1;
  519. }
  520. Proxy = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol);
  521. if (Proxy == INVALID_SOCKET){
  522. fprintf(stderr, "socket() failed with error %d: %s\n",
  523. WSAGetLastError(), DecodeError(WSAGetLastError()));
  524. freeaddrinfo(AI);
  525. WSACleanup();
  526. return -1;
  527. }
  528. if (bind(Proxy, AI->ai_addr, AI->ai_addrlen) == SOCKET_ERROR) {
  529. fprintf(stderr,"bind() failed with error %d: %s\n",
  530. WSAGetLastError(), DecodeError(WSAGetLastError()));
  531. freeaddrinfo(AI);
  532. closesocket(Proxy);
  533. WSACleanup();
  534. return -1;
  535. }
  536. if (listen(Proxy, 5) == SOCKET_ERROR) {
  537. fprintf(stderr, "listen() failed with error %d: %s\n",
  538. WSAGetLastError(), DecodeError(WSAGetLastError()));
  539. freeaddrinfo(AI);
  540. closesocket(Proxy);
  541. WSACleanup();
  542. return -1;
  543. }
  544. printf("'Listening' on port %s, protocol family %s\n",
  545. Port, (AI->ai_family == PF_INET) ? "PF_INET" : "PF_INET6");
  546. freeaddrinfo(AI);
  547. //
  548. // Determine the parameters to use to create and connect the
  549. // sockets used to communicate with the real server.
  550. //
  551. memset(&Hints, 0, sizeof(Hints));
  552. Hints.ai_family = ServerFamily;
  553. Hints.ai_socktype = DEFAULT_SOCKTYPE;
  554. RetVal = getaddrinfo(Address, Port, &Hints, &AI);
  555. if (RetVal != 0) {
  556. fprintf(stderr, "Cannot resolve address [%s] and port [%s], error %d: %s\n",
  557. Address, Port, RetVal, gai_strerror(RetVal));
  558. closesocket(Proxy);
  559. WSACleanup();
  560. return -1;
  561. }
  562. StartProxy(Proxy, AI);
  563. }