• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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