1 /**
2 @file
3 Web server application
4
5 Copyright (c) 2011-2012, Intel Corporation
6 All rights reserved. This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include <WebServer.h>
17
18 DT_WEB_SERVER mWebServer; ///< Web server's control structure
19
20
21 /**
22 Add a port to the list of ports to be polled.
23
24 @param [in] pWebServer The web server control structure address.
25
26 @param [in] SocketFD The socket's file descriptor to add to the list.
27
28 @retval EFI_SUCCESS The port was successfully added
29 @retval EFI_NO_RESOURCES Insufficient memory to add the port
30
31 **/
32 EFI_STATUS
PortAdd(IN DT_WEB_SERVER * pWebServer,IN int SocketFD)33 PortAdd (
34 IN DT_WEB_SERVER * pWebServer,
35 IN int SocketFD
36 )
37 {
38 nfds_t Index;
39 size_t LengthInBytes;
40 nfds_t MaxEntries;
41 nfds_t MaxEntriesNew;
42 struct pollfd * pFdList;
43 struct pollfd * pFdListNew;
44 WSDT_PORT ** ppPortListNew;
45 WSDT_PORT * pPort;
46 EFI_STATUS Status;
47
48 DBG_ENTER ( );
49
50 //
51 // Use for/break instead of goto
52 //
53 for ( ; ; ) {
54 //
55 // Assume success
56 //
57 Status = EFI_SUCCESS;
58
59 //
60 // Create a new list if necessary
61 //
62 pFdList = pWebServer->pFdList;
63 MaxEntries = pWebServer->MaxEntries;
64 if ( pWebServer->Entries >= MaxEntries ) {
65 MaxEntriesNew = 16 + MaxEntries;
66
67 //
68 // The current FD list is full
69 // Allocate a new FD list
70 //
71 LengthInBytes = sizeof ( *pFdList ) * MaxEntriesNew;
72 Status = gBS->AllocatePool ( EfiRuntimeServicesData,
73 LengthInBytes,
74 (VOID **)&pFdListNew );
75 if ( EFI_ERROR ( Status )) {
76 DEBUG (( DEBUG_ERROR | DEBUG_POOL,
77 "ERROR - Failed to allocate the FD list, Status: %r\r\n",
78 Status ));
79 break;
80 }
81
82 //
83 // Allocate a new port list
84 //
85 LengthInBytes = sizeof ( *ppPortListNew ) * MaxEntriesNew;
86 Status = gBS->AllocatePool ( EfiRuntimeServicesData,
87 LengthInBytes,
88 (VOID **) &ppPortListNew );
89 if ( EFI_ERROR ( Status )) {
90 DEBUG (( DEBUG_ERROR | DEBUG_POOL,
91 "ERROR - Failed to allocate the port list, Status: %r\r\n",
92 Status ));
93
94 //
95 // Free the new FD list
96 //
97 gBS->FreePool ( pFdListNew );
98 break;
99 }
100
101 //
102 // Duplicate the FD list
103 //
104 Index = MaxEntries;
105 if ( NULL != pFdList ) {
106 CopyMem ( pFdListNew,
107 pFdList,
108 Index * sizeof ( *pFdList ));
109 }
110
111 //
112 // Initialize the new entries in the FD list
113 //
114 for ( ; MaxEntriesNew > Index; Index++ ) {
115 pFdListNew[ Index ].fd = -1;
116 pFdListNew[ Index ].events = 0;
117 pFdListNew[ Index ].revents = 0;
118 }
119
120 //
121 // Free the old FD list
122 //
123 if ( NULL != pFdList ) {
124 gBS->FreePool ( pFdList );
125 }
126
127 //
128 // Switch to the new FD list
129 //
130 pWebServer->pFdList = pFdListNew;
131 pFdList = pWebServer->pFdList;
132
133 //
134 // Duplicate the port list
135 //
136 Index = MaxEntries;
137 if ( NULL != pWebServer->ppPortList ) {
138 CopyMem ( ppPortListNew,
139 pWebServer->ppPortList,
140 Index * sizeof ( *ppPortListNew ));
141 }
142
143 //
144 // Initialize the new entries in the port list
145 //
146 for ( ; MaxEntriesNew > Index; Index++ ) {
147 ppPortListNew[ Index ] = NULL;
148 }
149
150 //
151 // Free the old port list
152 //
153 if ( NULL != pWebServer->ppPortList ) {
154 gBS->FreePool ( pWebServer->ppPortList );
155 }
156
157 //
158 // Switch to the new port list
159 //
160 pWebServer->ppPortList = ppPortListNew;
161
162 //
163 // Update the list size
164 //
165 pWebServer->MaxEntries = MaxEntriesNew;
166 }
167
168 //
169 // Allocate a new port
170 //
171 LengthInBytes = sizeof ( *pPort );
172 Status = gBS->AllocatePool ( EfiRuntimeServicesData,
173 LengthInBytes,
174 (VOID **)&pPort );
175 if ( EFI_ERROR ( Status )) {
176 DEBUG (( DEBUG_ERROR | DEBUG_POOL,
177 "ERROR - Failed to allocate the port, Status: %r\r\n",
178 Status ));
179 break;
180 }
181
182 //
183 // Initialize the port
184 //
185 pPort->RequestLength = 0;
186 pPort->TxBytes = 0;
187
188 //
189 // Add the socket to the FD list
190 //
191 pFdList[ pWebServer->Entries ].fd = SocketFD;
192 pFdList[ pWebServer->Entries ].events = POLLRDNORM
193 | POLLHUP;
194 pFdList[ pWebServer->Entries ].revents = 0;
195
196 //
197 // Add the port to the port list
198 //
199 pWebServer->ppPortList[ pWebServer->Entries ] = pPort;
200
201 //
202 // Account for the new entry
203 //
204 pWebServer->Entries += 1;
205 DEBUG (( DEBUG_PORT_WORK | DEBUG_INFO,
206 "WebServer handling %d ports\r\n",
207 pWebServer->Entries ));
208
209 //
210 // All done
211 //
212 break;
213 }
214
215 //
216 // Return the operation status
217 //
218 DBG_EXIT_STATUS ( Status );
219 return Status;
220 }
221
222
223 /**
224 Remove a port from the list of ports to be polled.
225
226 @param [in] pWebServer The web server control structure address.
227
228 @param [in] SocketFD The socket's file descriptor to add to the list.
229
230 **/
231 VOID
PortRemove(IN DT_WEB_SERVER * pWebServer,IN int SocketFD)232 PortRemove (
233 IN DT_WEB_SERVER * pWebServer,
234 IN int SocketFD
235 )
236 {
237 nfds_t Entries;
238 nfds_t Index;
239 struct pollfd * pFdList;
240 WSDT_PORT ** ppPortList;
241
242 DBG_ENTER ( );
243
244 //
245 // Attempt to remove the entry from the list
246 //
247 Entries = pWebServer->Entries;
248 pFdList = pWebServer->pFdList;
249 ppPortList = pWebServer->ppPortList;
250 for ( Index = 0; Entries > Index; Index++ ) {
251 //
252 // Locate the specified socket file descriptor
253 //
254 if ( SocketFD == pFdList[ Index ].fd ) {
255 //
256 // Determine if this is the listen port
257 //
258 if ( SocketFD == pWebServer->HttpListenPort ) {
259 pWebServer->HttpListenPort = -1;
260 }
261
262 //
263 // Close the socket
264 //
265 close ( SocketFD );
266
267 //
268 // Free the port structure
269 //
270 gBS->FreePool ( ppPortList[ Index ]);
271
272 //
273 // Remove this port from the list by copying
274 // the rest of the list down one entry
275 //
276 Entries -= 1;
277 for ( ; Entries > Index; Index++ ) {
278 pFdList[ Index ] = pFdList[ Index + 1 ];
279 ppPortList[ Index ] = ppPortList[ Index + 1 ];
280 }
281 pFdList[ Index ].fd = -1;
282 pFdList[ Index ].events = 0;
283 pFdList[ Index ].revents = 0;
284 ppPortList[ Index ] = NULL;
285
286 //
287 // Update the number of entries in the list
288 //
289 pWebServer->Entries = Entries;
290 DEBUG (( DEBUG_PORT_WORK | DEBUG_INFO,
291 "WebServer handling %d ports\r\n",
292 pWebServer->Entries ));
293 break;
294 }
295 }
296
297 DBG_EXIT ( );
298 }
299
300
301 /**
302 Process the work for the sockets.
303
304 @param [in] pWebServer The web server control structure address.
305
306 @param [in] SocketFD The socket's file descriptor to add to the list.
307
308 @param [in] events everts is a bitmask of the work to be done
309
310 @param [in] pPort The address of a WSDT_PORT structure
311
312 @retval EFI_SUCCESS The operation was successful
313 @retval EFI_DEVICE_ERROR Error, close the port
314
315 **/
316 EFI_STATUS
PortWork(IN DT_WEB_SERVER * pWebServer,IN int SocketFD,IN INTN events,IN WSDT_PORT * pPort)317 PortWork (
318 IN DT_WEB_SERVER * pWebServer,
319 IN int SocketFD,
320 IN INTN events,
321 IN WSDT_PORT * pPort
322 )
323 {
324 BOOLEAN bDone;
325 size_t LengthInBytes;
326 int NewSocket;
327 EFI_STATUS OpStatus;
328 struct sockaddr_in6 RemoteAddress;
329 socklen_t RemoteAddressLength;
330 EFI_STATUS Status;
331
332 DEBUG (( DEBUG_PORT_WORK, "Entering PortWork\r\n" ));
333
334 //
335 // Assume success
336 //
337 OpStatus = EFI_SUCCESS;
338
339 //
340 // Handle input events
341 //
342 if ( 0 != ( events & POLLRDNORM )) {
343 //
344 // Determine if this is a connection attempt
345 //
346 if (( SocketFD == pWebServer->HttpListenPort )
347 || ( SocketFD == pWebServer->HttpListenPort6 )) {
348 //
349 // Handle connection attempts
350 // Accepts arrive as read events
351 //
352 RemoteAddressLength = sizeof ( RemoteAddress );
353 NewSocket = accept ( SocketFD,
354 (struct sockaddr *)&RemoteAddress,
355 &RemoteAddressLength );
356 if ( -1 != NewSocket ) {
357 if ( 0 != NewSocket ) {
358 //
359 // Add this port to the list monitored by the web server
360 //
361 Status = PortAdd ( pWebServer, NewSocket );
362 if ( EFI_ERROR ( Status )) {
363 DEBUG (( DEBUG_ERROR,
364 "ERROR - Failed to add the port 0x%08x, Status: %r\r\n",
365 NewSocket,
366 Status ));
367
368 //
369 // Done with the new socket
370 //
371 close ( NewSocket );
372 }
373 }
374 else {
375 DEBUG (( DEBUG_ERROR,
376 "ERROR - Socket not available!\r\n" ));
377 }
378
379 //
380 // Leave the listen port open
381 //
382 }
383 else {
384 //
385 // Listen port error
386 // Close the listen port by returning error status
387 //
388 OpStatus = EFI_DEVICE_ERROR;
389 DEBUG (( DEBUG_ERROR,
390 "ERROR - Failed to accept new connection, errno: 0x%08x\r\n",
391 errno ));
392 }
393 }
394 else {
395 //
396 // Handle the data received event
397 //
398 if ( 0 == pPort->RequestLength ) {
399 //
400 // Receive the page request
401 //
402 pPort->RequestLength = recv ( SocketFD,
403 &pPort->Request[0],
404 DIM ( pPort->Request ),
405 0 );
406 if ( -1 == pPort->RequestLength ) {
407 //
408 // Receive error detected
409 // Close the port
410 //
411 OpStatus = EFI_DEVICE_ERROR;
412 }
413 else {
414 DEBUG (( DEBUG_REQUEST,
415 "0x%08x: Socket - Received %d bytes of HTTP request\r\n",
416 SocketFD,
417 pPort->RequestLength ));
418
419 //
420 // Process the request
421 //
422 OpStatus = HttpRequest ( SocketFD, pPort, &bDone );
423 if ( bDone ) {
424 //
425 // Notify the upper layer to close the socket
426 //
427 OpStatus = EFI_DEVICE_ERROR;
428 }
429 }
430 }
431 else {
432 //
433 // Receive the file data
434 //
435 LengthInBytes = recv ( SocketFD,
436 &pPort->RxBuffer[0],
437 DIM ( pPort->RxBuffer ),
438 0 );
439 if ( -1 == LengthInBytes ) {
440 //
441 // Receive error detected
442 // Close the port
443 //
444 OpStatus = EFI_DEVICE_ERROR;
445 }
446 else {
447 DEBUG (( DEBUG_REQUEST,
448 "0x%08x: Socket - Received %d bytes of file data\r\n",
449 SocketFD,
450 LengthInBytes ));
451
452 //
453 // TODO: Process the file data
454 //
455 }
456 }
457 }
458 }
459
460 //
461 // Handle the close event
462 //
463 if ( 0 != ( events & POLLHUP )) {
464 //
465 // Close the port
466 //
467 OpStatus = EFI_DEVICE_ERROR;
468 }
469
470 //
471 // Return the operation status
472 //
473 DEBUG (( DEBUG_PORT_WORK,
474 "Exiting PortWork, Status: %r\r\n",
475 OpStatus ));
476 return OpStatus;
477 }
478
479
480 /**
481 Scan the list of sockets and process any pending work
482
483 @param [in] pWebServer The web server control structure address.
484
485 **/
486 VOID
SocketPoll(IN DT_WEB_SERVER * pWebServer)487 SocketPoll (
488 IN DT_WEB_SERVER * pWebServer
489 )
490 {
491 int FDCount;
492 struct pollfd * pPoll;
493 WSDT_PORT ** ppPort;
494 EFI_STATUS Status;
495
496 DEBUG (( DEBUG_SOCKET_POLL, "Entering SocketPoll\r\n" ));
497
498 //
499 // Determine if any ports are active
500 //
501 FDCount = poll ( pWebServer->pFdList,
502 pWebServer->Entries,
503 CLIENT_POLL_DELAY );
504 if ( -1 == FDCount ) {
505 DEBUG (( DEBUG_ERROR | DEBUG_SOCKET_POLL,
506 "ERROR - errno: %d\r\n",
507 errno ));
508 }
509
510 pPoll = pWebServer->pFdList;
511 ppPort = pWebServer->ppPortList;
512 while ( 0 < FDCount ) {
513 //
514 // Walk the list of ports to determine what work needs to be done
515 //
516 if ( 0 != pPoll->revents ) {
517 //
518 // Process this port
519 //
520 Status = PortWork ( pWebServer,
521 pPoll->fd,
522 pPoll->revents,
523 *ppPort );
524 pPoll->revents = 0;
525
526 //
527 // Close the port if necessary
528 //
529 if ( EFI_ERROR ( Status )) {
530 PortRemove ( pWebServer, pPoll->fd );
531 pPoll -= 1;
532 ppPort -= 1;
533 }
534
535 //
536 // Account for this file descriptor
537 //
538 FDCount -= 1;
539 }
540
541 //
542 // Set the next port
543 //
544 pPoll += 1;
545 ppPort += 1;
546 }
547
548 DEBUG (( DEBUG_SOCKET_POLL, "Exiting SocketPoll\r\n" ));
549 }
550
551
552 /**
553 Create an HTTP port for the web server
554
555 This routine polls the network layer to create an HTTP port for the
556 web server. More than one attempt may be necessary since it may take
557 some time to get the IP address and initialize the upper layers of
558 the network stack.
559
560 After the HTTP port is created, the socket layer will manage the
561 coming and going of the network connections until the last network
562 connection is broken.
563
564 @param [in] pWebServer The web server control structure address.
565 @param [in] AddressFamily Address family for the network connection
566 @param [in] Protocol Protocol to use for the network connection
567 @param [in] HttpPort Port number for the HTTP connection
568 @param [out] pPort Address of the port
569
570 **/
571 VOID
WebServerListen(IN DT_WEB_SERVER * pWebServer,IN sa_family_t AddressFamily,IN int Protocol,IN UINT16 HttpPort,OUT int * pPort)572 WebServerListen (
573 IN DT_WEB_SERVER * pWebServer,
574 IN sa_family_t AddressFamily,
575 IN int Protocol,
576 IN UINT16 HttpPort,
577 OUT int * pPort
578 )
579 {
580 union {
581 struct sockaddr_in v4;
582 struct sockaddr_in6 v6;
583 } WebServerAddress;
584 int SocketStatus;
585 EFI_STATUS Status;
586
587 DEBUG (( DEBUG_SERVER_LISTEN, "Entering WebServerListen\r\n" ));
588
589 //
590 // Attempt to create the socket for the web server
591 //
592 * pPort = socket ( AddressFamily, SOCK_STREAM, Protocol );
593 if ( -1 != *pPort ) {
594 //
595 // Build the socket address
596 //
597 ZeroMem ( &WebServerAddress, sizeof ( WebServerAddress ));
598 if ( AF_INET == AddressFamily ) {
599 WebServerAddress.v4.sin_len = sizeof ( WebServerAddress.v4 );
600 WebServerAddress.v4.sin_family = AddressFamily;
601 WebServerAddress.v4.sin_port = htons ( HttpPort );
602 }
603 else {
604 WebServerAddress.v6.sin6_len = sizeof ( WebServerAddress.v6 );
605 WebServerAddress.v6.sin6_family = AddressFamily;
606 WebServerAddress.v6.sin6_port = htons ( HttpPort );
607 WebServerAddress.v6.sin6_scope_id = __IPV6_ADDR_SCOPE_GLOBAL;
608 }
609
610 //
611 // Bind the socket to the HTTP port
612 //
613 SocketStatus = bind ( *pPort,
614 (struct sockaddr *) &WebServerAddress,
615 WebServerAddress.v4.sin_len );
616 if ( -1 != SocketStatus ) {
617 //
618 // Enable connections to the HTTP port
619 //
620 SocketStatus = listen ( *pPort, SOMAXCONN );
621 if ( -1 != SocketStatus ) {
622 //
623 // Add the HTTP port to the list of ports to poll
624 //
625 Status = PortAdd ( pWebServer, *pPort );
626 if ( EFI_ERROR ( Status )) {
627 SocketStatus = -1;
628 }
629 else {
630 DEBUG (( DEBUG_PORT_WORK,
631 "Listening on Tcp%d:%d\r\n",
632 ( AF_INET == AddressFamily ) ? 4 : 6,
633 HttpPort ));
634 }
635 }
636 }
637
638 //
639 // Release the socket if necessary
640 //
641 if ( -1 == SocketStatus ) {
642 close ( *pPort );
643 *pPort = -1;
644 }
645 }
646
647 DEBUG (( DEBUG_SERVER_LISTEN, "Exiting WebServerListen\r\n" ));
648 }
649
650
651 /**
652 Entry point for the web server application.
653
654 @param [in] Argc The number of arguments
655 @param [in] Argv The argument value array
656
657 @retval 0 The application exited normally.
658 @retval Other An error occurred.
659 **/
660 int
main(IN int Argc,IN char ** Argv)661 main (
662 IN int Argc,
663 IN char **Argv
664 )
665 {
666 UINT16 HttpPort;
667 UINTN Index;
668 DT_WEB_SERVER * pWebServer;
669 EFI_STATUS Status;
670 UINT64 TriggerTime;
671
672 //
673 // Get the HTTP port
674 //
675 HttpPort = PcdGet16 ( WebServer_HttpPort );
676 DEBUG (( DEBUG_HTTP_PORT,
677 "HTTP Port: %d\r\n",
678 HttpPort ));
679
680 //
681 // Create a timer event to start HTTP port
682 //
683 pWebServer = &mWebServer;
684 Status = gBS->CreateEvent ( EVT_TIMER,
685 TPL_WEB_SERVER,
686 NULL,
687 NULL,
688 &pWebServer->TimerEvent );
689 if ( !EFI_ERROR ( Status )) {
690 TriggerTime = HTTP_PORT_POLL_DELAY * ( 1000 * 10 );
691 Status = gBS->SetTimer ( pWebServer->TimerEvent,
692 TimerPeriodic,
693 TriggerTime );
694 if ( !EFI_ERROR ( Status )) {
695 //
696 // Run the web server forever
697 //
698 pWebServer->HttpListenPort = -1;
699 pWebServer->HttpListenPort6 = -1;
700 pWebServer->bRunning = TRUE;
701 do {
702 //
703 // Poll the network layer to create the HTTP port
704 // for the web server. More than one attempt may
705 // be necessary since it may take some time to get
706 // the IP address and initialize the upper layers
707 // of the network stack.
708 //
709 if (( -1 == pWebServer->HttpListenPort )
710 || ( -1 == pWebServer->HttpListenPort6 )) {
711 do {
712 //
713 // Wait a while before polling for a connection
714 //
715 if ( EFI_SUCCESS != gBS->CheckEvent ( pWebServer->TimerEvent )) {
716 if ( 0 != pWebServer->Entries ) {
717 break;
718 }
719 gBS->WaitForEvent ( 1, &pWebServer->TimerEvent, &Index );
720 }
721
722 //
723 // Poll for a network connection
724 //
725 if ( -1 == pWebServer->HttpListenPort ) {
726 WebServerListen ( pWebServer,
727 AF_INET,
728 IPPROTO_TCP,
729 HttpPort,
730 &pWebServer->HttpListenPort );
731 }
732 if ( -1 == pWebServer->HttpListenPort6 ) {
733 WebServerListen ( pWebServer,
734 AF_INET6,
735 IPPROTO_TCP,
736 HttpPort,
737 &pWebServer->HttpListenPort6 );
738 }
739
740 //
741 // Continue polling while both network connections are
742 // not present
743 //
744 } while ( 0 == pWebServer->Entries );
745 }
746
747 //
748 // Poll the sockets for activity while both network
749 // connections are connected
750 //
751 do {
752 SocketPoll ( pWebServer );
753 } while ( pWebServer->bRunning
754 && ( -1 != pWebServer->HttpListenPort )
755 && ( -1 != pWebServer->HttpListenPort6 ));
756
757 //
758 // Continue polling the network connections until both
759 // TCP4 and TCP6 are connected
760 //
761 } while ( pWebServer->bRunning );
762
763 //
764 // Stop the timer
765 //
766 gBS->SetTimer ( pWebServer->TimerEvent,
767 TimerCancel,
768 0 );
769 }
770
771 //
772 // Done with the timer event
773 //
774 gBS->CloseEvent ( pWebServer->TimerEvent );
775 }
776
777 //
778 // Return the final status
779 //
780 DBG_EXIT_STATUS ( Status );
781 return Status;
782 }
783